2021-03-05 15:25:45 -08:00
|
|
|
//! Helper functions defined in the Zcash Protocol Specification.
|
|
|
|
|
|
|
|
use std::iter;
|
|
|
|
|
2021-03-08 13:33:56 -08:00
|
|
|
use blake2b_simd::Params;
|
2021-03-05 15:25:45 -08:00
|
|
|
use ff::PrimeField;
|
2021-03-15 13:33:07 -07:00
|
|
|
use group::{Curve, Group};
|
2021-03-17 19:06:16 -07:00
|
|
|
use halo2::arithmetic::{CurveAffine, CurveExt, FieldExt};
|
|
|
|
use pasta_curves::pallas;
|
2021-03-05 15:25:45 -08:00
|
|
|
|
2021-03-15 18:27:08 -07:00
|
|
|
use crate::{
|
|
|
|
constants::L_ORCHARD_BASE,
|
|
|
|
primitives::{poseidon, sinsemilla},
|
|
|
|
};
|
2021-03-05 15:25:45 -08:00
|
|
|
|
|
|
|
const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
|
|
|
|
|
|
|
|
/// $\mathsf{ToBase}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod q_P)$
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-08 13:33:56 -08:00
|
|
|
pub(crate) fn to_base(x: [u8; 64]) -> pallas::Base {
|
|
|
|
pallas::Base::from_bytes_wide(&x)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// $\mathsf{ToScalar}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod r_P)$
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-08 13:33:56 -08:00
|
|
|
pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar {
|
|
|
|
pallas::Scalar::from_bytes_wide(&x)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:24:50 -07:00
|
|
|
/// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
|
|
|
|
///
|
|
|
|
/// This requires no modular reduction because Pallas' base field is smaller than its
|
|
|
|
/// scalar field.
|
|
|
|
pub(crate) fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
|
|
|
|
pallas::Scalar::from_repr(x.to_repr()).unwrap()
|
|
|
|
}
|
|
|
|
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-05 15:25:45 -08:00
|
|
|
pub(crate) fn commit_ivk(
|
2021-03-15 13:33:07 -07:00
|
|
|
ak: &pallas::Base,
|
2021-03-05 15:25:45 -08:00
|
|
|
nk: &pallas::Base,
|
|
|
|
rivk: &pallas::Scalar,
|
|
|
|
) -> pallas::Scalar {
|
2021-03-15 13:33:07 -07:00
|
|
|
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
|
|
|
// bits, which is equal to L_ORCHARD_BASE.
|
2021-03-18 17:43:18 -07:00
|
|
|
let domain = sinsemilla::CommitDomain::new(&"z.cash:Orchard-CommitIvk");
|
2021-03-29 17:54:36 -07:00
|
|
|
// TODO: handle the (negligible probability of) failure of SinsemillaShortCommit.
|
2021-03-15 18:24:50 -07:00
|
|
|
mod_r_p(
|
|
|
|
domain.short_commit(
|
|
|
|
iter::empty()
|
|
|
|
.chain(ak.to_le_bits().iter().by_val().take(L_ORCHARD_BASE))
|
|
|
|
.chain(nk.to_le_bits().iter().by_val().take(L_ORCHARD_BASE)),
|
|
|
|
rivk,
|
|
|
|
),
|
|
|
|
)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash
|
2021-03-17 12:15:55 -07:00
|
|
|
pub(crate) fn diversify_hash(d: &[u8; 11]) -> pallas::Point {
|
2021-03-17 17:39:04 -07:00
|
|
|
let hasher = pallas::Point::hash_to_curve("z.cash:Orchard-gd");
|
|
|
|
let pk_d = hasher(d);
|
2021-03-15 12:57:03 -07:00
|
|
|
if pk_d.is_identity().into() {
|
2021-03-17 12:15:55 -07:00
|
|
|
// If the identity occurs, we replace it with a different fixed point.
|
2021-03-17 17:39:04 -07:00
|
|
|
hasher(&[])
|
2021-03-15 12:57:03 -07:00
|
|
|
} else {
|
2021-03-17 12:15:55 -07:00
|
|
|
pk_d
|
2021-03-15 12:57:03 -07:00
|
|
|
}
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// $PRF^\mathsf{expand}(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)$
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-17 12:20:40 -07:00
|
|
|
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
|
2021-03-08 13:33:56 -08:00
|
|
|
pub(crate) fn prf_expand(sk: &[u8], t: &[u8]) -> [u8; 64] {
|
2021-03-05 15:25:45 -08:00
|
|
|
prf_expand_vec(sk, &[t])
|
|
|
|
}
|
|
|
|
|
2021-03-08 13:33:56 -08:00
|
|
|
pub(crate) fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> [u8; 64] {
|
2021-03-05 15:25:45 -08:00
|
|
|
let mut h = Params::new()
|
|
|
|
.hash_length(64)
|
|
|
|
.personal(PRF_EXPAND_PERSONALIZATION)
|
|
|
|
.to_state();
|
|
|
|
h.update(sk);
|
|
|
|
for t in ts {
|
|
|
|
h.update(t);
|
|
|
|
}
|
2021-03-08 13:33:56 -08:00
|
|
|
*h.finalize().as_array()
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:27:08 -07:00
|
|
|
/// $PRF^\mathsf{nfOrchard}(nk, \rho) := Poseidon(nk, \rho)$
|
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
|
|
|
|
///
|
2021-03-29 17:54:23 -07:00
|
|
|
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
|
2021-03-15 18:27:08 -07:00
|
|
|
pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
|
|
|
|
poseidon::Hash::init(poseidon::OrchardNullifier, poseidon::ConstantLength(2))
|
|
|
|
.hash(iter::empty().chain(Some(nk)).chain(Some(rho)))
|
|
|
|
}
|
|
|
|
|
2021-03-17 12:20:40 -07:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
2021-03-05 15:25:45 -08:00
|
|
|
pub(crate) fn ka_orchard(sk: &pallas::Scalar, b: &pallas::Point) -> pallas::Point {
|
|
|
|
b * sk
|
|
|
|
}
|
|
|
|
|
2021-03-17 12:20:40 -07:00
|
|
|
/// Coordinate extractor for Pallas.
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-17 12:20:40 -07:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
|
2021-03-05 17:17:51 -08:00
|
|
|
///
|
|
|
|
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
|
2021-03-05 15:25:45 -08:00
|
|
|
pub(crate) fn extract_p(point: &pallas::Point) -> pallas::Base {
|
|
|
|
if let Some((x, _)) = point.to_affine().get_xy().into() {
|
|
|
|
x
|
|
|
|
} else {
|
|
|
|
pallas::Base::zero()
|
|
|
|
}
|
|
|
|
}
|
2021-03-17 12:15:55 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use group::Group;
|
2021-03-17 19:06:16 -07:00
|
|
|
use halo2::arithmetic::CurveExt;
|
|
|
|
use pasta_curves::pallas;
|
2021-03-17 12:15:55 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn diversify_hash_substitution() {
|
|
|
|
assert!(!bool::from(
|
|
|
|
pallas::Point::hash_to_curve("z.cash:Orchard-gd")(&[]).is_identity()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|