Most things are filled in, including a guess at Pallas-based Mixing Pedersen Hash
This commit is contained in:
parent
23e391894b
commit
df1ecc72b1
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)),
|
|
||||||
// ))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 𝐾 = I2LEBSPℓsk(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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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`]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue