Most things are filled in, including a guess at Pallas-based Mixing Pedersen Hash

This commit is contained in:
Deirdre Connolly 2021-03-11 07:22:08 -05:00 committed by Deirdre Connolly
parent 23e391894b
commit df1ecc72b1
9 changed files with 397 additions and 324 deletions

120
Cargo.lock generated
View File

@ -59,6 +59,37 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aes"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
"aes-soft",
"aesni",
"cipher",
]
[[package]]
name = "aes-soft"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
"cipher",
"opaque-debug 0.3.0",
]
[[package]]
name = "aesni"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
"cipher",
"opaque-debug 0.3.0",
]
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.3.8" version = "0.3.8"
@ -377,13 +408,59 @@ dependencies = [
"constant_time_eq", "constant_time_eq",
] ]
[[package]]
name = "block-buffer"
<ersion = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding 0.1.5",
"byte-tools",
"byteorder",
"generic-array 0.12.3",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.9.0" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [ dependencies = [
"generic-array", "generic-array 0.14.4",
]
[[package]]
name = "block-modes"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
dependencies = [
"block-padding 0.2.1",
"cipher",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "block-padding"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "bls12_381"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20c116dad307b68138cc2e2f3a699c16f52faa47c65f98fc6de1dea9a097ee1e"
dependencies = [
"subtle",
] ]
[[package]] [[package]]
@ -521,6 +598,15 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
"generic-array 0.14.4",
]
[[package]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "1.2.0" version = "1.2.0"
@ -1113,6 +1199,19 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fpe"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a25080721bbcd2cd4d765b7d607ea350425fa087ce53cd3e31afcacdab850352"
dependencies = [
"aes",
"block-modes",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -1420,13 +1519,13 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
[[package]] [[package]]
name = "halo2" name = "halo2"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/zcash/halo2.git?branch=main#dda60a363001373d564156ad0334e2022d85a5b4" source = "git+https://github.com/zcash/halo2.git?branch=main#b079624ea78b4a07d44cb3c725dd734093577062"
dependencies = [ dependencies = [
"blake2b_simd", "blake2b_simd",
"crossbeam-utils 0.8.0", "crossbeam-utils 0.8.0",
"ff", "ff 0.9.0",
"funty", "funty",
"group", "group 0.9.0",
"num_cpus", "num_cpus",
"pasta_curves", "pasta_curves",
"rand 0.8.1", "rand 0.8.1",
@ -2095,6 +2194,17 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "num-bigint"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d0a3d5e207573f948a9e5376662aa743a2ea13f7c50a554d7af443a73fbfeba"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-format" name = "num-format"
version = "0.4.0" version = "0.4.0"
@ -4320,6 +4430,7 @@ dependencies = [
name = "zebra-chain" name = "zebra-chain"
version = "1.0.0-alpha.7" version = "1.0.0-alpha.7"
dependencies = [ dependencies = [
"aes",
"bech32", "bech32",
"bincode", "bincode",
"bitflags", "bitflags",
@ -4334,6 +4445,7 @@ dependencies = [
"displaydoc", "displaydoc",
"ed25519-zebra", "ed25519-zebra",
"equihash", "equihash",
"fpe",
"funty", "funty",
"futures 0.3.14", "futures 0.3.14",
"halo2", "halo2",

View File

@ -14,6 +14,7 @@ proptest-impl = ["proptest", "proptest-derive", "itertools"]
bench = ["zebra-test"] bench = ["zebra-test"]
[dependencies] [dependencies]
aes = "0.6"
bech32 = "0.8.0" bech32 = "0.8.0"
bitflags = "1.2.1" bitflags = "1.2.1"
bitvec = "0.17.4" bitvec = "0.17.4"
@ -24,6 +25,7 @@ byteorder = "1.4"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
displaydoc = "0.2.1" displaydoc = "0.2.1"
equihash = "0.1" equihash = "0.1"
fpe = "0.4"
futures = "0.3" futures = "0.3"
halo2 = { git = "https://github.com/zcash/halo2.git", branch = "main" } halo2 = { git = "https://github.com/zcash/halo2.git", branch = "main" }
hex = "0.4" hex = "0.4"

View File

@ -95,42 +95,39 @@ impl NoteCommitment {
diversifier: Diversifier, diversifier: Diversifier,
transmission_key: TransmissionKey, transmission_key: TransmissionKey,
value: Amount<NonNegative>, value: Amount<NonNegative>,
rho: pallas::Base,
psi: pallas::Base,
) -> Option<(CommitmentRandomness, Self)> ) -> Option<(CommitmentRandomness, Self)>
where where
T: RngCore + CryptoRng, T: RngCore + CryptoRng,
{ {
unimplemented!(); // s as in the argument name for WindowedPedersenCommit_r(s)
let mut s: BitVec<Lsb0, u8> = BitVec::new();
// // s as in the argument name for WindowedPedersenCommit_r(s) // Prefix
// let mut s: BitVec<Lsb0, u8> = BitVec::new(); s.append(&mut bitvec![1; 6]);
// // Prefix // The `TryFrom<Diversifier>` impls for the `pallas::*Point`s handles
// s.append(&mut bitvec![1; 6]); // 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;
}
// // The `TryFrom<Diversifier>` impls for the `jubjub::*Point`s handles let pk_d_bytes = <[u8; 32]>::from(transmission_key);
// // calling `DiversifyHash` implicitly. let v_bytes = value.to_bytes();
// 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;
// }
// let pk_d_bytes = <[u8; 32]>::from(transmission_key); // g*d || pk*d || I2LEBSP64(v)
// let v_bytes = value.to_bytes(); 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[..]));
// s.append(&mut BitVec::<Lsb0, u8>::from_slice(&g_d_bytes[..])); let rcm = CommitmentRandomness(generate_trapdoor(csprng));
// s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_d_bytes[..]));
// s.append(&mut BitVec::<Lsb0, u8>::from_slice(&v_bytes[..]));
// let rcm = CommitmentRandomness(generate_trapdoor(csprng)); Some((
rcm,
// Some(( NoteCommitment::from(sinsemilla_commit(rcm.0, "z.cash:Orchard-NoteCommit", &s)),
// rcm, ))
// NoteCommitment::from(windowed_pedersen_commitment(rcm.0, &s)),
// ))
} }
} }

View File

@ -15,7 +15,9 @@ use std::{
str::FromStr, str::FromStr,
}; };
use aes::Aes256;
use bech32::{self, FromBase32, ToBase32, Variant}; use bech32::{self, FromBase32, ToBase32, Variant};
use fpe::ff1::{BinaryNumeralString, FF1};
use halo2::pasta::pallas; use halo2::pasta::pallas;
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
@ -29,6 +31,26 @@ use crate::{
use super::sinsemilla::*; use super::sinsemilla::*;
/// PRP^d_K(d) := FF1-AES256_K("", d)
///
/// "Let FF1-AES256_K(tweak, x) be the FF1 format-preserving encryption
/// algorithm using AES with a 256-bit key K, and parameters radix = 2, minlen =
/// 88, maxlen = 88. It will be used only with the empty string "" as the
/// tweak. x is a sequence of 88bits, as is the output."
///
/// https://zips.z.cash/protocol/nu5.pdf#concreteprps
#[allow(non_snake_case)]
fn prp_d(K: [u8; 32], d: [u8; 11]) -> [u8; 11] {
let radix = 2;
let tweak = "";
let ff = FF1::<Aes256>::new(&K, radix).expect("valid radix");
let enc = ff
.encrypt(tweak.into(), &BinaryNumeralString::from_bytes_le(&d))
.unwrap();
}
/// Invokes Blake2b-512 as PRF^expand with parameter t. /// Invokes Blake2b-512 as PRF^expand with parameter t.
/// ///
/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) /// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)
@ -256,15 +278,13 @@ impl From<OutgoingViewingKey> for [u8; 32] {
impl From<FullViewingKey> for OutgoingViewingKey { impl From<FullViewingKey> for OutgoingViewingKey {
/// Derive an `OutgoingViewingKey` from a `FullViewingKey`. /// Derive an `OutgoingViewingKey` from a `FullViewingKey`.
/// ///
/// let 𝐾 = I2LEBSPsk(rivk) /// [4.2.3]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// let 𝐵 = reprP(ak) || I2LEBSP256(nk) #[allow(non_snake_case)]
/// let 𝑅 = PRFexpand fn from(fvk: FullViewingKey) -> OutgoingViewingKey {
/// 𝐾 ([0x82] || LEBS2OSP512(B)) let R = fvk.to_R();
/// let dk be the rst dk/8 bytes of 𝑅 and let ovk be the remaining ovk/8 bytes of 𝑅.
/// // let ovk be the remaining [32] bytes of R [which is 64 bytes]
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents Self(R[32..])
fn from(spending_key: SpendingKey) -> OutgoingViewingKey {
unimplemented!()
} }
} }
@ -381,6 +401,21 @@ impl fmt::Debug for IvkCommitRandomness {
} }
} }
impl From<SpendingKey> for IvkCommitRandomness {
/// rivk = ToScalar^Orchard(PRF^expand_sk ([8]))
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
fn from(sk: SpendingKey) -> Self {
Self(pallas::Scalar::from_bytes_wide(prf_expand(sk, [8])))
}
}
impl From<IvkCommitRandomness> for [u8; 32] {
fn from(rivk: IvkCommitRandomness) -> Self {
rivk.0.into()
}
}
/// Magic human-readable strings used to identify what networks Orchard incoming /// Magic human-readable strings used to identify what networks Orchard incoming
/// viewing keys are associated with when encoded/decoded with bech32. /// viewing keys are associated with when encoded/decoded with bech32.
/// ///
@ -483,161 +518,6 @@ impl PartialEq<[u8; 32]> for IncomingViewingKey {
} }
} }
/// 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#orchardkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub struct Diversifier(pub [u8; 11]);
impl fmt::Debug for Diversifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Diversifier")
.field(&hex::encode(&self.0))
.finish()
}
}
impl From<[u8; 11]> for Diversifier {
fn from(bytes: [u8; 11]) -> Self {
Self(bytes)
}
}
impl From<Diversifier> for [u8; 11] {
fn from(d: Diversifier) -> [u8; 11] {
d.0
}
}
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(projective_point) = pallas::Point::try_from(d) {
Ok(projective_point.into())
} else {
Err("Invalid Diversifier -> pallas::Affine")
}
}
}
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.3] from a `SpendingKey`.
///
/// 'For each spending key, there is also a default diversified
/// payment address with a “random-looking” diversifier. This
/// allows an implementation that does not expose diversified
/// addresses as a user-visible feature, to use a default address
/// that cannot be distinguished (without knowledge of the
/// spending key) from one with a random diversifier...'
///
/// 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 {
// Needs FF1-AES permutation
unimplemented!()
}
}
impl PartialEq<[u8; 11]> for Diversifier {
fn eq(&self, other: &[u8; 11]) -> bool {
self.0 == *other
}
}
impl Diversifier {
/// Generate a new `Diversifier`.
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
pub fn new<T>(csprng: &mut T) -> Self
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 11];
csprng.fill_bytes(&mut bytes);
Self::from(bytes)
}
}
/// A (diversified) transmission Key
///
/// 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 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 pallas::Affine);
impl fmt::Debug for TransmissionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TransmissionKey")
.field("x", &hex::encode(self.0.get_x().to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes()))
.finish()
}
}
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.
///
/// https://github.com/zkcrypto/jubjub/blob/master/src/lib.rs#L411
fn from(bytes: [u8; 32]) -> Self {
Self(pallas::Affine::from_bytes(bytes).unwrap())
}
}
impl From<TransmissionKey> for [u8; 32] {
fn from(pk_d: TransmissionKey) -> [u8; 32] {
pk_d.0.to_bytes()
}
}
impl From<(IncomingViewingKey, Diversifier)> for TransmissionKey {
/// This includes _KA^Orchard.DerivePublic(ivk, G_d)_, which is just a
/// scalar mult _\[ivk\]G_d_.
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement
fn from((ivk, d): (IncomingViewingKey, Diversifier)) -> Self {
Self(pallas::Affine::from(ivk.scalar * pallas::Point::from(d)))
}
}
impl PartialEq<[u8; 32]> for TransmissionKey {
fn eq(&self, other: &[u8; 32]) -> bool {
<[u8; 32]>::from(*self) == *other
}
}
/// Magic human-readable strings used to identify what networks Orchard full /// Magic human-readable strings used to identify what networks Orchard full
/// viewing keys are associated with when encoded/decoded with bech32. /// viewing keys are associated with when encoded/decoded with bech32.
/// ///
@ -722,9 +602,189 @@ impl FromStr for FullViewingKey {
} }
} }
impl FullViewingKey {
/// [4.2.3]: https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
#[allow(non_snake_case)]
pub fn to_R(&self) -> [u8; 32] {
// let K = I2LEBSP_l_sk(rivk)
let K: [u8; 32] = self.ivk_commit_randomness.into();
// let R = PRF^expand_K( [0x82] || I2LEOSP256(ak) || I2LEOSP256(nk) )
prf_expand(K, ([0x82], self.ak.into(), self.nk.into()).concat())
}
}
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct DiversifierKey([u8; 32]); pub struct DiversifierKey([u8; 32]);
impl From<FullViewingKey> for DiversifierKey {
/// Derives a _diversifier key_ from a `FullViewingKey`.
///
/// 'For each spending key, there is also a default diversified
/// payment address with a “random-looking” diversifier. This
/// allows an implementation that does not expose diversified
/// addresses as a user-visible feature, to use a default address
/// that cannot be distinguished (without knowledge of the
/// spending key) from one with a random diversifier...'
///
/// 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
#[allow(non_snake_case)]
fn from(fvk: FullViewingKey) -> DiversifierKey {
let R = fvk.to_R();
// let dk be the first [32] bytes of R
Self(R[..32])
}
}
/// 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#orchardkeycomponents
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub struct Diversifier(pub [u8; 11]);
impl fmt::Debug for Diversifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Diversifier")
.field(&hex::encode(&self.0))
.finish()
}
}
impl From<[u8; 11]> for Diversifier {
fn from(bytes: [u8; 11]) -> Self {
Self(bytes)
}
}
impl From<DiversifierKey> for Diversifier {
/// Generates the _default diversifier_, where the index into the
/// `DiversifierKey` is 0.
///
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
fn from(dk: DiversifierKey) -> Self {
Self(prp_d(dk.into(), [0u8; 11]))
}
}
impl From<Diversifier> for [u8; 11] {
fn from(d: Diversifier) -> [u8; 11] {
d.0
}
}
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 PartialEq<[u8; 11]> for Diversifier {
fn eq(&self, other: &[u8; 11]) -> bool {
self.0 == *other
}
}
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(projective_point) = pallas::Point::try_from(d) {
Ok(projective_point.into())
} else {
Err("Invalid Diversifier -> pallas::Affine")
}
}
}
impl Diversifier {
/// Generate a new `Diversifier`.
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
pub fn new<T>(csprng: &mut T) -> Self
where
T: RngCore + CryptoRng,
{
let mut bytes = [0u8; 11];
csprng.fill_bytes(&mut bytes);
Self::from(bytes)
}
}
/// A (diversified) transmission Key
///
/// 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 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 pallas::Affine);
impl fmt::Debug for TransmissionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("TransmissionKey")
.field("x", &hex::encode(self.0.get_x().to_bytes()))
.field("y", &hex::encode(self.0.get_y().to_bytes()))
.finish()
}
}
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.
///
/// https://github.com/zkcrypto/jubjub/blob/master/src/lib.rs#L411
fn from(bytes: [u8; 32]) -> Self {
Self(pallas::Affine::from_bytes(bytes).unwrap())
}
}
impl From<TransmissionKey> for [u8; 32] {
fn from(pk_d: TransmissionKey) -> [u8; 32] {
pk_d.0.to_bytes()
}
}
impl From<(IncomingViewingKey, Diversifier)> for TransmissionKey {
/// This includes _KA^Orchard.DerivePublic(ivk, G_d)_, which is just a
/// scalar mult _\[ivk\]G_d_.
///
/// https://zips.z.cash/protocol/protocol.pdf#orchardkeycomponents
/// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement
fn from((ivk, d): (IncomingViewingKey, Diversifier)) -> Self {
Self(pallas::Affine::from(pallas::Point::from(d) * ivk.scalar))
}
}
impl PartialEq<[u8; 32]> for TransmissionKey {
fn eq(&self, other: &[u8; 32]) -> bool {
<[u8; 32]>::from(*self) == *other
}
}
/// An ephemeral public key for Orchard key agreement. /// An ephemeral public key for Orchard key agreement.
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement /// https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement

View File

@ -18,7 +18,7 @@ use super::{
keys::{Diversifier, TransmissionKey}, keys::{Diversifier, TransmissionKey},
}; };
pub use ciphertexts::EncryptedNote; pub use ciphertexts::{EncryptedNote, WrappedNoteKey};
pub use nullifiers::Nullifier; pub use nullifiers::Nullifier;

View File

@ -3,7 +3,24 @@
use halo2::pasta::pallas; use halo2::pasta::pallas;
use super::super::{commitment::NoteCommitment, keys::NullifierDerivingKey, tree::Position}; use super::super::{
commitment::NoteCommitment, keys::NullifierDerivingKey, sinsemilla::*, tree::Position,
};
/// Orchard ixing 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]GroupHash^P^(r)(“Zcash_P_”, “”)
///
/// https://zips.z.cash/protocol/protocol.pdf#concretemixinghash
// TODO: I'M EXTRAPOLATING HERE, DOUBLE CHECK THE SPEC WHEN FINALIZED
#[allow(non_snake_case)]
pub fn mixing_pedersen_hash(P: pallas::Point, x: pallas::Scalar) -> pallas::Point {
P + pallas_group_hash(*b"Zcash_P_", b"") * x
}
/// A cryptographic permutation, defined in [poseidonhash]. /// A cryptographic permutation, defined in [poseidonhash].
/// ///
@ -42,8 +59,15 @@ impl From<[u8; 32]> for Nullifier {
} }
impl<'a> From<(NoteCommitment, Position, &'a NullifierDerivingKey)> for Nullifier { impl<'a> From<(NoteCommitment, Position, &'a NullifierDerivingKey)> for Nullifier {
/// Derive a `Nullifier` for an Orchard _note_.
///
/// For a _note_, the _nullifier_ is derived as PRF^nfOrchard_nk(ρ*), where
/// k is a representation of the _nullifier deriving key_ associated with
/// the _note_ and ρ = repr_P(ρ).
///
/// https://zips.z.cash/protocol/nu5.pdf#commitmentsandnullifiers
fn from((cm, pos, nk): (NoteCommitment, Position, &'a NullifierDerivingKey)) -> Self { fn from((cm, pos, nk): (NoteCommitment, Position, &'a NullifierDerivingKey)) -> Self {
let rho = jubjub::AffinePoint::from(mixing_pedersen_hash(cm.0.into(), pos.0.into())); let rho = pallas::Affine::from(mixing_pedersen_hash(cm.0.into(), pos.0.into()));
Nullifier(prf_nf(nk.into(), rho.to_bytes())) Nullifier(prf_nf(nk.into(), rho.to_bytes()))
} }

View File

@ -16,8 +16,8 @@ pub use self::halo2::Halo2Proof;
/// A marker trait used to abstract over BCTV14, Groth16, or Halo2 proofs. /// A marker trait used to abstract over BCTV14, Groth16, or Halo2 proofs.
pub trait ZkSnarkProof: pub trait ZkSnarkProof:
Copy // Copy +
+ Clone Clone
+ Debug + Debug
+ PartialEq + PartialEq
+ Eq + Eq

View File

@ -11,9 +11,10 @@ mod constants;
// mod hash; // mod hash;
// mod scalar_mul; // mod scalar_mul;
// mod signature; // mod signature;
// mod signing_key; mod signing_key;
mod verification_key; mod verification_key;
pub use signing_key::SigningKey;
pub use verification_key::{VerificationKey, VerificationKeyBytes}; pub use verification_key::{VerificationKey, VerificationKeyBytes};
/// Abstracts over different RedPallas parameter choices, [`Binding`] /// Abstracts over different RedPallas parameter choices, [`Binding`]

View File

@ -6,7 +6,7 @@ use std::{
use halo2::pasta::pallas; use halo2::pasta::pallas;
use super::{Error, SigType, Signature, SpendAuth}; use super::SigType;
/// A refinement type for `[u8; 32]` indicating that the bytes represent /// A refinement type for `[u8; 32]` indicating that the bytes represent
/// an encoding of a RedPallas verification key. /// an encoding of a RedPallas verification key.
@ -65,126 +65,3 @@ pub struct VerificationKey<T: SigType> {
pub(crate) point: pallas::Point, pub(crate) point: pallas::Point,
pub(crate) bytes: VerificationKeyBytes<T>, pub(crate) bytes: VerificationKeyBytes<T>,
} }
impl<T: SigType> From<VerificationKey<T>> for VerificationKeyBytes<T> {
fn from(pk: VerificationKey<T>) -> VerificationKeyBytes<T> {
pk.bytes
}
}
impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
fn from(pk: VerificationKey<T>) -> [u8; 32] {
pk.bytes.bytes
}
}
impl<T: SigType> TryFrom<VerificationKeyBytes<T>> for VerificationKey<T> {
type Error = Error;
fn try_from(bytes: VerificationKeyBytes<T>) -> Result<Self, Self::Error> {
// XXX-pasta-curves: this should not use CtOption
// XXX-pasta-curves: this takes ownership of bytes, while Fr doesn't.
// This checks that the encoding is canonical...
let maybe_point = pallas::Point::from_bytes(bytes.bytes);
if maybe_point.is_some().into() {
let point = maybe_point.unwrap();
// This checks that the verification key is not of small order.
if <bool>::from(point.is_small_order()) == false {
Ok(VerificationKey { point, bytes })
} else {
Err(Error::MalformedVerificationKey)
}
} else {
Err(Error::MalformedVerificationKey)
}
}
}
impl<T: SigType> TryFrom<[u8; 32]> for VerificationKey<T> {
type Error = Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
use std::convert::TryInto;
VerificationKeyBytes::from(bytes).try_into()
}
}
impl VerificationKey<SpendAuth> {
/// Randomize this verification key with the given `randomizer`.
///
/// Randomization is only supported for `SpendAuth` keys.
pub fn randomize(&self, randomizer: &pallas::Scalar) -> VerificationKey<SpendAuth> {
use super::private::Sealed;
let point = self.point + &(&SpendAuth::basepoint() * randomizer);
let bytes = VerificationKeyBytes {
bytes: point.to_bytes().as_ref().try_into().unwrap(),
_marker: PhantomData,
};
VerificationKey { bytes, point }
}
}
impl<T: SigType> VerificationKey<T> {
pub(crate) fn from(s: &pallas::Scalar) -> VerificationKey<T> {
let point = &T::basepoint() * s;
let bytes = VerificationKeyBytes {
bytes: point.to_bytes().as_ref().try_into().unwrap(),
_marker: PhantomData,
};
VerificationKey { bytes, point }
}
/// Verify a purported `signature` over `msg` made by this verification key.
// This is similar to impl signature::Verifier but without boxed errors
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), Error> {
use super::HStar;
let c = HStar::default()
.update(&signature.r_bytes[..])
.update(&self.bytes.bytes[..]) // XXX ugly
.update(msg)
.finalize();
self.verify_prehashed(signature, c)
}
/// Verify a purported `signature` with a prehashed challenge.
#[allow(non_snake_case)]
pub(crate) fn verify_prehashed(
&self,
signature: &Signature<T>,
c: pallas::Scalar,
) -> Result<(), Error> {
let r = {
// XXX-pasta-curves: should not use CtOption here
// XXX-pasta-curves: inconsistent ownership in from_bytes
let maybe_point = pallas::Point::from_bytes(signature.r_bytes);
if maybe_point.is_some().into() {
maybe_point.unwrap()
} else {
return Err(Error::InvalidSignature);
}
};
let s = {
// XXX-pasta-curves: should not use CtOption here
let maybe_scalar = pallas::Scalar::from_bytes(&signature.s_bytes);
if maybe_scalar.is_some().into() {
maybe_scalar.unwrap()
} else {
return Err(Error::InvalidSignature);
}
};
// XXX rewrite as normal double scalar mul
// Verify check is h * ( - s * B + R + c * A) == 0
// h * ( s * B - c * A - R) == 0
let sB = T::basepoint() * s;
let cA = self.point * c;
let check = sB - cA - r;
if check.is_small_order().into() {
Ok(())
} else {
Err(Error::InvalidSignature)
}
}
}