Skip to content

Commit

Permalink
zcash_keys: Keep the Ufvk and Uivk encodings private.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Mar 14, 2024
1 parent 9ddbf1e commit 9e1a432
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 29 deletions.
18 changes: 4 additions & 14 deletions zcash_client_sqlite/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ use std::io::{self, Cursor};
use std::num::NonZeroU32;
use std::ops::RangeInclusive;
use tracing::debug;
use zcash_address::unified::{Encoding, Uivk};
use zcash_keys::keys::{
AddressGenerationError, HdSeedFingerprint, UnifiedAddressRequest, UnifiedIncomingViewingKey,
};
Expand Down Expand Up @@ -120,7 +119,7 @@ use {
crate::UtxoId,
rusqlite::Row,
std::collections::BTreeSet,
zcash_address::unified::Ivk,
zcash_address::unified::{Encoding, Ivk, Uivk},
zcash_client_backend::wallet::{TransparentAddressMetadata, WalletTransparentOutput},
zcash_primitives::{
legacy::{
Expand Down Expand Up @@ -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_account_index": hd_account_index.map(u32::from),
":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,
":sapling_fvk_item_cache": sapling_item,
":p2pkh_fvk_item_cache": transparent_item,
Expand Down Expand Up @@ -1503,18 +1502,9 @@ pub(crate) fn get_account<P: Parameters>(
))
} else {
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(
UnifiedIncomingViewingKey::from_uivk(&uivk).map_err(|e| {
SqliteClientError::CorruptedData(format!("Failure to decode UIVK: {e}"))
})?,
UnifiedIncomingViewingKey::decode(params, &uivk_str[..])
.map_err(SqliteClientError::BadAccountData)?,

Check warning on line 1507 in zcash_client_sqlite/src/wallet.rs

View check run for this annotation

Codecov / codecov/patch

zcash_client_sqlite/src/wallet.rs#L1505-L1507

Added lines #L1505 - L1507 were not covered by tests
))
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use rusqlite::{named_params, Transaction};
use schemer_rusqlite::RusqliteMigration;
use secrecy::{ExposeSecret, SecretVec};
use uuid::Uuid;
use zcash_address::unified::Encoding;
use zcash_client_backend::{data_api::AccountKind, keys::UnifiedSpendingKey};
use zcash_keys::keys::{HdSeedFingerprint, UnifiedFullViewingKey};
use zcash_primitives::consensus;
Expand Down Expand Up @@ -122,8 +121,7 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {

let uivk = ufvk_parsed
.to_unified_incoming_viewing_key()
.to_uivk()
.encode(&self.params.network_type());
.encode(&self.params);

#[cfg(feature = "transparent-inputs")]
let transparent_item = ufvk_parsed.transparent().map(|k| k.serialize());
Expand Down
2 changes: 0 additions & 2 deletions zcash_keys/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ and this library adheres to Rust's notion of
- `impl Display for zcash_keys::keys::AddressGenerationError`
- `impl std::error::Error for zcash_keys::keys::AddressGenerationError`
- `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::UnifiedIncomingViewingKey`

Expand Down
40 changes: 30 additions & 10 deletions zcash_keys/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
/// [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")]
let mut orchard = None;
#[cfg(feature = "sapling")]
Expand Down Expand Up @@ -823,7 +823,7 @@ impl UnifiedFullViewingKey {
}

/// 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)| {
unified::Fvk::Unknown {
typecode: *typecode,
Expand Down Expand Up @@ -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,

Check warning on line 985 in zcash_keys/src/keys.rs

View check run for this annotation

Codecov / codecov/patch

zcash_keys/src/keys.rs#L979-L985

Added lines #L979 - L985 were not covered by tests
));
}

Self::parse(&ufvk).map_err(|e| e.to_string())

Check warning on line 989 in zcash_keys/src/keys.rs

View check run for this annotation

Codecov / codecov/patch

zcash_keys/src/keys.rs#L989

Added line #L989 was not covered by tests
}

/// 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")]
let mut orchard = None;
#[cfg(feature = "sapling")]
Expand Down Expand Up @@ -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.
pub fn to_uivk(&self) -> Uivk {
fn render(&self) -> Uivk {
let items = std::iter::empty().chain(self.unknown.iter().map(|(typecode, data)| {
unified::Ivk::Unknown {
typecode: *typecode,
Expand Down Expand Up @@ -1530,7 +1551,7 @@ mod tests {
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
// that will be treated as unknown if the `orchard` feature is not enabled.
Expand All @@ -1548,9 +1569,8 @@ mod tests {
assert_eq!(encoded, _encoded_no_t);
}

let decoded =
UnifiedIncomingViewingKey::from_uivk(&Uivk::decode(&encoded).unwrap().1).unwrap();
let reencoded = decoded.to_uivk().encode(&NetworkType::Main);
let decoded = UnifiedIncomingViewingKey::parse(&Uivk::decode(&encoded).unwrap().1).unwrap();
let reencoded = decoded.render().encode(&NetworkType::Main);
assert_eq!(encoded, reencoded);

#[cfg(feature = "transparent-inputs")]
Expand All @@ -1570,7 +1590,7 @@ mod tests {
);

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")]
assert_eq!(
decoded_with_t.transparent.map(|t| t.serialize()),
Expand Down

0 comments on commit 9e1a432

Please sign in to comment.