zcash_keys: Keep the Ufvk and Uivk encodings private.

This commit is contained in:
Kris Nuttycombe 2024-03-14 15:23:40 -06:00
parent 9ddbf1e3e9
commit 9e1a4327c3
4 changed files with 35 additions and 29 deletions

View File

@ -74,7 +74,6 @@ use std::io::{self, Cursor};
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use tracing::debug; use tracing::debug;
use zcash_address::unified::{Encoding, Uivk};
use zcash_keys::keys::{ use zcash_keys::keys::{
AddressGenerationError, HdSeedFingerprint, UnifiedAddressRequest, UnifiedIncomingViewingKey, AddressGenerationError, HdSeedFingerprint, UnifiedAddressRequest, UnifiedIncomingViewingKey,
}; };
@ -120,7 +119,7 @@ use {
crate::UtxoId, crate::UtxoId,
rusqlite::Row, rusqlite::Row,
std::collections::BTreeSet, std::collections::BTreeSet,
zcash_address::unified::Ivk, zcash_address::unified::{Encoding, Ivk, Uivk},
zcash_client_backend::wallet::{TransparentAddressMetadata, WalletTransparentOutput}, zcash_client_backend::wallet::{TransparentAddressMetadata, WalletTransparentOutput},
zcash_primitives::{ zcash_primitives::{
legacy::{ legacy::{
@ -347,7 +346,7 @@ pub(crate) fn add_account<P: consensus::Parameters>(
":hd_seed_fingerprint": hd_seed_fingerprint.as_ref().map(|fp| fp.as_bytes()), ":hd_seed_fingerprint": hd_seed_fingerprint.as_ref().map(|fp| fp.as_bytes()),
":hd_account_index": hd_account_index.map(u32::from), ":hd_account_index": hd_account_index.map(u32::from),
":ufvk": viewing_key.ufvk().map(|ufvk| ufvk.encode(params)), ":ufvk": viewing_key.ufvk().map(|ufvk| ufvk.encode(params)),
":uivk": viewing_key.uivk().to_uivk().encode(&params.network_type()), ":uivk": viewing_key.uivk().encode(params),
":orchard_fvk_item_cache": orchard_item, ":orchard_fvk_item_cache": orchard_item,
":sapling_fvk_item_cache": sapling_item, ":sapling_fvk_item_cache": sapling_item,
":p2pkh_fvk_item_cache": transparent_item, ":p2pkh_fvk_item_cache": transparent_item,
@ -1503,18 +1502,9 @@ pub(crate) fn get_account<P: Parameters>(
)) ))
} else { } else {
let uivk_str: String = row.get("uivk")?; let uivk_str: String = row.get("uivk")?;
let (network, uivk) = Uivk::decode(&uivk_str).map_err(|e| {
SqliteClientError::CorruptedData(format!("Failure to decode UIVK: {e}"))
})?;
if network != params.network_type() {
return Err(SqliteClientError::CorruptedData(
"UIVK network type does not match wallet network type".to_string(),
));
}
ViewingKey::Incoming(Box::new( ViewingKey::Incoming(Box::new(
UnifiedIncomingViewingKey::from_uivk(&uivk).map_err(|e| { UnifiedIncomingViewingKey::decode(params, &uivk_str[..])
SqliteClientError::CorruptedData(format!("Failure to decode UIVK: {e}")) .map_err(SqliteClientError::BadAccountData)?,
})?,
)) ))
}; };

View File

@ -5,7 +5,6 @@ use rusqlite::{named_params, Transaction};
use schemer_rusqlite::RusqliteMigration; use schemer_rusqlite::RusqliteMigration;
use secrecy::{ExposeSecret, SecretVec}; use secrecy::{ExposeSecret, SecretVec};
use uuid::Uuid; use uuid::Uuid;
use zcash_address::unified::Encoding;
use zcash_client_backend::{data_api::AccountKind, keys::UnifiedSpendingKey}; use zcash_client_backend::{data_api::AccountKind, keys::UnifiedSpendingKey};
use zcash_keys::keys::{HdSeedFingerprint, UnifiedFullViewingKey}; use zcash_keys::keys::{HdSeedFingerprint, UnifiedFullViewingKey};
use zcash_primitives::consensus; use zcash_primitives::consensus;
@ -122,8 +121,7 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
let uivk = ufvk_parsed let uivk = ufvk_parsed
.to_unified_incoming_viewing_key() .to_unified_incoming_viewing_key()
.to_uivk() .encode(&self.params);
.encode(&self.params.network_type());
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
let transparent_item = ufvk_parsed.transparent().map(|k| k.serialize()); let transparent_item = ufvk_parsed.transparent().map(|k| k.serialize());

View File

@ -12,8 +12,6 @@ and this library adheres to Rust's notion of
- `impl Display for zcash_keys::keys::AddressGenerationError` - `impl Display for zcash_keys::keys::AddressGenerationError`
- `impl std::error::Error for zcash_keys::keys::AddressGenerationError` - `impl std::error::Error for zcash_keys::keys::AddressGenerationError`
- `zcash_keys::keys::DecodingError` - `zcash_keys::keys::DecodingError`
- `zcash_keys::keys::UnifiedFullViewingKey::from_ufvk`
- `zcash_keys::keys::UnifiedFullViewingKey::to_ufvk`
- `zcash_keys::keys::UnifiedFullViewingKey::to_unified_incoming_viewing_key` - `zcash_keys::keys::UnifiedFullViewingKey::to_unified_incoming_viewing_key`
- `zcash_keys::keys::UnifiedIncomingViewingKey` - `zcash_keys::keys::UnifiedIncomingViewingKey`

View File

@ -740,13 +740,13 @@ impl UnifiedFullViewingKey {
)); ));
} }
Self::from_ufvk(&ufvk).map_err(|e| e.to_string()) Self::parse(&ufvk).map_err(|e| e.to_string())
} }
/// Parses a `UnifiedFullViewingKey` from its [ZIP 316] string encoding. /// Parses a `UnifiedFullViewingKey` from its [ZIP 316] string encoding.
/// ///
/// [ZIP 316]: https://zips.z.cash/zip-0316 /// [ZIP 316]: https://zips.z.cash/zip-0316
pub fn from_ufvk(ufvk: &Ufvk) -> Result<Self, DecodingError> { pub fn parse(ufvk: &Ufvk) -> Result<Self, DecodingError> {
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
let mut orchard = None; let mut orchard = None;
#[cfg(feature = "sapling")] #[cfg(feature = "sapling")]
@ -823,7 +823,7 @@ impl UnifiedFullViewingKey {
} }
/// Returns the string encoding of this `UnifiedFullViewingKey` for the given network. /// Returns the string encoding of this `UnifiedFullViewingKey` for the given network.
pub fn to_ufvk(&self) -> Ufvk { fn to_ufvk(&self) -> Ufvk {
let items = std::iter::empty().chain(self.unknown.iter().map(|(typecode, data)| { let items = std::iter::empty().chain(self.unknown.iter().map(|(typecode, data)| {
unified::Fvk::Unknown { unified::Fvk::Unknown {
typecode: *typecode, typecode: *typecode,
@ -973,8 +973,24 @@ impl UnifiedIncomingViewingKey {
} }
} }
/// Parses a `UnifiedFullViewingKey` from its [ZIP 316] string encoding.
///
/// [ZIP 316]: https://zips.z.cash/zip-0316
pub fn decode<P: consensus::Parameters>(params: &P, encoding: &str) -> Result<Self, String> {
let (net, ufvk) = unified::Uivk::decode(encoding).map_err(|e| e.to_string())?;
let expected_net = params.network_type();
if net != expected_net {
return Err(format!(
"UIVK is for network {:?} but we expected {:?}",
net, expected_net,
));
}
Self::parse(&ufvk).map_err(|e| e.to_string())
}
/// Constructs a unified incoming viewing key from a parsed unified encoding. /// Constructs a unified incoming viewing key from a parsed unified encoding.
pub fn from_uivk(uivk: &Uivk) -> Result<Self, DecodingError> { fn parse(uivk: &Uivk) -> Result<Self, DecodingError> {
#[cfg(feature = "orchard")] #[cfg(feature = "orchard")]
let mut orchard = None; let mut orchard = None;
#[cfg(feature = "sapling")] #[cfg(feature = "sapling")]
@ -1041,8 +1057,13 @@ impl UnifiedIncomingViewingKey {
}) })
} }
/// Returns the string encoding of this `UnifiedFullViewingKey` for the given network.
pub fn encode<P: consensus::Parameters>(&self, params: &P) -> String {
self.render().encode(&params.network_type())
}
/// Converts this unified incoming viewing key to a unified encoding. /// Converts this unified incoming viewing key to a unified encoding.
pub fn to_uivk(&self) -> Uivk { fn render(&self) -> Uivk {
let items = std::iter::empty().chain(self.unknown.iter().map(|(typecode, data)| { let items = std::iter::empty().chain(self.unknown.iter().map(|(typecode, data)| {
unified::Ivk::Unknown { unified::Ivk::Unknown {
typecode: *typecode, typecode: *typecode,
@ -1530,7 +1551,7 @@ mod tests {
orchard, orchard,
); );
let encoded = uivk.to_uivk().encode(&NetworkType::Main); let encoded = uivk.render().encode(&NetworkType::Main);
// Test encoded form against known values; these test vectors contain Orchard receivers // Test encoded form against known values; these test vectors contain Orchard receivers
// that will be treated as unknown if the `orchard` feature is not enabled. // that will be treated as unknown if the `orchard` feature is not enabled.
@ -1548,9 +1569,8 @@ mod tests {
assert_eq!(encoded, _encoded_no_t); assert_eq!(encoded, _encoded_no_t);
} }
let decoded = let decoded = UnifiedIncomingViewingKey::parse(&Uivk::decode(&encoded).unwrap().1).unwrap();
UnifiedIncomingViewingKey::from_uivk(&Uivk::decode(&encoded).unwrap().1).unwrap(); let reencoded = decoded.render().encode(&NetworkType::Main);
let reencoded = decoded.to_uivk().encode(&NetworkType::Main);
assert_eq!(encoded, reencoded); assert_eq!(encoded, reencoded);
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
@ -1570,7 +1590,7 @@ mod tests {
); );
let decoded_with_t = let decoded_with_t =
UnifiedIncomingViewingKey::from_uivk(&Uivk::decode(encoded_with_t).unwrap().1).unwrap(); UnifiedIncomingViewingKey::parse(&Uivk::decode(encoded_with_t).unwrap().1).unwrap();
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]
assert_eq!( assert_eq!(
decoded_with_t.transparent.map(|t| t.serialize()), decoded_with_t.transparent.map(|t| t.serialize()),