Rename `sk_iss` to `isk`, the `IssuanceKey` struct to `IssuanceAuthorizingKey`, and move to a two key structure (#92)

This performs a consistent renaming of the issuance authorizing key to make it consistent with the ZIP.
It also reworks the `IssuanceAuthorizingKey` struct in place of the `IssuanceKey` and `IssuanceAuthorizingKey` structs, as part of using a two key structure for issuance, as specified in ZIP 227.
This commit is contained in:
Vivek Arte 2023-11-07 17:06:30 +05:30 committed by GitHub
parent 7b943e197e
commit f38d6b9e4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 92 deletions

View File

@ -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),

View File

@ -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

View File

@ -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<SpendingKey> for IssuanceKey {
impl From<SpendingKey> 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<Self> {
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<Self> {
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<IssuanceAuth>);
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<IssuanceAuth> {
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<IssuanceAuth>);
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);

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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,