diff --git a/src/bundle/burn_validation.rs b/src/bundle/burn_validation.rs index c0cfb84f..0fdf35dd 100644 --- a/src/bundle/burn_validation.rs +++ b/src/bundle/burn_validation.rs @@ -69,7 +69,7 @@ mod tests { /// Creates an item of bundle burn list for a given asset description and value. /// /// This function is deterministic and guarantees that each call with the same parameters - /// will return the same result. It achieves determinism by using a static `IssuanceKey`. + /// will return the same result. It achieves determinism by using a static `IssuanceAuthorizingKey`. /// /// # Arguments /// @@ -81,10 +81,9 @@ mod tests { /// A tuple `(AssetBase, Amount)` representing the burn list item. /// pub fn get_burn_tuple(asset_desc: &str, value: i64) -> (AssetBase, i64) { - use crate::keys::{IssuanceAuthorizingKey, IssuanceKey, IssuanceValidatingKey}; + use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; - let sk_iss = IssuanceKey::from_bytes([0u8; 32]).unwrap(); - let isk: IssuanceAuthorizingKey = (&sk_iss).into(); + let isk = IssuanceAuthorizingKey::from_bytes([0u8; 32]).unwrap(); ( AssetBase::derive(&IssuanceValidatingKey::from(&isk), asset_desc), diff --git a/src/issuance.rs b/src/issuance.rs index 8b5f200a..9fe38c82 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -532,7 +532,7 @@ pub fn verify_issue_bundle( pub enum Error { /// The requested IssueAction not exists in the bundle. IssueActionNotFound, - /// The provided `isk` and the driven `ik` does not match at least one note type. + /// The provided `isk` and the derived `ik` does not match at least one note type. IssueBundleIkMismatchAssetBase, /// `asset_desc` should be between 1 and 512 bytes. WrongAssetDescSize, @@ -562,7 +562,7 @@ impl fmt::Display for Error { IssueBundleIkMismatchAssetBase => { write!( f, - "the provided `isk` and the driven `ik` does not match at least one note type" + "the provided `isk` and the derived `ik` do not match at least one note type" ) } WrongAssetDescSize => { @@ -606,8 +606,7 @@ mod tests { }; use crate::issuance::{verify_issue_bundle, IssueAction, Signed, Unauthorized}; use crate::keys::{ - FullViewingKey, IssuanceAuthorizingKey, IssuanceKey, IssuanceValidatingKey, Scope, - SpendingKey, + FullViewingKey, IssuanceAuthorizingKey, IssuanceValidatingKey, Scope, SpendingKey, }; use crate::note::{AssetBase, Nullifier}; use crate::value::{NoteValue, ValueSum}; @@ -629,8 +628,7 @@ mod tests { ) { let mut rng = OsRng; - let sk_iss = IssuanceKey::random(&mut rng); - let isk: IssuanceAuthorizingKey = (&sk_iss).into(); + let isk = IssuanceAuthorizingKey::random(&mut rng); let ik: IssuanceValidatingKey = (&isk).into(); let fvk = FullViewingKey::from(&SpendingKey::random(&mut rng)); @@ -951,7 +949,7 @@ mod tests { ) .unwrap(); - let wrong_isk: IssuanceAuthorizingKey = (&IssuanceKey::random(&mut OsRng)).into(); + let wrong_isk: IssuanceAuthorizingKey = IssuanceAuthorizingKey::random(&mut OsRng); let err = bundle .prepare([0; 32]) @@ -1183,7 +1181,7 @@ mod tests { ) .unwrap(); - let wrong_isk: IssuanceAuthorizingKey = (&IssuanceKey::random(&mut rng)).into(); + let wrong_isk: IssuanceAuthorizingKey = IssuanceAuthorizingKey::random(&mut rng); let mut signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); @@ -1278,8 +1276,7 @@ mod tests { let mut signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); - let incorrect_sk_iss = IssuanceKey::random(&mut rng); - let incorrect_isk: IssuanceAuthorizingKey = (&incorrect_sk_iss).into(); + let incorrect_isk = IssuanceAuthorizingKey::random(&mut rng); let incorrect_ik: IssuanceValidatingKey = (&incorrect_isk).into(); // Add "bad" note diff --git a/src/keys.rs b/src/keys.rs index 4c6014f2..4748bebd 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -223,25 +223,25 @@ type IssuanceAuth = SpendAuth; /// An issuance key, from which all key material is derived. /// -/// $\mathsf{sk}$ as defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents]. +/// $\mathsf{isk}$ as defined in [ZIP 227][issuancekeycomponents]. /// -/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents +/// [issuancekeycomponents]: https://qed-it.github.io/zips/zip-0227#issuance-key-derivation #[derive(Debug, Copy, Clone)] -pub struct IssuanceKey([u8; 32]); +pub struct IssuanceAuthorizingKey([u8; 32]); -impl From for IssuanceKey { +impl From for IssuanceAuthorizingKey { fn from(sk: SpendingKey) -> Self { - IssuanceKey(*sk.to_bytes()) + IssuanceAuthorizingKey(*sk.to_bytes()) } } -impl ConstantTimeEq for IssuanceKey { +impl ConstantTimeEq for IssuanceAuthorizingKey { fn ct_eq(&self, other: &Self) -> Choice { self.to_bytes().ct_eq(other.to_bytes()) } } -impl IssuanceKey { +impl IssuanceAuthorizingKey { /// Generates a random issuance key. /// /// This is only used when generating a random AssetBase. @@ -255,11 +255,9 @@ impl IssuanceKey { /// Constructs an Orchard issuance key from uniformly-random bytes. /// /// Returns `None` if the bytes do not correspond to a valid Orchard issuance key. - pub fn from_bytes(sk_iss: [u8; 32]) -> CtOption { - let sk_iss = IssuanceKey(sk_iss); - // If isk = 0 (A scalar value), discard this key. - let isk = IssuanceAuthorizingKey::derive_inner(&sk_iss); - CtOption::new(sk_iss, !isk.is_zero()) + pub fn from_bytes(isk_bytes: [u8; 32]) -> CtOption { + let isk = IssuanceAuthorizingKey(isk_bytes); + CtOption::new(isk, 1u8.into()) } /// Returns the raw bytes of the issuance key. @@ -267,7 +265,7 @@ impl IssuanceKey { &self.0 } - /// Derives the Orchard issuance key for the given seed, coin type, and account. + /// Derives the Orchard-ZSA issuance key for the given seed, coin type, and account. pub fn from_zip32_seed( seed: &[u8], coin_type: u32, @@ -282,22 +280,10 @@ impl IssuanceKey { ExtendedSpendingKey::from_path(seed, path, ZIP32_ORCHARD_PERSONALIZATION_FOR_ISSUANCE) .map(|esk| esk.sk().into()) } -} -/// An issuance authorizing key, used to create issuance authorization signatures. -/// This type enforces that the corresponding public point (ik^ℙ) has ỹ = 0. -/// -/// $\mathsf{isk}$ as defined in -/// [Issuance of Zcash Shielded Assets ZIP-0227 § Asset Identifier Generation (DRAFT ZIP)][IssuanceZSA]. -/// -/// [IssuanceZSA]: https://qed-it.github.io/zips/draft-ZIP-0227.html#asset-identifier-generation -#[derive(Clone, Debug)] -pub struct IssuanceAuthorizingKey(redpallas::SigningKey); - -impl IssuanceAuthorizingKey { - /// Derives isk from sk_iss. Internal use only, does not enforce all constraints. - fn derive_inner(sk_iss: &IssuanceKey) -> pallas::Scalar { - to_scalar(PrfExpand::ZsaIsk.expand(&sk_iss.0)) + /// Derives the RedPallas signing key from isk. Internal use only, does not enforce all constraints. + fn derive_inner(&self) -> pallas::Scalar { + to_scalar(PrfExpand::ZsaIsk.expand(&self.0)) } /// Sign the provided message using the `IssuanceAuthorizingKey`. @@ -306,32 +292,23 @@ impl IssuanceAuthorizingKey { rng: &mut (impl RngCore + CryptoRng), msg: &[u8], ) -> redpallas::Signature { - self.0.sign(rng, msg) - } -} - -impl From<&IssuanceKey> for IssuanceAuthorizingKey { - fn from(sk_iss: &IssuanceKey) -> Self { - let isk = IssuanceAuthorizingKey::derive_inner(sk_iss); - // IssuanceAuthorizingKey cannot be constructed such that this assertion would fail. - assert!(!bool::from(isk.is_zero())); - IssuanceAuthorizingKey(conditionally_negate(isk)) + conditionally_negate(self.derive_inner()).sign(rng, msg) } } /// A key used to validate issuance authorization signatures. /// -/// Defined in [Issuance of Zcash Shielded Assets ZIP-0227 § Asset Identifier Generation (DRAFT PR)][IssuanceZSA]. +/// Defined in [ZIP 227: Issuance of Zcash Shielded Assets § Issuance Key Generation][IssuanceZSA]. /// Note that this is $\mathsf{ik}^\mathbb{P}$, which by construction is equivalent to /// $\mathsf{ik}$ but stored here as a RedPallas verification key. /// -/// [IssuanceZSA]: https://qed-it.github.io/zips/draft-ZIP-0227.html#asset-identifier-generation +/// [IssuanceZSA]: https://qed-it.github.io/zips/zip-0227#issuance-key-derivation #[derive(Debug, Clone, PartialOrd, Ord)] pub struct IssuanceValidatingKey(VerificationKey); impl From<&IssuanceAuthorizingKey> for IssuanceValidatingKey { fn from(isk: &IssuanceAuthorizingKey) -> Self { - IssuanceValidatingKey((&isk.0).into()) + IssuanceValidatingKey((&(conditionally_negate(isk.derive_inner()))).into()) } } @@ -1116,11 +1093,10 @@ impl SharedSecret { #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] pub mod testing { use super::{ - DiversifierIndex, DiversifierKey, EphemeralSecretKey, IssuanceAuthorizingKey, IssuanceKey, + DiversifierIndex, DiversifierKey, EphemeralSecretKey, IssuanceAuthorizingKey, IssuanceValidatingKey, SpendingKey, }; use proptest::prelude::*; - use rand::{rngs::StdRng, SeedableRng}; prop_compose! { /// Generate a uniformly distributed Orchard spending key. @@ -1137,15 +1113,15 @@ pub mod testing { } prop_compose! { - /// Generate a uniformly distributed Orchard issuance key. - pub fn arb_issuance_key()( + /// Generate a uniformly distributed Orchard issuance master key. + pub fn arb_issuance_authorizing_key()( key in prop::array::uniform32(prop::num::u8::ANY) - .prop_map(IssuanceKey::from_bytes) + .prop_map(IssuanceAuthorizingKey::from_bytes) .prop_filter( "Values must correspond to valid Orchard issuance keys.", |opt| bool::from(opt.is_some()) ) - ) -> IssuanceKey { + ) -> IssuanceAuthorizingKey { key.unwrap() } } @@ -1182,14 +1158,6 @@ pub mod testing { } } - prop_compose! { - /// Generate a uniformly distributed RedDSA issuance authorizing key. - pub fn arb_issuance_authorizing_key()(rng_seed in prop::array::uniform32(prop::num::u8::ANY)) -> IssuanceAuthorizingKey { - let mut rng = StdRng::from_seed(rng_seed); - IssuanceAuthorizingKey::from(&IssuanceKey::random(&mut rng)) - } - } - prop_compose! { /// Generate a uniformly distributed RedDSA issuance validating key. pub fn arb_issuance_validating_key()(isk in arb_issuance_authorizing_key()) -> IssuanceValidatingKey { @@ -1267,10 +1235,7 @@ mod tests { let ask: SpendAuthorizingKey = (&sk).into(); assert_eq!(<[u8; 32]>::from(&ask.0), tv.ask); - let sk_iss = IssuanceKey::from_bytes(tv.sk).unwrap(); - - let isk: IssuanceAuthorizingKey = (&sk_iss).into(); - assert_eq!(<[u8; 32]>::from(&isk.0), tv.isk); + let isk = IssuanceAuthorizingKey::from_bytes(tv.sk).unwrap(); let ak: SpendValidatingKey = (&ask).into(); assert_eq!(<[u8; 32]>::from(ak.0), tv.ak); diff --git a/src/note/asset_base.rs b/src/note/asset_base.rs index 41a284a8..ba8c8082 100644 --- a/src/note/asset_base.rs +++ b/src/note/asset_base.rs @@ -10,7 +10,7 @@ use subtle::{Choice, ConstantTimeEq, CtOption}; use crate::constants::fixed_bases::{ NATIVE_ASSET_BASE_V_BYTES, VALUE_COMMITMENT_PERSONALIZATION, ZSA_ASSET_BASE_PERSONALIZATION, }; -use crate::keys::{IssuanceAuthorizingKey, IssuanceKey, IssuanceValidatingKey}; +use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; /// Note type identifier. #[derive(Clone, Copy, Debug, Eq)] @@ -102,8 +102,7 @@ impl AssetBase { /// /// This is only used in tests. pub(crate) fn random(rng: &mut impl RngCore) -> Self { - let sk_iss = IssuanceKey::random(rng); - let isk = IssuanceAuthorizingKey::from(&sk_iss); + let isk = IssuanceAuthorizingKey::random(rng); let ik = IssuanceValidatingKey::from(&isk); let asset_descr = "zsa_asset"; AssetBase::derive(&ik, asset_descr) @@ -136,19 +135,18 @@ pub mod testing { use proptest::prelude::*; - use crate::keys::{testing::arb_issuance_key, IssuanceAuthorizingKey, IssuanceValidatingKey}; + use crate::keys::{testing::arb_issuance_authorizing_key, IssuanceValidatingKey}; prop_compose! { /// Generate a uniformly distributed note type pub fn arb_asset_id()( is_native in prop::bool::ANY, - sk in arb_issuance_key(), + isk in arb_issuance_authorizing_key(), str in "[A-Za-z]{255}", ) -> AssetBase { if is_native { AssetBase::native() } else { - let isk = IssuanceAuthorizingKey::from(&sk); AssetBase::derive(&IssuanceValidatingKey::from(&isk), &str) } } @@ -165,10 +163,9 @@ pub mod testing { prop_compose! { /// Generate an asset ID pub fn arb_zsa_asset_id()( - sk_iss in arb_issuance_key(), + isk in arb_issuance_authorizing_key(), str in "[A-Za-z]{255}" ) -> AssetBase { - let isk = IssuanceAuthorizingKey::from(&sk_iss); AssetBase::derive(&IssuanceValidatingKey::from(&isk), &str) } } @@ -176,10 +173,9 @@ pub mod testing { prop_compose! { /// Generate an asset ID using a specific description pub fn zsa_asset_id(asset_desc: String)( - sk_iss in arb_issuance_key(), + isk in arb_issuance_authorizing_key(), ) -> AssetBase { assert!(super::is_asset_desc_of_valid_size(&asset_desc)); - let isk = IssuanceAuthorizingKey::from(&sk_iss); AssetBase::derive(&IssuanceValidatingKey::from(&isk), &asset_desc) } } diff --git a/src/supply_info.rs b/src/supply_info.rs index 1c2f346b..1752fbd6 100644 --- a/src/supply_info.rs +++ b/src/supply_info.rs @@ -80,10 +80,9 @@ mod tests { use super::*; fn create_test_asset(asset_desc: &str) -> AssetBase { - use crate::keys::{IssuanceAuthorizingKey, IssuanceKey, IssuanceValidatingKey}; + use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; - let sk_iss = IssuanceKey::from_bytes([0u8; 32]).unwrap(); - let isk: IssuanceAuthorizingKey = (&sk_iss).into(); + let isk = IssuanceAuthorizingKey::from_bytes([0u8; 32]).unwrap(); AssetBase::derive(&IssuanceValidatingKey::from(&isk), asset_desc) } diff --git a/tests/zsa.rs b/tests/zsa.rs index 5bf9063c..dec5dcc1 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -13,10 +13,7 @@ use orchard::{ builder::Builder, bundle::Flags, circuit::{ProvingKey, VerifyingKey}, - keys::{ - FullViewingKey, IssuanceKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, - SpendingKey, - }, + keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, value::NoteValue, Address, Anchor, Bundle, Note, }; @@ -61,8 +58,7 @@ fn prepare_keys() -> Keychain { let fvk = FullViewingKey::from(&sk); let recipient = fvk.address_at(0u32, Scope::External); - let sk_iss = IssuanceKey::from_bytes([0; 32]).unwrap(); - let isk = IssuanceAuthorizingKey::from(&sk_iss); + let isk = IssuanceAuthorizingKey::from_bytes([0; 32]).unwrap(); let ik = IssuanceValidatingKey::from(&isk); Keychain { pk,