Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename sk_iss to imk, the IssuanceKey struct to IssuanceMasterKey, and move to a two key structure #92

Merged
merged 11 commits into from
Nov 7, 2023
7 changes: 3 additions & 4 deletions src/bundle/burn_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand All @@ -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),
Expand Down
17 changes: 7 additions & 10 deletions src/issuance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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};
Expand All @@ -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));
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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
Expand Down
83 changes: 24 additions & 59 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -255,19 +255,17 @@ 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.
pub fn to_bytes(&self) -> &[u8; 32] {
&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,
Expand All @@ -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`.
Expand All @@ -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())
}
}

Expand Down Expand Up @@ -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.
Expand All @@ -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()
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 6 additions & 10 deletions src/note/asset_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -165,21 +163,19 @@ 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)
}
}

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)
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/supply_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 2 additions & 6 deletions tests/zsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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,
Expand Down
Loading