Bunch more work implementing orchard, porting from sapling where applicable

This commit is contained in:
Deirdre Connolly 2021-03-08 05:30:39 -05:00 committed by Deirdre Connolly
parent 40383b2741
commit c3e40d73cf
10 changed files with 385 additions and 1534 deletions

View File

@ -5,16 +5,13 @@ mod address;
#[cfg(any(test, feature = "proptest-impl"))]
mod arbitrary;
mod commitment;
mod note;
#[cfg(test)]
mod tests;
pub mod keys;
mod note;
mod sinsemilla;
pub mod tree;
pub use action::Action;
pub use address::Address;
pub use commitment::{CommitmentRandomness, NoteCommitment, ValueCommitment};
pub use keys::Diversifier;
pub use note::{EncryptedNote, Note, Nullifier, WrappedNoteKey};
pub use note::{EncryptedNote, Note, Nullifier};

View File

@ -1,9 +1,9 @@
//! Note and value commitments.
#[cfg(test)]
mod test_vectors;
// #[cfg(test)]
// mod test_vectors;
pub mod pedersen_hashes;
pub mod sinsemilla_hashes;
use std::{convert::TryFrom, fmt, io};
@ -18,9 +18,23 @@ use crate::{
},
};
use super::keys::{find_group_hash, Diversifier, TransmissionKey};
use super::{
keys::{Diversifier, TransmissionKey},
sinsemilla::*,
};
use pedersen_hashes::*;
/// Generates a random scalar from the scalar field 𝔽_{q_P}.
///
/// https://zips.z.cash/protocol/nu5.pdf#pallasandvesta
pub fn generate_trapdoor<T>(csprng: &mut T) -> pallas::Scalar
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 64];
csprng.fill_bytes(&mut bytes);
// Scalar::from_bytes_wide() reduces the input modulo q under the hood.
pallas::Scalar::from_bytes_wide(&bytes)
}
/// The randomness used in the Simsemilla Hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)]
@ -33,17 +47,17 @@ pub struct NoteCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::A
impl fmt::Debug for NoteCommitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("NoteCommitment")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.field("x", &hex::encode(self.0.get_x().to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes()))
.finish()
}
}
impl Eq for NoteCommitment {}
impl From<jubjub::ExtendedPoint> for NoteCommitment {
fn from(extended_point: jubjub::ExtendedPoint) -> Self {
Self(pallas::Affine::from(extended_point))
impl From<pallas::Point> for NoteCommitment {
fn from(projective_point: pallas::Point) -> Self {
Self(pallas::Affine::from(projective_point))
}
}
@ -71,11 +85,10 @@ impl NoteCommitment {
/// Generate a new _NoteCommitment_ and the randomness used to create it.
///
/// We return the randomness because it is needed to construct a _Note_,
/// before it is encrypted as part of an _Output Description_. This is a
/// before it is encrypted as part of an output of an _Action_. This is a
/// higher level function that calls `NoteCommit^Orchard_rcm` internally.
///
/// NoteCommit^Orchard_rcm (g*_d , pk*_d , v) :=
/// WindowedPedersenCommit_rcm([1; 6] || I2LEBSP_64(v) || g*_d || pk*_d)
/// NoteCommit^Orchard_rcm(repr_P(gd),repr_P(pkd), v, ρ, ψ) :=
///
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
#[allow(non_snake_case)]
@ -84,48 +97,42 @@ impl NoteCommitment {
diversifier: Diversifier,
transmission_key: TransmissionKey,
value: Amount<NonNegative>,
rho: pallas::Base,
psi: pallas::Base,
) -> Option<(CommitmentRandomness, Self)>
where
T: RngCore + CryptoRng,
{
// s as in the argument name for WindowedPedersenCommit_r(s)
let mut s: BitVec<Lsb0, u8> = BitVec::new();
unimplemented!();
// Prefix
s.append(&mut bitvec![1; 6]);
// // s as in the argument name for WindowedPedersenCommit_r(s)
// let mut s: BitVec<Lsb0, u8> = BitVec::new();
// Jubjub repr_J canonical byte encoding
// https://zips.z.cash/protocol/protocol.pdf#jubjub
//
// The `TryFrom<Diversifier>` impls for the `jubjub::*Point`s handles
// calling `DiversifyHash` implicitly.
let g_d_bytes: [u8; 32];
if let Ok(g_d) = pallas::Affine::try_from(diversifier) {
g_d_bytes = g_d.to_bytes();
} else {
return None;
}
// // Prefix
// s.append(&mut bitvec![1; 6]);
let pk_d_bytes = <[u8; 32]>::from(transmission_key);
let v_bytes = value.to_bytes();
// // The `TryFrom<Diversifier>` impls for the `jubjub::*Point`s handles
// // calling `DiversifyHash` implicitly.
// let g_d_bytes: [u8; 32];
// if let Ok(g_d) = pallas::Affine::try_from(diversifier) {
// g_d_bytes = g_d.to_bytes();
// } else {
// return None;
// }
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&g_d_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_d_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&v_bytes[..]));
// let pk_d_bytes = <[u8; 32]>::from(transmission_key);
// let v_bytes = value.to_bytes();
let rcm = CommitmentRandomness(generate_trapdoor(csprng));
// s.append(&mut BitVec::<Lsb0, u8>::from_slice(&g_d_bytes[..]));
// s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_d_bytes[..]));
// s.append(&mut BitVec::<Lsb0, u8>::from_slice(&v_bytes[..]));
Some((
rcm,
NoteCommitment::from(windowed_pedersen_commitment(rcm.0, &s)),
))
}
// let rcm = CommitmentRandomness(generate_trapdoor(csprng));
/// Hash Extractor for Pallas (?)
///
/// https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
pub fn extract_x(&self) -> pallas::Base {
self.0.get_u()
// Some((
// rcm,
// NoteCommitment::from(windowed_pedersen_commitment(rcm.0, &s)),
// ))
}
}
@ -162,24 +169,23 @@ impl std::ops::AddAssign<ValueCommitment> for ValueCommitment {
impl fmt::Debug for ValueCommitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ValueCommitment")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.field("x", &hex::encode(self.0.get_x().to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes()))
.finish()
}
}
impl From<jubjub::ExtendedPoint> for ValueCommitment {
fn from(extended_point: jubjub::ExtendedPoint) -> Self {
Self(pallas::Affine::from(extended_point))
impl From<pallas::Point> for ValueCommitment {
fn from(projective_point: pallas::Point) -> Self {
Self(pallas::Affine::from(projective_point))
}
}
impl Eq for ValueCommitment {}
/// LEBS2OSP256(repr_J(cv))
/// LEBS2OSP256(repr_P(cv))
///
/// https://zips.z.cash/protocol/protocol.pdf#spendencoding
/// https://zips.z.cash/protocol/protocol.pdf#jubjub
/// https://zips.z.cash/protocol/protocol.pdf#pallasandvesta
impl From<ValueCommitment> for [u8; 32] {
fn from(cm: ValueCommitment) -> [u8; 32] {
cm.0.to_bytes()
@ -220,10 +226,9 @@ impl std::iter::Sum for ValueCommitment {
}
}
/// LEBS2OSP256(repr_J(cv))
/// LEBS2OSP256(repr_P(cv))
///
/// https://zips.z.cash/protocol/protocol.pdf#spendencoding
/// https://zips.z.cash/protocol/protocol.pdf#jubjub
/// https://zips.z.cash/protocol/protocol.pdf#pallasandvesta
impl TryFrom<[u8; 32]> for ValueCommitment {
type Error = &'static str;
@ -266,15 +271,17 @@ impl ValueCommitment {
/// Generate a new _ValueCommitment_ from an existing _rcv_ on a _value_.
///
/// ValueCommit^Orchard(v) :=
///
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
#[allow(non_snake_case)]
pub fn new(rcv: jubjub::Fr, value: Amount) -> Self {
let v = jubjub::Fr::from(value);
pub fn new(rcv: pallas::Scalar, value: Amount) -> Self {
let v = pallas::Scalar::from(value);
// TODO: These generator points can be generated once somewhere else to
// avoid having to recompute them on every new commitment.
let V = find_group_hash(*b"z.cash:Orchard-cv", b"v");
let R = find_group_hash(*b"z.cash:Orchard-cv", b"r");
let V = pallas_group_hash(*b"z.cash:Orchard-cv", b"v");
let R = pallas_group_hash(*b"z.cash:Orchard-cv", b"r");
Self::from(V * v + R * rcv)
}
@ -288,19 +295,21 @@ mod tests {
use super::*;
#[test]
fn pedersen_hash_to_point_test_vectors() {
fn sinsemilla_hash_to_point_test_vectors() {
zebra_test::init();
const D: [u8; 8] = *b"Zcash_PH";
for test_vector in test_vectors::TEST_VECTORS.iter() {
let result =
pallas::Affine::from(pedersen_hash_to_point(D, &test_vector.input_bits.clone()));
pallas::Affine::from(sinsemilla_hash_to_point(D, &test_vector.input_bits.clone()));
assert_eq!(result, test_vector.output_point);
}
}
// TODO: these test vectors for ops are from Jubjub, replace with Pallas ones
#[test]
fn add() {
zebra_test::init();
@ -308,13 +317,13 @@ mod tests {
let identity = ValueCommitment(pallas::Affine::identity());
let g = ValueCommitment(pallas::Affine::from_raw_unchecked(
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0xe4b3_d35d_f1a7_adfe,
0xcaf5_5d1b_29bf_81af,
0x8b0f_03dd_d60a_8187,
0x62ed_cbb8_bf37_87c8,
]),
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0x0000_0000_0000_000b,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
@ -332,13 +341,13 @@ mod tests {
let mut identity = ValueCommitment(pallas::Affine::identity());
let g = ValueCommitment(pallas::Affine::from_raw_unchecked(
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0xe4b3_d35d_f1a7_adfe,
0xcaf5_5d1b_29bf_81af,
0x8b0f_03dd_d60a_8187,
0x62ed_cbb8_bf37_87c8,
]),
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0x0000_0000_0000_000b,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
@ -357,13 +366,13 @@ mod tests {
zebra_test::init();
let g_point = pallas::Affine::from_raw_unchecked(
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0xe4b3_d35d_f1a7_adfe,
0xcaf5_5d1b_29bf_81af,
0x8b0f_03dd_d60a_8187,
0x62ed_cbb8_bf37_87c8,
]),
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0x0000_0000_0000_000b,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
@ -383,13 +392,13 @@ mod tests {
zebra_test::init();
let g_point = pallas::Affine::from_raw_unchecked(
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0xe4b3_d35d_f1a7_adfe,
0xcaf5_5d1b_29bf_81af,
0x8b0f_03dd_d60a_8187,
0x62ed_cbb8_bf37_87c8,
]),
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0x0000_0000_0000_000b,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,
@ -412,13 +421,13 @@ mod tests {
zebra_test::init();
let g_point = pallas::Affine::from_raw_unchecked(
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0xe4b3_d35d_f1a7_adfe,
0xcaf5_5d1b_29bf_81af,
0x8b0f_03dd_d60a_8187,
0x62ed_cbb8_bf37_87c8,
]),
jubjub::Fq::from_raw([
pallas::Base::from_raw([
0x0000_0000_0000_000b,
0x0000_0000_0000_0000,
0x0000_0000_0000_0000,

View File

@ -1,147 +0,0 @@
//! Pedersen hash functions and helpers.
use bitvec::prelude::*;
use rand_core::{CryptoRng, RngCore};
use super::super::keys::find_group_hash;
/// I_i
///
/// Expects i to be 1-indexed from the loop it's called in.
///
/// https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash
#[allow(non_snake_case)]
fn I_i(domain: [u8; 8], i: u32) -> jubjub::ExtendedPoint {
find_group_hash(domain, &(i - 1).to_le_bytes())
}
/// The encoding function ⟨Mᵢ⟩
///
/// Σ j={0,k-1}: (1 - 2x₂)⋅(1 + x₀ + 2x₁)⋅2^(4⋅j)
///
/// https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash
#[allow(non_snake_case)]
fn M_i(segment: &BitSlice<Lsb0, u8>) -> jubjub::Fr {
let mut m_i = jubjub::Fr::zero();
for (j, chunk) in segment.chunks(3).enumerate() {
// Pad each chunk with zeros.
let mut store = 0u8;
let bits = store.bits_mut::<Lsb0>();
chunk
.iter()
.enumerate()
.for_each(|(i, bit)| bits.set(i, *bit));
let mut tmp = jubjub::Fr::one();
if bits[0] {
tmp += &jubjub::Fr::one();
}
if bits[1] {
tmp += &jubjub::Fr::one().double();
}
if bits[2] {
tmp -= tmp.double();
}
if j > 0 {
// Inclusive range!
tmp *= (1..=(4 * j)).fold(jubjub::Fr::one(), |acc, _| acc.double());
}
m_i += tmp;
}
m_i
}
/// "...an algebraic hash function with collision resistance (for fixed input
/// length) derived from assumed hardness of the Discrete Logarithm Problem on
/// the Jubjub curve."
///
/// PedersenHash is used in the definitions of Pedersen commitments (§
/// 5.4.7.2 Windowed Pedersen commitments), and of the Pedersen hash for the
/// Sapling incremental Merkle tree (§ 5.4.1.3 MerkleCRH^Sapling Hash
/// Function).
///
/// https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash
#[allow(non_snake_case)]
pub fn pedersen_hash_to_point(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> jubjub::ExtendedPoint {
let mut result = jubjub::ExtendedPoint::identity();
// Split M into n segments of 3 * c bits, where c = 63, padding the last
// segment with zeros.
//
// This loop is 1-indexed per the math definitions in the spec.
//
// https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash
for (i, segment) in M
.chunks(189)
.enumerate()
.map(|(i, segment)| (i + 1, segment))
{
result += I_i(domain, i as u32) * M_i(&segment);
}
result
}
/// Pedersen Hash Function
///
/// This is technically returning 255 (l_MerkleSapling) bits, not 256.
///
/// https://zips.z.cash/protocol/protocol.pdf#concretepedersenhash
#[allow(non_snake_case)]
pub fn pedersen_hash(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> jubjub::Fq {
jubjub::AffinePoint::from(pedersen_hash_to_point(domain, M)).get_u()
}
/// Mixing Pedersen Hash Function
///
/// Used to compute ρ from a note commitment and its position in the note
/// commitment tree. It takes as input a Pedersen commitment P, and hashes it
/// with another input x.
///
/// MixingPedersenHash(P, x) := P + [x]FindGroupHash^J^(r)(“Zcash_J_”, “”)
///
/// https://zips.z.cash/protocol/protocol.pdf#concretemixinghash
#[allow(non_snake_case)]
pub fn mixing_pedersen_hash(P: jubjub::ExtendedPoint, x: jubjub::Fr) -> jubjub::ExtendedPoint {
const J: [u8; 8] = *b"Zcash_J_";
P + find_group_hash(J, b"") * x
}
/// Construct a 'windowed' Pedersen commitment by reusing a Pederson hash
/// construction, and adding a randomized point on the Jubjub curve.
///
/// WindowedPedersenCommit_r (s) := \
/// PedersenHashToPoint(“Zcash_PH”, s) + [r]FindGroupHash^J^(r)(“Zcash_PH”, “r”)
///
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
pub fn windowed_pedersen_commitment(r: jubjub::Fr, s: &BitVec<Lsb0, u8>) -> jubjub::ExtendedPoint {
const D: [u8; 8] = *b"Zcash_PH";
pedersen_hash_to_point(D, &s) + find_group_hash(D, b"r") * r
}
/// Generates a random scalar from the scalar field 𝔽_{r_𝕁}.
///
/// The prime order subgroup 𝕁^(r) is the order-r_𝕁 subgroup of 𝕁 that consists
/// of the points whose order divides r. This function is useful when generating
/// the uniform distribution on 𝔽_{r_𝕁} needed for Sapling commitment schemes'
/// trapdoor generators.
///
/// https://zips.z.cash/protocol/protocol.pdf#jubjub
pub fn generate_trapdoor<T>(csprng: &mut T) -> jubjub::Fr
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 64];
csprng.fill_bytes(&mut bytes);
// Fr::from_bytes_wide() reduces the input modulo r via Fr::from_u512()
jubjub::Fr::from_bytes_wide(&bytes)
}

View File

@ -1,299 +0,0 @@
// Test vector data generated from
// https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_pedersen.py
//
// These vectors in particular correspond to the Personalization::NoteCommitment
// enum variant from the original source.
//
// The Python hex-encoded outputs for these test vectors were output in
// big-endian byte order, so to parse them, we reversed their order; in
// librustzcash, they match their Display impl to match the Python hex strings
// and that's what they compare in their unit tests, not the bytes.
#![allow(dead_code)]
use bitvec::prelude::*;
use lazy_static::lazy_static;
fn point_from_hex<T: AsRef<[u8]>>(point_in_hex: T) -> jubjub::AffinePoint {
let mut bytes = [0u8; 32];
let _ = hex::decode_to_slice(point_in_hex, &mut bytes);
jubjub::AffinePoint::from_bytes(bytes).unwrap()
}
pub struct TestVector {
pub input_bits: BitVec<Lsb0, u8>,
pub output_point: jubjub::AffinePoint,
}
lazy_static! {
pub static ref TEST_VECTORS: [TestVector; 12] = [
TestVector {
input_bits: bitvec![Lsb0, u8; 1, 1, 1, 1, 1, 1],
// original librustzcash affine point test vector (in reversed-endian byte order):
// "06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b",
// "3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982"
output_point: point_from_hex(
"82c9cb10480db45d1ed1168e50559d8be0c4551e3a3996a4def05266530fe7bc"
)
},
TestVector {
input_bits: bitvec![Lsb0, u8; 1, 1, 1, 1, 1, 1, 0],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a",
// "46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638"
output_point: point_from_hex(
"3806ecb9f96f032c7a1bc3ea9f9513519298f35a1e9b6adcee54a6f15bd2d046"
)
},
TestVector {
input_bits: bitvec![Lsb0, u8; 1, 1, 1, 1, 1, 1, 1],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97",
// "312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346"
output_point: point_from_hex(
"46e3977cdeeb0f60ca40fa74d0fb53fbff503cfee14f33afb9c9143bb95a2bb1"
)
},
TestVector {
input_bits: bitvec![Lsb0, u8; 1, 1, 1, 1, 1, 1, 1, 0, 0],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97",
// "312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346"
output_point: point_from_hex(
"46e3977cdeeb0f60ca40fa74d0fb53fbff503cfee14f33afb9c9143bb95a2bb1"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1,
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f",
// "4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527"
output_point: point_from_hex(
"27a505d0e13e7ac5821684405f818c8e20432bd7fff468d1280e67da73c320c3"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd",
// "198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2"
output_point: point_from_hex(
"f2998356fe44694390efbe363f86504d1f77514adab959c3e6330c9c96898799"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40",
// "371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743"
output_point: point_from_hex(
"43a74214f65e4ee58342c697f61936c815bdcea655ad2c5cb9e7733b73311937"
)
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1,
1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1,
0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1,
1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0,
1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0,
0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,
0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1,
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0,
1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1,
0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1,
1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27",
// "6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74"
output_point: point_from_hex(
"740b33d32ce8f771cc5ff511f64b701b961d59ebe9de994757ad72132fb312eb"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0,
1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1,
1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,
0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1,
0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
0,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e",
// "6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab"
output_point: point_from_hex(
"ab18ecee12f61a411cbd2082b3330cd1a83aa2de6eb95677c0472eafcfbc5867"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0,
0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1,
1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1,
0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0,
0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1,
1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f",
// "5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71"
output_point: point_from_hex(
"71cd68fbfa381ae092a46e0e830cdef3fcc48a96a72a6558e3ff540597c62fde"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1,
0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1,
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1,
1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1,
0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1,
0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1,
1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1,
1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1,
1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7",
// "44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f"
output_point: point_from_hex(
"9f2d0526c13894597763b664f4a214156f1ec2d695df109ae5cff0c0700877c4"
),
},
TestVector {
input_bits: bitvec![Lsb0, u8;
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
],
// Original librustzcash affine point test vector (in reversed-endian byte order):
// "329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d",
// "471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9"
output_point: point_from_hex(
"a9bf994700dc9fd14d0651602cf7fc7eb932b171b309066db9fc6a6509211dc7"
),
},
];
}

View File

@ -16,6 +16,7 @@ use std::{
};
use bech32::{self, FromBase32, ToBase32, Variant};
use halo2::pasta::pallas;
use rand_core::{CryptoRng, RngCore};
use crate::{
@ -26,12 +27,35 @@ use crate::{
},
};
/// Used to derive the outgoing cipher key _ock_ used to encrypt an Output ciphertext.
use super::sinsemilla::*;
/// Invokes Blake2b-512 as PRF^expand with parameter t.
///
/// PRF^ovk(ock, cv, cm_x, ephemeralKey) := BLAKE2b-256(“Zcash_Orchardock”, ovk || cv || cm_x || ephemeralKey)
/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)
///
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
// TODO: This is basically a duplicate of the one in our sapling module, its
// definition in the draft NU5 spec is incomplete so I'm putting it here in case
// it changes.
fn prf_expand(sk: [u8; 32], t: &[u8]) -> [u8; 64] {
let hash = blake2b_simd::Params::new()
.hash_length(64)
.personal(b"Zcash_ExpandSeed")
.to_state()
.update(&sk[..])
.update(t)
.finalize();
*hash.as_array()
}
/// Used to derive the outgoing cipher key _ock_ used to encrypt an encrypted
/// output note from an Action.
///
/// PRF^ock(ovk, cv, cm_x, ephemeralKey) := BLAKE2b-256(“Zcash_Orchardock”, ovk || cv || cm_x || ephemeralKey)
///
/// https://zips.z.cash/protocol/nu5.pdf#concreteprfs
fn prf_ovk(ovk: [u8; 32], cv: [u8; 32], cm_x: [u8; 32], ephemeral_key: [u8; 32]) -> [u8; 32] {
fn prf_ock(ovk: [u8; 32], cv: [u8; 32], cm_x: [u8; 32], ephemeral_key: [u8; 32]) -> [u8; 32] {
let hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(b"Zcash_Orchardock")
@ -45,50 +69,30 @@ fn prf_ovk(ovk: [u8; 32], cv: [u8; 32], cm_x: [u8; 32], ephemeral_key: [u8; 32])
*hash.as_array()
}
/// Invokes Blake2s-256 as _CRH^ivk_, to derive the IncomingViewingKey
/// bytes from an AuthorizingKey and NullifierDerivingKey.
///
/// _CRH^ivk(ak, nk) := BLAKE2s-256("Zcashivk", ak || nk)_
///
/// https://zips.z.cash/protocol/protocol.pdf#concretecrhivk
fn crh_ivk(ak: [u8; 32], nk: [u8; 32]) -> [u8; 32] {
let hash = blake2s_simd::Params::new()
.hash_length(32)
.personal(b"Zcashivk")
.to_state()
.update(&ak[..])
.update(&nk[..])
.finalize();
*hash.as_array()
}
/// Used to derive a diversified base point from a diversifier value.
///
/// DiversifyHash^Orchard(d) := GroupHash^P("z.cash:Orchard-gd", LEBS2OSP_l_d(d))
///
/// https://zips.z.cash/protocol/protocol.pdf#concretediversifyhash
fn diversify_hash(d: [u8; 11]) -> Option<pallas::Point> {
jubjub_group_hash(*b"Zcash_gd", &d)
fn diversify_hash(d: &[u8]) -> pallas::Point {
pallas_group_hash(*b"z.cash:Orchard-gd", &d)
}
// TODO: replace with reference to redjubjub or jubjub when merged and
// exported.
type Scalar = jubjub::Fr;
/// Magic human-readable strings used to identify what networks
/// Sapling Spending Keys are associated with when encoded/decoded
/// with bech32.
/// Magic human-readable strings used to identify what networks Orchard spending
/// keys are associated with when encoded/decoded with bech32.
///
/// [orchardspendingkeyencoding]: https://zips.z.cash/protocol/nu5.pdf#orchardspendingkeyencoding
mod sk_hrp {
pub const MAINNET: &str = "secret-spending-key-main";
pub const TESTNET: &str = "secret-spending-key-test";
pub const MAINNET: &str = "secret-orchard-sk-main";
pub const TESTNET: &str = "secret-orchard-sk-test";
}
/// A _Spending Key_, as described in [protocol specification
/// §4.2.2][ps].
/// A spending key, as described in [protocol specification §4.2.3][ps].
///
/// Our root secret key of the Sapling key derivation tree. All other
/// Sapling key types derive from the SpendingKey value.
/// Our root secret key of the Orchard key derivation tree. All other Orchard
/// key types derive from the [`SpendingKey`] value.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
@ -159,15 +163,15 @@ impl SpendingKey {
}
}
/// A _Spend Authorizing Key_, as described in [protocol specification
/// §4.2.2][ps].
/// A Spend authorizing key (_ask_), as described in [protocol specification
/// §4.2.3][orchardkeycomponents].
///
/// Used to generate _spend authorization randomizers_ to sign each
/// _Spend Description_, proving ownership of notes.
/// Used to generate _spend authorization randomizers_ to sign each _Spend
/// Description_, proving ownership of notes.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct SpendAuthorizingKey(pub Scalar);
pub struct SpendAuthorizingKey(pub pallas::Scalar);
impl fmt::Debug for SpendAuthorizingKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -184,15 +188,18 @@ impl From<SpendAuthorizingKey> for [u8; 32] {
}
impl From<SpendingKey> for SpendAuthorizingKey {
/// Invokes Blake2b-512 as _PRF^expand_, t=0, to derive a
/// SpendAuthorizingKey from a SpendingKey.
/// Invokes Blake2b-512 as _PRF^expand_, t=6, to derive a
/// `SpendAuthorizingKey` from a `SpendingKey`.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// ask := ToScalar^Orchard(PRF^expand(sk, [6]))
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
fn from(spending_key: SpendingKey) -> SpendAuthorizingKey {
let hash_bytes = prf_expand(spending_key.bytes, &[0]);
let hash_bytes = prf_expand(spending_key.bytes, &[6]);
Self(Scalar::from_bytes_wide(&hash_bytes))
// Handles ToScalar^Orchard
Self(pallas::Scalar::from_bytes_wide(&hash_bytes))
}
}
@ -202,53 +209,12 @@ impl PartialEq<[u8; 32]> for SpendAuthorizingKey {
}
}
/// A _Proof Authorizing Key_, as described in [protocol specification
/// §4.2.2][ps].
///
/// Used in the _Spend Statement_ to prove nullifier integrity.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ProofAuthorizingKey(pub Scalar);
impl fmt::Debug for ProofAuthorizingKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("ProofAuthorizingKey")
.field(&hex::encode(<[u8; 32]>::from(*self)))
.finish()
}
}
impl From<ProofAuthorizingKey> for [u8; 32] {
fn from(nsk: ProofAuthorizingKey) -> Self {
nsk.0.to_bytes()
}
}
impl From<SpendingKey> for ProofAuthorizingKey {
/// For this invocation of Blake2b-512 as _PRF^expand_, t=1.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
fn from(spending_key: SpendingKey) -> ProofAuthorizingKey {
let hash_bytes = prf_expand(spending_key.bytes, &[1]);
Self(Scalar::from_bytes_wide(&hash_bytes))
}
}
impl PartialEq<[u8; 32]> for ProofAuthorizingKey {
fn eq(&self, other: &[u8; 32]) -> bool {
<[u8; 32]>::from(*self) == *other
}
}
/// An _Outgoing Viewing Key_, as described in [protocol specification
/// §4.2.2][ps].
/// An outgoing viewing key, as described in [protocol specification
/// §4.2.3][ps].
///
/// Used to decrypt outgoing notes without spending them.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct OutgoingViewingKey(pub [u8; 32]);
@ -261,7 +227,7 @@ impl fmt::Debug for OutgoingViewingKey {
}
impl From<[u8; 32]> for OutgoingViewingKey {
/// Generate an _OutgoingViewingKey_ from existing bytes.
/// Generate an `OutgoingViewingKey` from existing bytes.
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
@ -273,18 +239,18 @@ impl From<OutgoingViewingKey> for [u8; 32] {
}
}
impl From<SpendingKey> for OutgoingViewingKey {
/// For this invocation of Blake2b-512 as _PRF^expand_, t=2.
impl From<FullViewingKey> for OutgoingViewingKey {
/// Derive an `OutgoingViewingKey` from a `FullViewingKey`.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
/// let 𝐾 = I2LEBSPsk(rivk)
/// let 𝐵 = reprP(ak) || I2LEBSP256(nk)
/// let 𝑅 = PRFexpand
/// 𝐾 ([0x82] || LEBS2OSP512(B))
/// let dk be the rst dk/8 bytes of 𝑅 and let ovk be the remaining ovk/8 bytes of 𝑅.
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
fn from(spending_key: SpendingKey) -> OutgoingViewingKey {
let hash_bytes = prf_expand(spending_key.bytes, &[2]);
let mut bytes = [0u8; 32];
bytes[..].copy_from_slice(&hash_bytes[0..32]);
Self(bytes)
unimplemented!()
}
}
@ -294,57 +260,56 @@ impl PartialEq<[u8; 32]> for OutgoingViewingKey {
}
}
/// An _Authorizing Key_, as described in [protocol specification
/// §4.2.2][ps].
/// A Spend validating key, as described in [protocol specification §4.2.3][orchardkeycomponents].
///
/// Used to validate _Spend Authorization Signatures_, proving
/// ownership of notes.
/// Used to validate Orchard _Spend Authorization Signatures_, proving ownership
/// of notes.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[derive(Copy, Clone, Debug)]
pub struct AuthorizingKey(pub redjubjub::VerificationKey<SpendAuth>);
pub struct SpendValidatingKey(pub redpallas::VerificationKey<SpendAuth>);
impl Eq for AuthorizingKey {}
impl Eq for SpendValidatingKey {}
impl From<[u8; 32]> for AuthorizingKey {
impl From<[u8; 32]> for SpendValidatingKey {
fn from(bytes: [u8; 32]) -> Self {
Self(redjubjub::VerificationKey::try_from(bytes).unwrap())
Self(redpallas::VerificationKey::try_from(bytes).unwrap())
}
}
impl From<AuthorizingKey> for [u8; 32] {
fn from(ak: AuthorizingKey) -> [u8; 32] {
impl From<SpendValidatingKey> for [u8; 32] {
fn from(ak: SpendValidatingKey) -> [u8; 32] {
ak.0.into()
}
}
impl From<SpendAuthorizingKey> for AuthorizingKey {
impl From<SpendAuthorizingKey> for SpendValidatingKey {
fn from(ask: SpendAuthorizingKey) -> Self {
let sk = redjubjub::SigningKey::<SpendAuth>::try_from(<[u8; 32]>::from(ask)).unwrap();
Self(redjubjub::VerificationKey::from(&sk))
let sk = redpallas::SigningKey::<SpendAuth>::try_from(<[u8; 32]>::from(ask)).unwrap();
Self(redpallas::VerificationKey::from(&sk))
}
}
impl PartialEq for AuthorizingKey {
impl PartialEq for SpendValidatingKey {
fn eq(&self, other: &Self) -> bool {
<[u8; 32]>::from(self.0) == <[u8; 32]>::from(other.0)
}
}
impl PartialEq<[u8; 32]> for AuthorizingKey {
impl PartialEq<[u8; 32]> for SpendValidatingKey {
fn eq(&self, other: &[u8; 32]) -> bool {
<[u8; 32]>::from(self.0) == *other
}
}
/// A _Nullifier Deriving Key_, as described in [protocol
/// specification §4.2.2][ps].
/// A Orchard nullifier deriving key, as described in [protocol specification
/// §4.2.3][orchardkeycomponents].
///
/// Used to create a _Nullifier_ per note.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [orchardkeycomponents]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[derive(Copy, Clone, PartialEq)]
pub struct NullifierDerivingKey(pub jubjub::AffinePoint);
pub struct NullifierDerivingKey(pub pallas::Base);
impl fmt::Debug for NullifierDerivingKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -357,7 +322,7 @@ impl fmt::Debug for NullifierDerivingKey {
impl From<[u8; 32]> for NullifierDerivingKey {
fn from(bytes: [u8; 32]) -> Self {
Self(jubjub::AffinePoint::from_bytes(bytes).unwrap())
Self(pallas::Affine::from_bytes(bytes).unwrap())
}
}
@ -375,24 +340,17 @@ impl From<&NullifierDerivingKey> for [u8; 32] {
}
}
impl From<ProofAuthorizingKey> for NullifierDerivingKey {
impl From<SpendingKey> for NullifierDerivingKey {
/// Requires JubJub's _FindGroupHash^J("Zcash_H_", "")_, then uses
/// the resulting generator point to scalar multiply the
/// ProofAuthorizingKey into the new NullifierDerivingKey
///
/// https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/group_hash.rs
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub
fn from(nsk: ProofAuthorizingKey) -> Self {
// Should this point, when generated, be fixed for the rest of
// the protocol instance? Since this is kind of hash-and-pray, it
// seems it might not always return the same result?
let generator_point = zcash_h();
fn from(sk: SpendingKey) -> Self {
let generator_point = prf_expand(sk, []);
// TODO: impl Mul<ExtendedPoint> for Fr, so we can reverse
// this to match the math in the spec / general scalar mult
// notation convention.
Self(jubjub::AffinePoint::from(generator_point * nsk.0))
Self(pallas::Affine::from(generator_point * sk.0))
}
}
@ -402,24 +360,36 @@ impl PartialEq<[u8; 32]> for NullifierDerivingKey {
}
}
/// Magic human-readable strings used to identify what networks
/// Sapling IncomingViewingKeys are associated with when
/// encoded/decoded with bech32.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct IvkCommitRandomness(pallas::Scalar);
impl fmt::Debug for IvkCommitRandomness {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("IvkCommitRandomness")
.field(&hex::encode(self.0.to_bytes()))
.finish()
}
}
/// Magic human-readable strings used to identify what networks Orchard incoming
/// viewing keys are associated with when encoded/decoded with bech32.
///
/// https://zips.z.cash/protocol/nu5.pdf#orchardinviewingkeyencoding
mod ivk_hrp {
pub const MAINNET: &str = "zivks";
pub const TESTNET: &str = "zivktestsapling";
pub const MAINNET: &str = "zivko";
pub const TESTNET: &str = "zivktestorchard";
}
/// An _Incoming Viewing Key_, as described in [protocol specification
/// §4.2.2][ps].
/// §4.2.3][ps].
///
/// Used to decrypt incoming notes without spending them.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct IncomingViewingKey {
network: Network,
scalar: Scalar,
scalar: pallas::Scalar,
}
// TODO: impl a From that accepts a Network?
@ -446,38 +416,24 @@ impl fmt::Display for IncomingViewingKey {
impl From<[u8; 32]> for IncomingViewingKey {
/// Generate an _IncomingViewingKey_ from existing bytes.
fn from(mut bytes: [u8; 32]) -> Self {
// Drop the most significant five bits, so it can be interpreted
// as a scalar.
//
// I don't want to put this inside crh_ivk, but does it belong
// inside Scalar/Fr::from_bytes()? That seems the better
// place...
//
// https://github.com/zcash/librustzcash/blob/master/zcash_primitives/src/primitives.rs#L86
bytes[31] &= 0b0000_0111;
Self {
// TODO: handle setting the Network better.
network: Network::default(),
scalar: Scalar::from_bytes(&bytes).unwrap(),
scalar: pallas::Scalar::from_bytes(&bytes).unwrap(),
}
}
}
impl From<(AuthorizingKey, NullifierDerivingKey)> for IncomingViewingKey {
impl From<(SpendValidatingKey, NullifierDerivingKey)> for IncomingViewingKey {
/// For this invocation of Blake2s-256 as _CRH^ivk_.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
/// https://zips.z.cash/protocol/protocol.pdf#jubjub
// TODO: return None if ivk = 0
//
// "If ivk = 0, discard this key and start over with a new
// [spending key]." - [§4.2.2][ps]
//
// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
fn from((ask, nk): (AuthorizingKey, NullifierDerivingKey)) -> Self {
let hash_bytes = crh_ivk(ask.into(), nk.into());
fn from((ask, nk): (SpendValidatingKey, NullifierDerivingKey)) -> Self {
unimplemented!();
let hash_bytes = commit_ivk(ask.into(), nk.into());
IncomingViewingKey::from(hash_bytes)
}
@ -499,7 +455,7 @@ impl FromStr for IncomingViewingKey {
ivk_hrp::MAINNET => Network::Mainnet,
_ => Network::Testnet,
},
scalar: Scalar::from_bytes(&scalar_bytes).unwrap(),
scalar: pallas::Scalar::from_bytes(&scalar_bytes).unwrap(),
})
}
_ => Err(SerializationError::Parse("bech32 decoding error")),
@ -513,12 +469,12 @@ impl PartialEq<[u8; 32]> for IncomingViewingKey {
}
}
/// A _Diversifier_, as described in [protocol specification §4.2.2][ps].
/// A _Diversifier_, as described in [protocol specification §4.2.3][ps].
///
/// Combined with an _IncomingViewingKey_, produces a _diversified
/// payment address_.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
@ -546,36 +502,31 @@ impl From<Diversifier> for [u8; 11] {
}
}
impl TryFrom<Diversifier> for jubjub::AffinePoint {
impl TryFrom<Diversifier> for pallas::Affine {
type Error = &'static str;
/// Get a diversified base point from a diversifier value in affine
/// representation.
fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
if let Ok(extended_point) = pallas::Point::try_from(d) {
Ok(extended_point.into())
if let Ok(projective_point) = pallas::Point::try_from(d) {
Ok(projective_point.into())
} else {
Err("Invalid Diversifier -> jubjub::AffinePoint")
Err("Invalid Diversifier -> pallas::Affine")
}
}
}
impl TryFrom<Diversifier> for pallas::Point {
type Error = &'static str;
fn try_from(d: Diversifier) -> Result<Self, Self::Error> {
let possible_point = diversify_hash(d.0);
if let Some(point) = possible_point {
Ok(point)
} else {
Err("Invalid Diversifier -> pallas::Point")
}
impl From<Diversifier> for pallas::Point {
/// g_d := DiversifyHash^Orchard(d)
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
fn from(d: Diversifier) -> Self {
diversify_hash(d.0)
}
}
impl From<SpendingKey> for Diversifier {
/// Derives a [_default diversifier_][4.2.2] from a SpendingKey.
/// Derives a [_default diversifier_][4.2.3] from a `SpendingKey`.
///
/// 'For each spending key, there is also a default diversified
/// payment address with a “random-looking” diversifier. This
@ -584,22 +535,13 @@ impl From<SpendingKey> for Diversifier {
/// that cannot be distinguished (without knowledge of the
/// spending key) from one with a random diversifier...'
///
/// [4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// Derived as specied in [ZIP-32].
///
/// [4.2.3]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// [ZIP-32]: https://zips.z.cash/zip-0032#orchard-diversifier-derivation
fn from(sk: SpendingKey) -> Diversifier {
let mut i = 0u8;
loop {
let mut d_bytes = [0u8; 11];
d_bytes[..].copy_from_slice(&prf_expand(sk.bytes, &[3, i])[..11]);
if diversify_hash(d_bytes).is_some() {
break Self(d_bytes);
}
assert!(i < 255);
i += 1;
}
// Needs FF1-AES permutation
unimplemented!()
}
}
@ -610,47 +552,39 @@ impl PartialEq<[u8; 11]> for Diversifier {
}
impl Diversifier {
/// Generate a new _Diversifier_ that has already been confirmed
/// as a preimage to a valid diversified base point when used to
/// derive a diversified payment address.
/// Generate a new `Diversifier`.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concretediversifyhash
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
pub fn new<T>(csprng: &mut T) -> Self
where
T: RngCore + CryptoRng,
{
loop {
let mut bytes = [0u8; 11];
csprng.fill_bytes(&mut bytes);
let mut bytes = [0u8; 11];
csprng.fill_bytes(&mut bytes);
if diversify_hash(bytes).is_some() {
break Self(bytes);
}
}
Self::from(bytes)
}
}
/// A (diversified) _TransmissionKey_
/// A (diversified) transmission Key
///
/// In Sapling, secrets need to be transmitted to a recipient of funds
/// in order for them to be later spent. To transmit these secrets
/// securely to a recipient without requiring an out-of-band
/// communication channel, the diversified transmission key is used to
/// encrypt them.
/// In Orchard, secrets need to be transmitted to a recipient of funds in order
/// for them to be later spent. To transmit these secrets securely to a
/// recipient without requiring an out-of-band communication channel, the
/// transmission key is used to encrypt them.
///
/// Derived by multiplying a JubJub point [derived][ps] from a
/// _Diversifier_ by the _IncomingViewingKey_ scalar.
/// Derived by multiplying a Pallas point [derived][ps] from a `Diversifier` by
/// the `IncomingViewingKey` scalar.
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#concretediversifyhash
#[derive(Copy, Clone, PartialEq)]
pub struct TransmissionKey(pub jubjub::AffinePoint);
pub struct TransmissionKey(pub pallas::Affine);
impl fmt::Debug for TransmissionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TransmissionKey")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.field("x", &hex::encode(self.0.get_x().to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes()))
.finish()
}
}
@ -658,13 +592,12 @@ impl fmt::Debug for TransmissionKey {
impl Eq for TransmissionKey {}
impl From<[u8; 32]> for TransmissionKey {
/// Attempts to interpret a byte representation of an
/// affine point, failing if the element is not on
/// the curve or non-canonical.
/// Attempts to interpret a byte representation of an affine point, failing
/// if the element is not on the curve or non-canonical.
///
/// https://github.com/zkcrypto/jubjub/blob/master/src/lib.rs#L411
fn from(bytes: [u8; 32]) -> Self {
Self(jubjub::AffinePoint::from_bytes(bytes).unwrap())
Self(pallas::Affine::from_bytes(bytes).unwrap())
}
}
@ -675,15 +608,13 @@ impl From<TransmissionKey> for [u8; 32] {
}
impl From<(IncomingViewingKey, Diversifier)> for TransmissionKey {
/// This includes _KA^Sapling.DerivePublic(ivk, G_d)_, which is just a
/// This includes _KA^Orchard.DerivePublic(ivk, G_d)_, which is just a
/// scalar mult _\[ivk\]G_d_.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement
fn from((ivk, d): (IncomingViewingKey, Diversifier)) -> Self {
Self(jubjub::AffinePoint::from(
diversify_hash(d.0).unwrap() * ivk.scalar,
))
Self(pallas::Affine::from(ivk.scalar * pallas::Point::from(d)))
}
}
@ -693,12 +624,13 @@ impl PartialEq<[u8; 32]> for TransmissionKey {
}
}
/// Magic human-readable strings used to identify what networks
/// Sapling FullViewingKeys are associated with when encoded/decoded
/// with bech32.
/// Magic human-readable strings used to identify what networks Orchard full
/// viewing keys are associated with when encoded/decoded with bech32.
///
/// https://zips.z.cash/protocol/nu5.pdf#orchardfullviewingkeyencoding
mod fvk_hrp {
pub const MAINNET: &str = "zviews";
pub const TESTNET: &str = "zviewtestsapling";
pub const MAINNET: &str = "zviewo";
pub const TESTNET: &str = "zviewtestorchard";
}
/// Full Viewing Keys
@ -707,16 +639,16 @@ mod fvk_hrp {
/// spend authority.
///
/// For incoming viewing keys on the production network, the
/// Human-Readable Part is “zviews”. For incoming viewing keys on the
/// test network, the Human-Readable Part is “zviewtestsapling”.
/// Human-Readable Part is “zviewo”. For incoming viewing keys on the
/// test network, the Human-Readable Part is “zviewtestorchard”.
///
/// https://zips.z.cash/protocol/protocol.pdf#saplingfullviewingkeyencoding
/// https://zips.z.cash/protocol/protocol.pdf#orchardfullviewingkeyencoding
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct FullViewingKey {
network: Network,
authorizing_key: AuthorizingKey,
spend_validating_key: SpendValidatingKey,
nullifier_deriving_key: NullifierDerivingKey,
outgoing_viewing_key: OutgoingViewingKey,
ivk_commit_randomness: IvkCommitRandomness,
}
// TODO: impl a From that accepts a Network?
@ -725,9 +657,9 @@ impl fmt::Debug for FullViewingKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("FullViewingKey")
.field("network", &self.network)
.field("authorizing_key", &self.authorizing_key)
.field("spend_validating_key", &self.spend_validating_key)
.field("nullifier_deriving_key", &self.nullifier_deriving_key)
.field("outgoing_viewing_key", &self.outgoing_viewing_key)
.field("ivk_commit_randomness", &self.ivk_commit_randomness)
.finish()
}
}
@ -736,9 +668,9 @@ impl fmt::Display for FullViewingKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut bytes = io::Cursor::new(Vec::new());
let _ = bytes.write_all(&<[u8; 32]>::from(self.authorizing_key));
let _ = bytes.write_all(&<[u8; 32]>::from(self.spend_validating_key));
let _ = bytes.write_all(&<[u8; 32]>::from(self.nullifier_deriving_key));
let _ = bytes.write_all(&<[u8; 32]>::from(self.outgoing_viewing_key));
let _ = bytes.write_all(&<[u8; 32]>::from(self.ivk_commit_randomness));
let hrp = match self.network {
Network::Mainnet => fvk_hrp::MAINNET,
@ -757,20 +689,18 @@ impl FromStr for FullViewingKey {
Ok((hrp, bytes, Variant::Bech32)) => {
let mut decoded_bytes = io::Cursor::new(Vec::<u8>::from_base32(&bytes).unwrap());
let authorizing_key_bytes = decoded_bytes.read_32_bytes()?;
let nullifier_deriving_key_bytes = decoded_bytes.read_32_bytes()?;
let outgoing_key_bytes = decoded_bytes.read_32_bytes()?;
let ak_bytes = decoded_bytes.read_32_bytes()?;
let nk_bytes = decoded_bytes.read_32_bytes()?;
let rivk_bytes = decoded_bytes.read_32_bytes()?;
Ok(FullViewingKey {
network: match hrp.as_str() {
fvk_hrp::MAINNET => Network::Mainnet,
_ => Network::Testnet,
},
authorizing_key: AuthorizingKey::from(authorizing_key_bytes),
nullifier_deriving_key: NullifierDerivingKey::from(
nullifier_deriving_key_bytes,
),
outgoing_viewing_key: OutgoingViewingKey::from(outgoing_key_bytes),
spend_validating_key: SpendValidatingKey::from(ak_bytes),
nullifier_deriving_key: NullifierDerivingKey::from(nk_bytes),
ivk_commit_randomness: IvkCommitRandomness::from(rivk_bytes),
})
}
_ => Err(SerializationError::Parse("bech32 decoding error")),
@ -778,19 +708,20 @@ impl FromStr for FullViewingKey {
}
}
/// An ephemeral public key for Sapling key agreement.
#[derive(Copy, Clone, PartialEq)]
pub struct DiversifierKey();
/// An ephemeral public key for Orchard key agreement.
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesaplingkeyagreement
/// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement
#[derive(Copy, Clone, Deserialize, PartialEq, Serialize)]
pub struct EphemeralPublicKey(
#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint,
);
pub struct EphemeralPublicKey(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
impl fmt::Debug for EphemeralPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EphemeralPublicKey")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.field("x", &hex::encode(self.0.get_x().to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes()))
.finish()
}
}
@ -813,12 +744,12 @@ impl TryFrom<[u8; 32]> for EphemeralPublicKey {
type Error = &'static str;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let possible_point = jubjub::AffinePoint::from_bytes(bytes);
let possible_point = pallas::Affine::from_bytes(bytes);
if possible_point.is_some().into() {
Ok(Self(possible_point.unwrap()))
} else {
Err("Invalid jubjub::AffinePoint value")
Err("Invalid pallas::Affine value")
}
}
}

View File

@ -1,641 +0,0 @@
// Generated from https://github.com/zcash-hackworks/zcash-test-vectors/blob/07dc43fd90cd78a0b45b2eb5d2be3ce3c1841603/sapling_key_components.py
pub struct TestVector {
pub sk: [u8; 32],
pub ask: [u8; 32],
pub nsk: [u8; 32],
pub ovk: [u8; 32],
pub ak: [u8; 32],
pub nk: [u8; 32],
pub ivk: [u8; 32],
pub default_d: [u8; 11],
pub default_pk_d: [u8; 32],
pub note_v: u64,
pub note_r: [u8; 32],
pub note_cmu: [u8; 32],
pub note_pos: u64,
pub note_nf: [u8; 32],
}
pub const TEST_VECTORS: [TestVector; 10] = [
TestVector {
sk: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
],
ask: [
0x85, 0x48, 0xa1, 0x4a, 0x47, 0x3e, 0xa5, 0x47, 0xaa, 0x23, 0x78, 0x40, 0x20, 0x44,
0xf8, 0x18, 0xcf, 0x19, 0x11, 0xcf, 0x5d, 0xd2, 0x05, 0x4f, 0x67, 0x83, 0x45, 0xf0,
0x0d, 0x0e, 0x88, 0x06,
],
nsk: [
0x30, 0x11, 0x4e, 0xa0, 0xdd, 0x0b, 0xb6, 0x1c, 0xf0, 0xea, 0xea, 0xb6, 0xec, 0x33,
0x31, 0xf5, 0x81, 0xb0, 0x42, 0x5e, 0x27, 0x33, 0x85, 0x01, 0x26, 0x2d, 0x7e, 0xac,
0x74, 0x5e, 0x6e, 0x05,
],
ovk: [
0x98, 0xd1, 0x69, 0x13, 0xd9, 0x9b, 0x04, 0x17, 0x7c, 0xab, 0xa4, 0x4f, 0x6e, 0x4d,
0x22, 0x4e, 0x03, 0xb5, 0xac, 0x03, 0x1d, 0x7c, 0xe4, 0x5e, 0x86, 0x51, 0x38, 0xe1,
0xb9, 0x96, 0xd6, 0x3b,
],
ak: [
0xf3, 0x44, 0xec, 0x38, 0x0f, 0xe1, 0x27, 0x3e, 0x30, 0x98, 0xc2, 0x58, 0x8c, 0x5d,
0x3a, 0x79, 0x1f, 0xd7, 0xba, 0x95, 0x80, 0x32, 0x76, 0x07, 0x77, 0xfd, 0x0e, 0xfa,
0x8e, 0xf1, 0x16, 0x20,
],
nk: [
0xf7, 0xcf, 0x9e, 0x77, 0xf2, 0xe5, 0x86, 0x83, 0x38, 0x3c, 0x15, 0x19, 0xac, 0x7b,
0x06, 0x2d, 0x30, 0x04, 0x0e, 0x27, 0xa7, 0x25, 0xfb, 0x88, 0xfb, 0x19, 0xa9, 0x78,
0xbd, 0x3f, 0xd6, 0xba,
],
ivk: [
0xb7, 0x0b, 0x7c, 0xd0, 0xed, 0x03, 0xcb, 0xdf, 0xd7, 0xad, 0xa9, 0x50, 0x2e, 0xe2,
0x45, 0xb1, 0x3e, 0x56, 0x9d, 0x54, 0xa5, 0x71, 0x9d, 0x2d, 0xaa, 0x0f, 0x5f, 0x14,
0x51, 0x47, 0x92, 0x04,
],
default_d: [
0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39,
],
default_pk_d: [
0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67,
0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8,
0x41, 0xae, 0x74, 0x15,
],
note_v: 0,
note_r: [
0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89,
0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
],
note_cmu: [
0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0,
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
0xdd, 0x07, 0x64, 0x39,
],
note_pos: 0,
note_nf: [
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
0x47, 0xab, 0x40, 0x63,
],
},
TestVector {
sk: [
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01,
],
ask: [
0xc9, 0x43, 0x56, 0x29, 0xbf, 0x8b, 0xff, 0xe5, 0x5e, 0x73, 0x35, 0xec, 0x07, 0x77,
0x18, 0xba, 0x60, 0xba, 0x28, 0xd7, 0xac, 0x37, 0x94, 0xb7, 0x4f, 0x51, 0x2c, 0x31,
0xaf, 0x0a, 0x53, 0x04,
],
nsk: [
0x11, 0xac, 0xc2, 0xea, 0xd0, 0x7b, 0x5f, 0x00, 0x8c, 0x1f, 0x0f, 0x09, 0x0c, 0xc8,
0xdd, 0xf3, 0x35, 0x23, 0x6f, 0xf4, 0xb2, 0x53, 0xc6, 0x49, 0x56, 0x95, 0xe9, 0xd6,
0x39, 0xda, 0xcd, 0x08,
],
ovk: [
0x3b, 0x94, 0x62, 0x10, 0xce, 0x6d, 0x1b, 0x16, 0x92, 0xd7, 0x39, 0x2a, 0xc8, 0x4a,
0x8b, 0xc8, 0xf0, 0x3b, 0x72, 0x72, 0x3c, 0x7d, 0x36, 0x72, 0x1b, 0x80, 0x9a, 0x79,
0xc9, 0xd6, 0xe4, 0x5b,
],
ak: [
0x82, 0xff, 0x5e, 0xff, 0xc5, 0x27, 0xae, 0x84, 0x02, 0x0b, 0xf2, 0xd3, 0x52, 0x01,
0xc1, 0x02, 0x19, 0x13, 0x19, 0x47, 0xff, 0x4b, 0x96, 0xf8, 0x81, 0xa4, 0x5f, 0x2e,
0x8a, 0xe3, 0x05, 0x18,
],
nk: [
0xc4, 0x53, 0x4d, 0x84, 0x8b, 0xb9, 0x18, 0xcf, 0x4a, 0x7f, 0x8b, 0x98, 0x74, 0x0a,
0xb3, 0xcc, 0xee, 0x58, 0x67, 0x95, 0xff, 0x4d, 0xf6, 0x45, 0x47, 0xa8, 0x88, 0x8a,
0x6c, 0x74, 0x15, 0xd2,
],
ivk: [
0xc5, 0x18, 0x38, 0x44, 0x66, 0xb2, 0x69, 0x88, 0xb5, 0x10, 0x90, 0x67, 0x41, 0x8d,
0x19, 0x2d, 0x9d, 0x6b, 0xd0, 0xd9, 0x23, 0x22, 0x05, 0xd7, 0x74, 0x18, 0xc2, 0x40,
0xfc, 0x68, 0xa4, 0x06,
],
default_d: [
0xae, 0xf1, 0x80, 0xf6, 0xe3, 0x4e, 0x35, 0x4b, 0x88, 0x8f, 0x81,
],
default_pk_d: [
0xa6, 0xb1, 0x3e, 0xa3, 0x36, 0xdd, 0xb7, 0xa6, 0x7b, 0xb0, 0x9a, 0x0e, 0x68, 0xe9,
0xd3, 0xcf, 0xb3, 0x92, 0x10, 0x83, 0x1e, 0xa3, 0xa2, 0x96, 0xba, 0x09, 0xa9, 0x22,
0x06, 0x0f, 0xd3, 0x8b,
],
note_v: 12_227_227_834_928_555_328,
note_r: [
0x47, 0x8b, 0xa0, 0xee, 0x6e, 0x1a, 0x75, 0xb6, 0x00, 0x03, 0x6f, 0x26, 0xf1, 0x8b,
0x70, 0x15, 0xab, 0x55, 0x6b, 0xed, 0xdf, 0x8b, 0x96, 0x02, 0x38, 0x86, 0x9f, 0x89,
0xdd, 0x80, 0x4e, 0x06,
],
note_cmu: [
0xb5, 0x78, 0x93, 0x50, 0x0b, 0xfb, 0x85, 0xdf, 0x2e, 0x8b, 0x01, 0xac, 0x45, 0x2f,
0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72,
0xca, 0xd4, 0x69, 0x50,
],
note_pos: 763_714_296,
note_nf: [
0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2,
0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17,
0x4c, 0x2e, 0x9d, 0x93,
],
},
TestVector {
sk: [
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02,
],
ask: [
0xee, 0x1c, 0x3d, 0x7e, 0xfe, 0x0a, 0x78, 0x06, 0x3d, 0x6a, 0xf3, 0xd9, 0xd8, 0x12,
0x12, 0xaf, 0x47, 0xb7, 0xc1, 0xb7, 0x61, 0xf8, 0x5c, 0xcb, 0x06, 0x6f, 0xc1, 0x1a,
0x6a, 0x42, 0x17, 0x03,
],
nsk: [
0x1d, 0x3b, 0x71, 0x37, 0x55, 0xd7, 0x48, 0x75, 0xe8, 0xea, 0x38, 0xfd, 0x16, 0x6e,
0x76, 0xc6, 0x2a, 0x42, 0x50, 0x21, 0x6e, 0x6b, 0xbf, 0xe4, 0x8a, 0x5e, 0x2e, 0xab,
0xad, 0x11, 0x7f, 0x0b,
],
ovk: [
0x8b, 0xf4, 0x39, 0x0e, 0x28, 0xdd, 0xc9, 0x5b, 0x83, 0x02, 0xc3, 0x81, 0xd5, 0x81,
0x0b, 0x84, 0xba, 0x8e, 0x60, 0x96, 0xe5, 0xa7, 0x68, 0x22, 0x77, 0x4f, 0xd4, 0x9f,
0x49, 0x1e, 0x8f, 0x49,
],
ak: [
0xab, 0x83, 0x57, 0x4e, 0xb5, 0xde, 0x85, 0x9a, 0x0a, 0xb8, 0x62, 0x9d, 0xec, 0x34,
0xc7, 0xbe, 0xe8, 0xc3, 0xfc, 0x74, 0xdf, 0xa0, 0xb1, 0x9a, 0x3a, 0x74, 0x68, 0xd1,
0x5d, 0xca, 0x64, 0xc6,
],
nk: [
0x95, 0xd5, 0x80, 0x53, 0xe0, 0x59, 0x2e, 0x4a, 0x16, 0x9c, 0xc0, 0xb7, 0x92, 0x8a,
0xaa, 0xc3, 0xde, 0x24, 0xef, 0x15, 0x31, 0xaa, 0x9e, 0xb6, 0xf4, 0xab, 0x93, 0x91,
0x4d, 0xa8, 0xa0, 0x6e,
],
ivk: [
0x47, 0x1c, 0x24, 0xa3, 0xdc, 0x87, 0x30, 0xe7, 0x50, 0x36, 0xc0, 0xa9, 0x5f, 0x3e,
0x2f, 0x7d, 0xd1, 0xbe, 0x6f, 0xb9, 0x3a, 0xd2, 0x95, 0x92, 0x20, 0x3d, 0xef, 0x30,
0x41, 0x95, 0x45, 0x05,
],
default_d: [
0x75, 0x99, 0xf0, 0xbf, 0x9b, 0x57, 0xcd, 0x2d, 0xc2, 0x99, 0xb6,
],
default_pk_d: [
0x66, 0x14, 0x17, 0x39, 0x51, 0x4b, 0x28, 0xf0, 0x5d, 0xef, 0x8a, 0x18, 0xee, 0xee,
0x5e, 0xed, 0x4d, 0x44, 0xc6, 0x22, 0x5c, 0x3c, 0x65, 0xd8, 0x8d, 0xd9, 0x90, 0x77,
0x08, 0x01, 0x2f, 0x5a,
],
note_v: 6_007_711_596_147_559_040,
note_r: [
0x14, 0x7c, 0xf2, 0xb5, 0x1b, 0x4c, 0x7c, 0x63, 0xcb, 0x77, 0xb9, 0x9e, 0x8b, 0x78,
0x3e, 0x5b, 0x51, 0x11, 0xdb, 0x0a, 0x7c, 0xa0, 0x4d, 0x6c, 0x01, 0x4a, 0x1d, 0x7d,
0xa8, 0x3b, 0xae, 0x0a,
],
note_cmu: [
0xdb, 0x85, 0xa7, 0x0a, 0x98, 0x43, 0x7f, 0x73, 0x16, 0x7f, 0xc3, 0x32, 0xd5, 0xb7,
0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f,
0x4e, 0x55, 0xf1, 0x51,
],
note_pos: 1_527_428_592,
note_nf: [
0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f,
0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f,
0x59, 0xae, 0x1d, 0x14,
],
},
TestVector {
sk: [
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03,
],
ask: [
0x00, 0xc3, 0xa1, 0xe1, 0xca, 0x8f, 0x4e, 0x04, 0x80, 0xee, 0x1e, 0xe9, 0x0c, 0xa7,
0x51, 0x78, 0x79, 0xd3, 0xfc, 0x5c, 0x81, 0x5c, 0x09, 0x03, 0xe5, 0xee, 0xbc, 0x94,
0xbb, 0x80, 0x95, 0x03,
],
nsk: [
0xe6, 0x62, 0x85, 0xa5, 0xe9, 0xb6, 0x5e, 0x15, 0x7a, 0xd2, 0xfc, 0xd5, 0x43, 0xda,
0xd9, 0x8c, 0x67, 0xa5, 0x8a, 0xbd, 0xf2, 0x87, 0xe0, 0x55, 0x06, 0xbd, 0x1c, 0x2e,
0x59, 0xb0, 0x72, 0x0b,
],
ovk: [
0x14, 0x76, 0x78, 0xe0, 0x55, 0x3b, 0x97, 0x82, 0x93, 0x47, 0x64, 0x7c, 0x5b, 0xc7,
0xda, 0xb4, 0xcc, 0x22, 0x02, 0xb5, 0x4e, 0xc2, 0x9f, 0xd3, 0x1a, 0x3d, 0xe6, 0xbe,
0x08, 0x25, 0xfc, 0x5e,
],
ak: [
0x3c, 0x9c, 0xde, 0x7e, 0x5d, 0x0d, 0x38, 0xa8, 0x61, 0x0f, 0xaa, 0xdb, 0xcf, 0x4c,
0x34, 0x3f, 0x5d, 0x3c, 0xfa, 0x31, 0x55, 0xa5, 0xb9, 0x46, 0x61, 0xa6, 0x75, 0x3e,
0x96, 0xe8, 0x84, 0xea,
],
nk: [
0xb7, 0x7d, 0x36, 0xf5, 0x08, 0x94, 0x1d, 0xbd, 0x61, 0xcf, 0xd0, 0xf1, 0x59, 0xee,
0x05, 0xcf, 0xaa, 0x78, 0xa2, 0x6c, 0x94, 0x92, 0x90, 0x38, 0x06, 0xd8, 0x3b, 0x59,
0x8d, 0x3c, 0x1c, 0x2a,
],
ivk: [
0x63, 0x6a, 0xa9, 0x64, 0xbf, 0xc2, 0x3c, 0xe4, 0xb1, 0xfc, 0xf7, 0xdf, 0xc9, 0x91,
0x79, 0xdd, 0xc4, 0x06, 0xff, 0x55, 0x40, 0x0c, 0x92, 0x95, 0xac, 0xfc, 0x14, 0xf0,
0x31, 0xc7, 0x26, 0x00,
],
default_d: [
0x1b, 0x81, 0x61, 0x4f, 0x1d, 0xad, 0xea, 0x0f, 0x8d, 0x0a, 0x58,
],
default_pk_d: [
0x25, 0xeb, 0x55, 0xfc, 0xcf, 0x76, 0x1f, 0xc6, 0x4e, 0x85, 0xa5, 0x88, 0xef, 0xe6,
0xea, 0xd7, 0x83, 0x2f, 0xb1, 0xf0, 0xf7, 0xa8, 0x31, 0x65, 0x89, 0x5b, 0xdf, 0xf9,
0x42, 0x92, 0x5f, 0x5c,
],
note_v: 18_234_939_431_076_114_368,
note_r: [
0x34, 0xa4, 0xb2, 0xa9, 0x14, 0x4f, 0xf5, 0xea, 0x54, 0xef, 0xee, 0x87, 0xcf, 0x90,
0x1b, 0x5b, 0xed, 0x5e, 0x35, 0xd2, 0x1f, 0xbb, 0xd7, 0x88, 0xd5, 0xbd, 0x9d, 0x83,
0x3e, 0x11, 0x28, 0x04,
],
note_cmu: [
0xe0, 0x8c, 0xe4, 0x82, 0xb3, 0xa8, 0xfb, 0x3b, 0x35, 0xcc, 0xdb, 0xe3, 0x43, 0x37,
0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa,
0x60, 0xd1, 0x9b, 0x6c,
],
note_pos: 2_291_142_888,
note_nf: [
0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56,
0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75,
0x8b, 0xec, 0x47, 0xa1,
],
},
TestVector {
sk: [
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04,
],
ask: [
0x82, 0x36, 0xd1, 0x9d, 0x32, 0x05, 0xd8, 0x55, 0x43, 0xa0, 0x68, 0x11, 0x34, 0x3f,
0x82, 0x7b, 0x65, 0x63, 0x77, 0x0a, 0x49, 0xaa, 0x4d, 0x0c, 0xa0, 0x08, 0x18, 0x05,
0xd4, 0xc8, 0xea, 0x0d,
],
nsk: [
0x7e, 0xc1, 0xef, 0x0b, 0xed, 0x82, 0x71, 0x82, 0x72, 0xf0, 0xf4, 0x4f, 0x01, 0x7c,
0x48, 0x41, 0x74, 0x51, 0x3d, 0x66, 0x1d, 0xd1, 0x68, 0xaf, 0x02, 0xd2, 0x09, 0x2a,
0x1d, 0x8a, 0x05, 0x07,
],
ovk: [
0x1b, 0x6e, 0x75, 0xec, 0xe3, 0xac, 0xe8, 0xdb, 0xa6, 0xa5, 0x41, 0x0d, 0x9a, 0xd4,
0x75, 0x56, 0x68, 0xe4, 0xb3, 0x95, 0x85, 0xd6, 0x35, 0xec, 0x1d, 0xa7, 0xc8, 0xdc,
0xfd, 0x5f, 0xc4, 0xed,
],
ak: [
0x55, 0xe8, 0x83, 0x89, 0xbb, 0x7e, 0x41, 0xde, 0x13, 0x0c, 0xfa, 0x51, 0xa8, 0x71,
0x5f, 0xde, 0x01, 0xff, 0x9c, 0x68, 0x76, 0x64, 0x7f, 0x01, 0x75, 0xad, 0x34, 0xf0,
0x58, 0xdd, 0xe0, 0x1a,
],
nk: [
0x72, 0x5d, 0x4a, 0xd6, 0xa1, 0x50, 0x21, 0xcd, 0x1c, 0x48, 0xc5, 0xee, 0x19, 0xde,
0x6c, 0x1e, 0x76, 0x8a, 0x2c, 0xc0, 0xa9, 0xa7, 0x30, 0xa0, 0x1b, 0xb2, 0x1c, 0x95,
0xe3, 0xd9, 0xe4, 0x3c,
],
ivk: [
0x67, 0xfa, 0x2b, 0xf7, 0xc6, 0x7d, 0x46, 0x58, 0x24, 0x3c, 0x31, 0x7c, 0x0c, 0xb4,
0x1f, 0xd3, 0x20, 0x64, 0xdf, 0xd3, 0x70, 0x9f, 0xe0, 0xdc, 0xb7, 0x24, 0xf1, 0x4b,
0xb0, 0x1a, 0x1d, 0x04,
],
default_d: [
0xfc, 0xfb, 0x68, 0xa4, 0x0d, 0x4b, 0xc6, 0xa0, 0x4b, 0x09, 0xc4,
],
default_pk_d: [
0x8b, 0x2a, 0x33, 0x7f, 0x03, 0x62, 0x2c, 0x24, 0xff, 0x38, 0x1d, 0x4c, 0x54, 0x6f,
0x69, 0x77, 0xf9, 0x05, 0x22, 0xe9, 0x2f, 0xde, 0x44, 0xc9, 0xd1, 0xbb, 0x09, 0x97,
0x14, 0xb9, 0xdb, 0x2b,
],
note_v: 12_015_423_192_295_118_080,
note_r: [
0xe5, 0x57, 0x85, 0x13, 0x55, 0x74, 0x7c, 0x09, 0xac, 0x59, 0x01, 0x3c, 0xbd, 0xe8,
0x59, 0x80, 0x96, 0x4e, 0xc1, 0x84, 0x4d, 0x9c, 0x69, 0x67, 0xca, 0x0c, 0x02, 0x9c,
0x84, 0x57, 0xbb, 0x04,
],
note_cmu: [
0xbd, 0xc8, 0x54, 0xbf, 0x3e, 0x7b, 0x00, 0x82, 0x1f, 0x3b, 0x8b, 0x85, 0x23, 0x8c,
0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b,
0xc7, 0x1b, 0x7f, 0x36,
],
note_pos: 3_054_857_184,
note_nf: [
0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f,
0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03,
0x01, 0xbf, 0x3d, 0x13,
],
},
TestVector {
sk: [
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05,
],
ask: [
0xea, 0xe6, 0x88, 0x4d, 0x76, 0x4a, 0x05, 0x40, 0x61, 0xa8, 0xf1, 0xc0, 0x07, 0x6c,
0x62, 0x4d, 0xcb, 0x73, 0x87, 0x89, 0xf7, 0xad, 0x1e, 0x74, 0x08, 0xe3, 0x1f, 0x24,
0xdf, 0xc8, 0x26, 0x07,
],
nsk: [
0xfb, 0xe6, 0x10, 0xf4, 0x2a, 0x41, 0x74, 0x9f, 0x9b, 0x6e, 0x6e, 0x4a, 0x54, 0xb5,
0xa3, 0x2e, 0xbf, 0xe8, 0xf4, 0x38, 0x00, 0x88, 0x1b, 0xa6, 0xcd, 0x13, 0xed, 0x0b,
0x05, 0x29, 0x46, 0x01,
],
ovk: [
0xc6, 0xbc, 0x1f, 0x39, 0xf0, 0xd7, 0x86, 0x31, 0x4c, 0xb2, 0x0b, 0xf9, 0xab, 0x22,
0x85, 0x40, 0x91, 0x35, 0x55, 0xf9, 0x70, 0x69, 0x6b, 0x6d, 0x7c, 0x77, 0xbb, 0x33,
0x23, 0x28, 0x37, 0x2a,
],
ak: [
0xe6, 0x82, 0x76, 0x59, 0x14, 0xe3, 0x86, 0x4c, 0x33, 0x9e, 0x57, 0x82, 0xb8, 0x55,
0xc0, 0xfd, 0xf4, 0x0e, 0x0d, 0xfc, 0xed, 0xb9, 0xe7, 0xb4, 0x7b, 0xc9, 0x4b, 0x90,
0xb3, 0xa4, 0xc9, 0x88,
],
nk: [
0x82, 0x25, 0x6b, 0x95, 0x62, 0x3c, 0x67, 0x02, 0x4b, 0x44, 0x24, 0xd9, 0x14, 0x00,
0xa3, 0x70, 0xe7, 0xac, 0x8e, 0x4d, 0x15, 0x48, 0x2a, 0x37, 0x59, 0xe0, 0x0d, 0x21,
0x97, 0x49, 0xda, 0xee,
],
ivk: [
0xea, 0x3f, 0x1d, 0x80, 0xe4, 0x30, 0x7c, 0xa7, 0x3b, 0x9f, 0x37, 0x80, 0x1f, 0x91,
0xfb, 0xa8, 0x10, 0xcc, 0x41, 0xd2, 0x79, 0xfc, 0x29, 0xf5, 0x64, 0x23, 0x56, 0x54,
0xa2, 0x17, 0x8e, 0x03,
],
default_d: [
0xeb, 0x51, 0x98, 0x82, 0xad, 0x1e, 0x5c, 0xc6, 0x54, 0xcd, 0x59,
],
default_pk_d: [
0x6b, 0x27, 0xda, 0xcc, 0xb5, 0xa8, 0x20, 0x7f, 0x53, 0x2d, 0x10, 0xca, 0x23, 0x8f,
0x97, 0x86, 0x64, 0x8a, 0x11, 0xb5, 0x96, 0x6e, 0x51, 0xa2, 0xf7, 0xd8, 0x9e, 0x15,
0xd2, 0x9b, 0x8f, 0xdf,
],
note_v: 5_795_906_953_514_121_792,
note_r: [
0x68, 0xf0, 0x61, 0x04, 0x60, 0x6b, 0x0c, 0x54, 0x49, 0x84, 0x5f, 0xf4, 0xc6, 0x5f,
0x73, 0xe9, 0x0f, 0x45, 0xef, 0x5a, 0x43, 0xc9, 0xd7, 0x4c, 0xb2, 0xc8, 0x5c, 0xf5,
0x6c, 0x94, 0xc0, 0x02,
],
note_cmu: [
0xe8, 0x26, 0x7d, 0x30, 0xac, 0x11, 0xc1, 0x00, 0xbc, 0x7a, 0x0f, 0xdf, 0x91, 0xf7,
0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69,
0xde, 0x1a, 0x5b, 0x4c,
],
note_pos: 3_818_571_480,
note_nf: [
0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2,
0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20,
0xb6, 0xc9, 0xa7, 0xe1,
],
},
TestVector {
sk: [
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06,
],
ask: [
0xe8, 0xf8, 0x16, 0xb4, 0xbc, 0x08, 0xa7, 0xe5, 0x66, 0x75, 0x0c, 0xc2, 0x8a, 0xfe,
0x82, 0xa4, 0xce, 0xa9, 0xc2, 0xbe, 0xf2, 0x44, 0xfa, 0x4b, 0x13, 0xc4, 0x73, 0x9b,
0x28, 0x07, 0x4c, 0x0d,
],
nsk: [
0x32, 0x61, 0x5b, 0x13, 0x7f, 0x28, 0x01, 0xed, 0x44, 0x6e, 0x48, 0x78, 0x1a, 0xb0,
0x63, 0x45, 0x72, 0xe1, 0x8c, 0xfb, 0x06, 0x93, 0x72, 0x1b, 0x88, 0x03, 0xc0, 0x5b,
0x82, 0x27, 0xd1, 0x07,
],
ovk: [
0xf6, 0x2c, 0x05, 0xe8, 0x48, 0xa8, 0x73, 0xef, 0x88, 0x5e, 0x12, 0xb0, 0x8c, 0x5e,
0x7c, 0xa2, 0xf3, 0x24, 0x24, 0xba, 0xcc, 0x75, 0x4c, 0xb6, 0x97, 0x50, 0x44, 0x4d,
0x35, 0x5f, 0x51, 0x06,
],
ak: [
0xff, 0x27, 0xdb, 0x07, 0x51, 0x94, 0x5d, 0x3e, 0xe4, 0xbe, 0x9c, 0xf1, 0x5c, 0x2e,
0xa2, 0x11, 0xb2, 0x4b, 0x16, 0x4d, 0x5f, 0x2d, 0x7d, 0xdf, 0xf5, 0xe4, 0xa0, 0x70,
0x8f, 0x10, 0xb9, 0x5e,
],
nk: [
0x94, 0x38, 0x85, 0x95, 0x9d, 0x4e, 0xf8, 0xa9, 0xcf, 0xca, 0x07, 0xc4, 0x57, 0xf0,
0x9e, 0xc7, 0x4b, 0x96, 0xf9, 0x93, 0xd8, 0xe0, 0xfa, 0x32, 0xb1, 0x9c, 0x03, 0xe3,
0xb0, 0x7a, 0x42, 0x0f,
],
ivk: [
0xb5, 0xc5, 0x89, 0x49, 0x43, 0x95, 0x69, 0x33, 0xc0, 0xe5, 0xc1, 0x2d, 0x31, 0x1f,
0xc1, 0x2c, 0xba, 0x58, 0x35, 0x4b, 0x5c, 0x38, 0x9e, 0xdc, 0x03, 0xda, 0x55, 0x08,
0x4f, 0x74, 0xc2, 0x05,
],
default_d: [
0xbe, 0xbb, 0x0f, 0xb4, 0x6b, 0x8a, 0xaf, 0xf8, 0x90, 0x40, 0xf6,
],
default_pk_d: [
0xd1, 0x1d, 0xa0, 0x1f, 0x0b, 0x43, 0xbd, 0xd5, 0x28, 0x8d, 0x32, 0x38, 0x5b, 0x87,
0x71, 0xd2, 0x23, 0x49, 0x3c, 0x69, 0x80, 0x25, 0x44, 0x04, 0x3f, 0x77, 0xcf, 0x1d,
0x71, 0xc1, 0xcb, 0x8c,
],
note_v: 18_023_134_788_442_677_120,
note_r: [
0x49, 0xf9, 0x0b, 0x47, 0xfd, 0x52, 0xfe, 0xe7, 0xc1, 0xc8, 0x1f, 0x0d, 0xcb, 0x5b,
0x74, 0xc3, 0xfb, 0x9b, 0x3e, 0x03, 0x97, 0x6f, 0x8b, 0x75, 0x24, 0xea, 0xba, 0xd0,
0x08, 0x89, 0x21, 0x07,
],
note_cmu: [
0x57, 0x2b, 0xa2, 0x05, 0x25, 0xb0, 0xac, 0x4d, 0x6d, 0xc0, 0x1a, 0xc2, 0xea, 0x10,
0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b,
0x78, 0x3a, 0x1e, 0x55,
],
note_pos: 287_318_480,
note_nf: [
0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae,
0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4,
0x07, 0xd5, 0x1e, 0x11,
],
},
TestVector {
sk: [
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07,
],
ask: [
0x74, 0xb4, 0x4a, 0x37, 0xf1, 0x50, 0x23, 0xc0, 0x60, 0x42, 0x7e, 0x1d, 0xae, 0xa3,
0xf6, 0x43, 0x12, 0xdd, 0x8f, 0xeb, 0x7b, 0x2c, 0xed, 0xf0, 0xdd, 0x55, 0x44, 0x49,
0x3f, 0x87, 0x2c, 0x06,
],
nsk: [
0x07, 0x5c, 0x35, 0xdb, 0x8b, 0x1b, 0x25, 0x75, 0x42, 0x23, 0xec, 0xee, 0x34, 0xab,
0x73, 0x0d, 0xdd, 0xd1, 0xf1, 0x4a, 0x6a, 0x54, 0xf4, 0xc6, 0xf4, 0x68, 0x45, 0x3c,
0x3c, 0x6e, 0xd6, 0x0b,
],
ovk: [
0xe9, 0xe0, 0xdc, 0x1e, 0xd3, 0x11, 0xda, 0xed, 0x64, 0xbd, 0x74, 0xda, 0x5d, 0x94,
0xfe, 0x88, 0xa6, 0xea, 0x41, 0x4b, 0x73, 0x12, 0xde, 0x3d, 0x2a, 0x78, 0xf6, 0x46,
0x32, 0xbb, 0xe3, 0x73,
],
ak: [
0x28, 0x3f, 0x9a, 0xaf, 0xa9, 0xbc, 0xb3, 0xe6, 0xce, 0x17, 0xe6, 0x32, 0x12, 0x63,
0x4c, 0xb3, 0xee, 0x55, 0x0c, 0x47, 0x6b, 0x67, 0x6b, 0xd3, 0x56, 0xa6, 0xdf, 0x8a,
0xdf, 0x51, 0xd2, 0x5e,
],
nk: [
0xdc, 0x4c, 0x67, 0xb1, 0x0d, 0x4b, 0x0a, 0x21, 0x8d, 0xc6, 0xe1, 0x48, 0x70, 0x66,
0x74, 0x0a, 0x40, 0x93, 0x17, 0x86, 0x6c, 0x32, 0xe6, 0x64, 0xb5, 0x0e, 0x39, 0x7a,
0xa8, 0x03, 0x89, 0xd4,
],
ivk: [
0x87, 0x16, 0xc8, 0x28, 0x80, 0xe1, 0x36, 0x83, 0xe1, 0xbb, 0x05, 0x9d, 0xd0, 0x6c,
0x80, 0xc9, 0x01, 0x34, 0xa9, 0x6d, 0x5a, 0xfc, 0xa8, 0xaa, 0xc2, 0xbb, 0xf6, 0x8b,
0xb0, 0x5f, 0x84, 0x02,
],
default_d: [
0xad, 0x6e, 0x2e, 0x18, 0x5a, 0x31, 0x00, 0xe3, 0xa6, 0xa8, 0xb3,
],
default_pk_d: [
0x32, 0xcb, 0x28, 0x06, 0xb8, 0x82, 0xf1, 0x36, 0x8b, 0x0d, 0x4a, 0x89, 0x8f, 0x72,
0xc4, 0xc8, 0xf7, 0x28, 0x13, 0x2c, 0xc1, 0x24, 0x56, 0x94, 0x6e, 0x7f, 0x4c, 0xb0,
0xfb, 0x05, 0x8d, 0xa9,
],
note_v: 11_803_618_549_661_680_832,
note_r: [
0x51, 0x65, 0xaf, 0xf2, 0x2d, 0xd4, 0xed, 0x56, 0xb4, 0xd8, 0x1d, 0x1f, 0x17, 0x1c,
0xc3, 0xd6, 0x43, 0x2f, 0xed, 0x1b, 0xeb, 0xf2, 0x0a, 0x7b, 0xea, 0xb1, 0x2d, 0xb1,
0x42, 0xf9, 0x4a, 0x0c,
],
note_cmu: [
0xab, 0x7f, 0xc5, 0x66, 0x87, 0x3c, 0xcd, 0xe6, 0x71, 0xf5, 0x98, 0x27, 0x67, 0x85,
0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36,
0xf7, 0x8c, 0x2b, 0x23,
],
note_pos: 1_051_032_776,
note_nf: [
0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f,
0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc,
0x8b, 0xb6, 0x98, 0x90,
],
},
TestVector {
sk: [
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08,
],
ask: [
0x03, 0x9d, 0xd9, 0x3d, 0xf3, 0x11, 0xff, 0x8f, 0xba, 0xb3, 0xfe, 0x23, 0x02, 0x19,
0xcd, 0x42, 0xac, 0x87, 0x94, 0x84, 0xf3, 0x0b, 0x90, 0x3a, 0x3c, 0x1e, 0x67, 0xcc,
0xca, 0x5a, 0x7b, 0x0d,
],
nsk: [
0x04, 0x9f, 0xa1, 0x4f, 0x48, 0x6c, 0x75, 0xb9, 0xfa, 0xd7, 0xe3, 0xb6, 0x73, 0xa4,
0x43, 0xdd, 0x07, 0x4e, 0xaa, 0x96, 0xed, 0xcb, 0x2a, 0x53, 0xea, 0xaa, 0xbd, 0xaf,
0x70, 0xff, 0xbb, 0x08,
],
ovk: [
0x14, 0x7d, 0xd1, 0x1d, 0x77, 0xeb, 0xa1, 0xb1, 0x63, 0x6f, 0xd6, 0x19, 0x0c, 0x62,
0xb9, 0xa5, 0xd0, 0x48, 0x1b, 0xee, 0x7e, 0x91, 0x7f, 0xab, 0x02, 0xe2, 0x18, 0x58,
0x06, 0x3a, 0xb5, 0x04,
],
ak: [
0x36, 0x40, 0x48, 0xee, 0xdb, 0xe8, 0xca, 0x20, 0x5e, 0xb7, 0xe7, 0xba, 0x0a, 0x90,
0x12, 0x16, 0x6c, 0x7c, 0x7b, 0xd9, 0xeb, 0x22, 0x8e, 0x08, 0x48, 0x14, 0x48, 0xc4,
0x88, 0xaa, 0x21, 0xd2,
],
nk: [
0xed, 0x60, 0xaf, 0x1c, 0xe7, 0xdf, 0x38, 0x07, 0x0d, 0x38, 0x51, 0x43, 0x2a, 0x96,
0x48, 0x0d, 0xb0, 0xb4, 0x17, 0xc3, 0x68, 0x2a, 0x1d, 0x68, 0xe3, 0xe8, 0x93, 0x34,
0x23, 0x5c, 0x0b, 0xdf,
],
ivk: [
0x99, 0xc9, 0xb4, 0xb8, 0x4f, 0x4b, 0x4e, 0x35, 0x0f, 0x78, 0x7d, 0x1c, 0xf7, 0x05,
0x1d, 0x50, 0xec, 0xc3, 0x4b, 0x1a, 0x5b, 0x20, 0xd2, 0xd2, 0x13, 0x9b, 0x4a, 0xf1,
0xf1, 0x60, 0xe0, 0x01,
],
default_d: [
0x21, 0xc9, 0x0e, 0x1c, 0x65, 0x8b, 0x3e, 0xfe, 0x86, 0xaf, 0x58,
],
default_pk_d: [
0x9e, 0x64, 0x17, 0x4b, 0x4a, 0xb9, 0x81, 0x40, 0x5c, 0x32, 0x3b, 0x5e, 0x12, 0x47,
0x59, 0x45, 0xa4, 0x6d, 0x4f, 0xed, 0xf8, 0x06, 0x08, 0x28, 0x04, 0x1c, 0xd2, 0x0e,
0x62, 0xfd, 0x2c, 0xef,
],
note_v: 5_584_102_310_880_684_544,
note_r: [
0x8c, 0x3e, 0x56, 0x44, 0x9d, 0xc8, 0x63, 0x54, 0xd3, 0x3b, 0x02, 0x5e, 0xf2, 0x79,
0x34, 0x60, 0xbc, 0xb1, 0x69, 0xf3, 0x32, 0x4e, 0x4a, 0x6b, 0x64, 0xba, 0xa6, 0x08,
0x32, 0x31, 0x57, 0x04,
],
note_cmu: [
0x7b, 0x48, 0xa8, 0x37, 0x5d, 0x3e, 0xbd, 0x56, 0xbc, 0x64, 0x9b, 0xb5, 0xb5, 0x24,
0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07,
0x8f, 0xea, 0x4d, 0x04,
],
note_pos: 1_814_747_072,
note_nf: [
0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20,
0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe,
0xb1, 0x7c, 0xd6, 0x20,
],
},
TestVector {
sk: [
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09,
],
ask: [
0xeb, 0xbb, 0x40, 0xa9, 0x80, 0xba, 0x3b, 0x88, 0x60, 0x94, 0x8d, 0x01, 0x1e, 0x1b,
0xfb, 0x4a, 0xff, 0xe1, 0x6c, 0x65, 0x2e, 0x90, 0xe9, 0x82, 0x58, 0x30, 0x2f, 0x44,
0x64, 0xc9, 0x1e, 0x0c,
],
nsk: [
0x68, 0x43, 0x1b, 0x19, 0x91, 0x04, 0x21, 0x52, 0x00, 0xb9, 0x5e, 0xe5, 0xcb, 0x71,
0xbf, 0x8b, 0x88, 0x3a, 0x3e, 0x95, 0xb7, 0x98, 0x9c, 0xad, 0x19, 0x70, 0x63, 0x14,
0x1e, 0xbb, 0xfd, 0x00,
],
ovk: [
0x57, 0x34, 0x67, 0xa7, 0xb3, 0x0e, 0xad, 0x6c, 0xcc, 0x50, 0x47, 0x44, 0xca, 0x9e,
0x1a, 0x28, 0x1a, 0x0d, 0x1a, 0x08, 0x73, 0x8b, 0x06, 0xa0, 0x68, 0x4f, 0xea, 0xcd,
0x1e, 0x9d, 0x12, 0x6d,
],
ak: [
0x71, 0xc3, 0x52, 0x3e, 0xec, 0xa3, 0x53, 0x11, 0xfb, 0xd5, 0xd7, 0xe7, 0xd7, 0x0b,
0x70, 0x9d, 0x6c, 0x35, 0xa2, 0x4f, 0x26, 0x2b, 0x34, 0xbf, 0x64, 0x05, 0x9b, 0xf2,
0xc0, 0x2e, 0x0b, 0xa8,
],
nk: [
0x62, 0x44, 0x00, 0x10, 0x3b, 0x65, 0x69, 0xb7, 0x35, 0x8f, 0xe8, 0x0f, 0x6f, 0x6c,
0xad, 0x43, 0x25, 0xde, 0xfd, 0xa9, 0xd9, 0x49, 0x9c, 0x2b, 0x8f, 0x88, 0x6a, 0x62,
0x69, 0xa2, 0xaa, 0x52,
],
ivk: [
0xdb, 0x95, 0xea, 0x8b, 0xd9, 0xf9, 0x3d, 0x41, 0xb5, 0xab, 0x2b, 0xeb, 0xc9, 0x1a,
0x38, 0xed, 0xd5, 0x27, 0x08, 0x3e, 0x2a, 0x6e, 0xf9, 0xf3, 0xc2, 0x97, 0x02, 0xd5,
0xff, 0x89, 0xed, 0x00,
],
default_d: [
0x23, 0x3c, 0x4a, 0xb8, 0x86, 0xa5, 0x5e, 0x3b, 0xa3, 0x74, 0xc0,
],
default_pk_d: [
0xb6, 0x8e, 0x9e, 0xe0, 0xc0, 0x67, 0x8d, 0x7b, 0x30, 0x36, 0x93, 0x1c, 0x83, 0x1a,
0x25, 0x25, 0x5f, 0x7e, 0xe4, 0x87, 0x38, 0x5a, 0x30, 0x31, 0x6e, 0x15, 0xf6, 0x48,
0x2b, 0x87, 0x4f, 0xda,
],
note_v: 17_811_330_145_809_239_872,
note_r: [
0x6e, 0xbb, 0xed, 0x74, 0x36, 0x19, 0xa2, 0x56, 0xf9, 0xad, 0x2e, 0x85, 0x88, 0x0c,
0xfa, 0xa9, 0x09, 0x8a, 0x5f, 0xdb, 0x16, 0x29, 0x99, 0x0d, 0x9a, 0x7d, 0x3b, 0xb9,
0x3f, 0xc9, 0x00, 0x03,
],
note_cmu: [
0xd3, 0x76, 0xa7, 0xbe, 0xe8, 0xce, 0x67, 0xf4, 0xef, 0xde, 0x56, 0xaa, 0x77, 0xcf,
0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63,
0xe4, 0x1d, 0xeb, 0x37,
],
note_pos: 2_578_461_368,
note_nf: [
0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe,
0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71,
0x0d, 0x93, 0xc9, 0xe9,
],
},
];

View File

@ -1,25 +1,24 @@
//! Sapling notes
//! Orchard notes
#![allow(clippy::unit_arg)]
#![allow(dead_code)]
use halo2::pasta::pallas;
mod ciphertexts;
mod nullifiers;
#[cfg(any(test, feature = "proptest-impl"))]
mod arbitrary;
use crate::{
amount::{Amount, NonNegative},
transaction::Memo,
};
use crate::amount::{Amount, NonNegative};
use super::{
commitment::CommitmentRandomness,
keys::{Diversifier, TransmissionKey},
};
pub use ciphertexts::{EncryptedNote, WrappedNoteKey};
pub use ciphertexts::EncryptedNote;
pub use nullifiers::Nullifier;
@ -35,9 +34,11 @@ pub struct Note {
pub transmission_key: TransmissionKey,
/// An integer representing the value of the note in zatoshi.
pub value: Amount<NonNegative>,
/// Used as input to PRF^nf as part of deriving the nullier of the note.
pub rho: pallas::Base, // TODO: refine type
/// Sender-controlled randomness.
pub psi: pallas::Base, // TODO: refine type
/// A random commitment trapdoor used to produce the associated
/// note commitment.
pub rcm: CommitmentRandomness,
/// The note memo, after decryption
pub memo: Memo,
}

View File

@ -1,31 +1,33 @@
#![allow(clippy::unit_arg)]
#![allow(dead_code)]
use super::super::{
commitment::{pedersen_hashes::mixing_pedersen_hash, NoteCommitment},
keys::NullifierDerivingKey,
tree::Position,
};
use halo2::pasta::pallas;
/// Invokes Blake2s-256 as PRF^nfSapling to derive the nullifier for a
/// Sapling note.
///
/// PRF^nfSapling(ρ*) := BLAKE2s-256("Zcash_nf", nk* || ρ*)
///
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
fn prf_nf(nk: [u8; 32], rho: [u8; 32]) -> [u8; 32] {
let hash = blake2s_simd::Params::new()
.hash_length(32)
.personal(b"Zcash_nf")
.to_state()
.update(&nk[..])
.update(&rho[..])
.finalize();
use super::super::{commitment::NoteCommitment, keys::NullifierDerivingKey, tree::Position};
*hash.as_array()
/// A cryptographic permutation, defined in [poseidonhash].
///
/// PoseidonHash(x, y) = f([x, y, 0])_1 (using 1-based indexing).
///
/// [poseidonhash]: https://zips.z.cash/protocol/nu5.pdf#poseidonhash
fn poseidon_hash(x: pallas::Base, y: pallas::Base) -> pallas::Base {
unimplemented!()
}
/// A Nullifier for Sapling transactions
/// Derive the nullifier for a Orchard note.
///
/// Instantiated using the PoseidonHash hash function defined in [§5.4.1.10
/// PoseidonHash Function][poseidon]
///
/// PRF^nfOrchard(nk*, ρ*) := PoseidonHash(nk*, ρ*)
///
/// [concreteprfs]: https://zips.z.cash/protocol/protocol.pdf#concreteprfs
/// [poseidonhash]: https://zips.z.cash/protocol/nu5.pdf#poseidonhash
fn prf_nf(nk: [u8; 32], rho: [u8; 32]) -> [u8; 32] {
poseidon_hash(nk, rho)
}
/// A Nullifier for Orchard transactions
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[cfg_attr(
any(test, feature = "proptest-impl"),

View File

@ -1,10 +1,81 @@
//! Sinsemilla hash functions and helpers.
use bitvec::prelude::*;
use rand_core::{CryptoRng, RngCore};
use halo2::pasta::pallas;
/// GroupHash into Pallas, aka _GroupHash^P_
///
/// Produces a random point in the Pallas curve. The first input element acts
/// as a domain separator to distinguish uses of the group hash for different
/// purposes; the second input element is the message.
///
/// https://zips.z.cash/protocol/protocol.pdf#concretegrouphashpallasandvesta
#[allow(non_snake_case)]
pub fn pallas_group_hash(D: &[u8], M: &[u8]) -> pallas::Point {
pallas::Point::hash_to_curve(D)(M)
}
/// Q(D) := GroupHash^P(“z.cash:SinsemillaQ”, D)
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
#[allow(non_snake_case)]
fn Q(D: &[u8]) -> pallas::Point {
pallas_group_hash("z.cash:SinsemillaQ", D)
}
/// S(j) := GroupHash^P(“z.cash:SinsemillaS”, LEBS2OSP32(I2LEBSP32(j)))
///
/// S: {0 .. 2^k - 1} -> P^*, aka 10 bits hashed into the group
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
// XXX: should j be strictly limited to k=10 bits?
#[allow(non_snake_case)]
fn S(j: [u8; 2]) -> pallas::Point {
pallas_group_hash("z.cash:SinsemillaS", &j)
}
/// "...an algebraic hash function with collision resistance (for fixed input
/// length) derived from assumed hardness of the Discrete Logarithm Problem on
/// the Pallas curve."
///
/// SinsemillaHash is used in the definitions of Sinsemilla commitments and of
/// the Sinsemilla hash for the Orchard incremental Merkle tree (§ 5.4.1.3
/// MerkleCRH^Orchard Hash Function).
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
#[allow(non_snake_case)]
// XXX: M is a max of k*c = 2530 bits
pub fn sinsemilla_hash_to_point(D: &[u8], M: &BitVec<Lsb0, u8>) -> pallas::Point {
let k = 10;
let c = 253;
assert!(M.len() <= k * c);
let mut acc = Q(D);
// Split M into n segments of k bits, where k = 10 and c = 253, padding
// the last segment with zeros.
//
// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
for chunk in M.chunks(k) {
// Pad each chunk with zeros.
let mut store = 0u8;
let bits = store.bits_mut::<Lsb0>();
chunk
.iter()
.enumerate()
.for_each(|(i, bit)| bits.set(i, *bit));
// An instance of LEBS2IP_k
let j = &bits.iter().fold(0u16, |j, &bit| j * *2 + bit as u16);
acc += acc + S(j.to_le_bytes());
}
acc
}
/// [Hash Extractor for Pallas][concreteextractorpallas]
///
/// P → B^[l^Orchard_Merkle]
@ -19,66 +90,6 @@ pub fn extract_p(point: pallas::Point) -> pallas::Base {
}
}
/// Q
///
/// Q(D) := GroupHash^P(“z.cash:SinsemillaQ”, D)
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
#[allow(non_snake_case)]
fn Q(domain: [u8; 8]) -> pallas::Point {
pallas::Point::hash_to_curve("z.cash:SinsemillaQ")(&domain[..])
}
/// S
///
/// S(j) := GroupHash^P(“z.cash:SinsemillaS”, LEBS2OSP32(I2LEBSP32(j)))
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
#[allow(non_snake_case)]
fn S(j: [u8; 2]) -> pallas::Point {
pallas::Point::hash_to_curve("z.cash:SinsemillaS")(&j)
}
/// "...an algebraic hash function with collision resistance (for fixed input
/// length) derived from assumed hardness of the Discrete Logarithm Problem on
/// the Jubjub curve."
///
/// SinsemillaHash is used in the definitions of Sinsemilla commitments and of
/// the Sinsemilla hash for the Sapling incremental Merkle tree (§ 5.4.1.3
/// MerkleCRH^Orchard Hash Function).
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
#[allow(non_snake_case)]
pub fn sinsemilla_hash_to_point(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> pallas::Point {
const K: u8 = 10;
const C: u8 = 253;
assert!(M.len() <= K * C);
let mut acc = Q(domain);
// Split M into n segments of k bits, where k = 10 and c = 253, padding
// the last segment with zeros.
//
// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
for chunk in M.chunks(K) {
// Pad each chunk with zeros.
let mut store = 0u8;
let bits = store.bits_mut::<Lsb0>();
chunk
.iter()
.enumerate()
.for_each(|(i, bit)| bits.set(i, *bit));
// An instance of LEBS2IP_k
let j = &bits.iter().fold(0u16, |j, &bit| j * *2 + bit as u16);
acc += (acc + S(j.to_le_bytes()));
}
acc
}
/// Sinsemilla Hash Function
///
/// "SinsemillaHash is an algebraic hash function with collision resistance (for
@ -90,19 +101,7 @@ pub fn sinsemilla_hash_to_point(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> pallas
///
/// https://zips.z.cash/protocol/protocol.pdf#concretesinsemillahash
#[allow(non_snake_case)]
pub fn sinsemilla_hash(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> pallas::Base {
extract_p(sinsemilla_hash_to_point(domain, M))
}
/// Generates a random scalar from the scalar field 𝔽_{q_P}.
///
/// https://zips.z.cash/protocol/nu5.pdf#pallasandvesta
pub fn generate_trapdoor<T>(csprng: &mut T) -> pallas::Scalar
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 64];
csprng.fill_bytes(&mut bytes);
// Scalar::from_bytes_wide() reduces the input modulo q under the hood.
pallas::Scalar::from_bytes_wide(&bytes)
// XXX: M is a max of k*c = 2530 bits, sinsemilla_hash_to_point checks this
pub fn sinsemilla_hash(D: &[u8], M: &BitVec<Lsb0, u8>) -> pallas::Base {
extract_p(sinsemilla_hash_to_point(D, M))
}

View File

@ -1 +0,0 @@