mirror of https://github.com/zcash/orchard.git
Move zero-handling from inside CommitIVK to outside it
This more closely matches the change to the protocol spec.
This commit is contained in:
parent
5356804bfe
commit
72b6febf7b
21
src/keys.rs
21
src/keys.rs
|
@ -537,9 +537,30 @@ impl From<&FullViewingKey> for KeyAgreementPrivateKey {
|
||||||
|
|
||||||
impl KeyAgreementPrivateKey {
|
impl KeyAgreementPrivateKey {
|
||||||
/// Derives ivk from fvk. Internal use only, does not enforce all constraints.
|
/// Derives ivk from fvk. Internal use only, does not enforce all constraints.
|
||||||
|
///
|
||||||
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
|
///
|
||||||
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
|
||||||
fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
|
fn derive_inner(fvk: &FullViewingKey) -> CtOption<NonZeroPallasBase> {
|
||||||
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
|
let ak = extract_p(&pallas::Point::from_bytes(&(&fvk.ak.0).into()).unwrap());
|
||||||
commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
|
commit_ivk(&ak, &fvk.nk.0, &fvk.rivk.0)
|
||||||
|
// sinsemilla::CommitDomain::short_commit returns a value in range
|
||||||
|
// [0..q_P] ∪ {⊥}:
|
||||||
|
// - sinsemilla::HashDomain::hash_to_point uses incomplete addition and
|
||||||
|
// returns a point in P* ∪ {⊥}.
|
||||||
|
// - sinsemilla::CommitDomain::commit applies a final complete addition step
|
||||||
|
// and returns a point in P ∪ {⊥}.
|
||||||
|
// - 0 is not a valid x-coordinate for any Pallas point.
|
||||||
|
// - sinsemilla::CommitDomain::short_commit calls extract_p_bottom, which
|
||||||
|
// replaces the identity (which has no affine coordinates) with 0.
|
||||||
|
//
|
||||||
|
// Commit^ivk.Output is specified as [1..q_P] ∪ {⊥}, so we explicitly check
|
||||||
|
// for 0 and map it to None. Note that we are collapsing this case (which is
|
||||||
|
// rejected by the circuit) with ⊥ (which the circuit explicitly allows for
|
||||||
|
// efficiency); this is fine because we don't want users of the `orchard`
|
||||||
|
// crate to encounter either case (and it matches the behaviour described in
|
||||||
|
// Section 4.2.3 of the protocol spec when generating spending keys).
|
||||||
|
.and_then(NonZeroPallasBase::from_base)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the payment address for this key corresponding to the given diversifier.
|
/// Returns the payment address for this key corresponding to the given diversifier.
|
||||||
|
|
34
src/spec.rs
34
src/spec.rs
|
@ -166,39 +166,23 @@ pub(crate) fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
|
||||||
pallas::Scalar::from_repr(x.to_repr()).unwrap()
|
pallas::Scalar::from_repr(x.to_repr()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [concretesinsemillacommit]: https://zips.z.cash/protocol/protocol.pdf#concretesinsemillacommit
|
||||||
pub(crate) fn commit_ivk(
|
pub(crate) fn commit_ivk(
|
||||||
ak: &pallas::Base,
|
ak: &pallas::Base,
|
||||||
nk: &pallas::Base,
|
nk: &pallas::Base,
|
||||||
rivk: &pallas::Scalar,
|
rivk: &pallas::Scalar,
|
||||||
) -> CtOption<NonZeroPallasBase> {
|
) -> CtOption<pallas::Base> {
|
||||||
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
||||||
// bits, which is equal to L_ORCHARD_BASE.
|
// bits, which is equal to L_ORCHARD_BASE.
|
||||||
let domain = sinsemilla::CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
|
let domain = sinsemilla::CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
|
||||||
domain
|
domain.short_commit(
|
||||||
.short_commit(
|
iter::empty()
|
||||||
iter::empty()
|
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
||||||
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
||||||
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
rivk,
|
||||||
rivk,
|
)
|
||||||
)
|
|
||||||
// sinsemilla::CommitDomain::short_commit returns a value in range [0..q_P] ∪ {⊥}:
|
|
||||||
// - sinsemilla::HashDomain::hash_to_point uses incomplete addition and returns a
|
|
||||||
// point in P* ∪ {⊥}.
|
|
||||||
// - sinsemilla::CommitDomain::commit applies a final complete addition step and
|
|
||||||
// returns a point in P ∪ {⊥}.
|
|
||||||
// - 0 is not a valid x-coordinate for any Pallas point.
|
|
||||||
// - sinsemilla::CommitDomain::short_commit calls extract_p_bottom, which replaces
|
|
||||||
// the identity (which has no affine coordinates) with 0.
|
|
||||||
//
|
|
||||||
// Commit^ivk.Output is specified as [1..q_P] ∪ {⊥}, so we explicitly check for 0
|
|
||||||
// and map it to None. Note that we are collapsing this case (which is rejected by
|
|
||||||
// the circuit) with ⊥ (which the circuit explicitly allows for efficiency); this
|
|
||||||
// is fine because we don't want users of the `orchard` crate to encounter either
|
|
||||||
// case.
|
|
||||||
.and_then(NonZeroPallasBase::from_base)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
||||||
|
|
Loading…
Reference in New Issue