From 52951f72369f983436839e761ac5389e5676d5f9 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 15:39:55 -0800 Subject: [PATCH 1/9] Add keygen. --- Cargo.toml | 1 + src/secret_key.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 62d2706..d5d0dcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Henry de Valence "] edition = "2018" [dependencies] +rand_core = "0.5" thiserror = "1.0" blake2b_simd = "0.5" jubjub = { git = "https://github.com/zkcrypto/jubjub", rev = "e83f7d2bd136498a27f9d943fea635d8682bf2c6" } diff --git a/src/secret_key.rs b/src/secret_key.rs index 54a6fdd..aae0dc6 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -2,6 +2,8 @@ use std::{convert::TryFrom, marker::PhantomData}; use crate::{Binding, Error, PublicKey, Randomizer, Scalar, SigType, Signature, SpendAuth}; +use rand_core::{CryptoRng, RngCore}; + /// A refinement type indicating that the inner `[u8; 32]` represents an /// encoding of a RedJubJub secret key. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -63,6 +65,21 @@ impl TryFrom> for SecretKey { } } +impl From for SecretKey +where + R: RngCore + CryptoRng, + T: SigType, +{ + fn from(mut rng: R) -> SecretKey { + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes); + SecretKey { + sk: Scalar::from_bytes_wide(&bytes), + _marker: PhantomData, + } + } +} + impl<'a> From<&'a SecretKey> for PublicKey { fn from(sk: &'a SecretKey) -> PublicKey { // XXX-jubjub: this is pretty baroque From b44f149381522641a181f912123085ce3f1420fc Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 15:59:24 -0800 Subject: [PATCH 2/9] Reorganize data types. --- src/lib.rs | 2 +- src/public_key.rs | 23 +++++++++---------- src/secret_key.rs | 57 +++++++++++++---------------------------------- 3 files changed, 27 insertions(+), 55 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7ca40cf..a016790 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ type Scalar = jubjub::Fr; pub use error::Error; pub use public_key::{PublicKey, PublicKeyBytes}; -pub use secret_key::{SecretKey, SecretKeyBytes}; +pub use secret_key::SecretKey; pub use signature::Signature; /// Abstracts over different RedJubJub parameter choices. diff --git a/src/public_key.rs b/src/public_key.rs index c6d773c..b076238 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -2,12 +2,16 @@ use std::{convert::TryFrom, marker::PhantomData}; use crate::{Binding, Error, Randomizer, SigType, Signature, SpendAuth}; -/// A refinement type indicating that the inner `[u8; 32]` represents an -/// encoding of a RedJubJub public key. +/// A refinement type for `[u8; 32]` indicating that the bytes represent +/// an encoding of a RedJubJub public key. +/// +/// This is useful for representing a compressed public key; the +/// [`PublicKey`] type in this library holds other decompressed state +/// used in signature verification. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct PublicKeyBytes { - bytes: [u8; 32], - _marker: PhantomData, + pub(crate) bytes: [u8; 32], + pub(crate) _marker: PhantomData, } impl From<[u8; 32]> for PublicKeyBytes { @@ -26,20 +30,16 @@ impl From> for [u8; 32] { } /// A RedJubJub public key. -// XXX PartialEq, Eq? #[derive(Copy, Clone, Debug)] pub struct PublicKey { // XXX-jubjub: this should just be Point pub(crate) point: jubjub::ExtendedPoint, - // XXX should this just store a PublicKeyBytes? - pub(crate) bytes: [u8; 32], - pub(crate) _marker: PhantomData, + pub(crate) bytes: PublicKeyBytes, } impl From> for PublicKeyBytes { fn from(pk: PublicKey) -> PublicKeyBytes { - let PublicKey { bytes, _marker, .. } = pk; - PublicKeyBytes { bytes, _marker } + pk.bytes } } @@ -53,8 +53,7 @@ impl TryFrom> for PublicKey { if maybe_point.is_some().into() { Ok(PublicKey { point: maybe_point.unwrap().into(), - bytes: bytes.bytes, - _marker: PhantomData, + bytes, }) } else { Err(Error::MalformedPublicKey) diff --git a/src/secret_key.rs b/src/secret_key.rs index aae0dc6..db8a913 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -1,59 +1,31 @@ use std::{convert::TryFrom, marker::PhantomData}; -use crate::{Binding, Error, PublicKey, Randomizer, Scalar, SigType, Signature, SpendAuth}; +use crate::{ + Binding, Error, PublicKey, PublicKeyBytes, Randomizer, Scalar, SigType, Signature, SpendAuth, +}; use rand_core::{CryptoRng, RngCore}; -/// A refinement type indicating that the inner `[u8; 32]` represents an -/// encoding of a RedJubJub secret key. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct SecretKeyBytes { - bytes: [u8; 32], - _marker: PhantomData, -} - -impl From<[u8; 32]> for SecretKeyBytes { - fn from(bytes: [u8; 32]) -> SecretKeyBytes { - SecretKeyBytes { - bytes, - _marker: PhantomData, - } - } -} - -impl From> for [u8; 32] { - fn from(refined: SecretKeyBytes) -> [u8; 32] { - refined.bytes - } -} - /// A RedJubJub secret key. -// XXX PartialEq, Eq? #[derive(Copy, Clone, Debug)] pub struct SecretKey { sk: Scalar, _marker: PhantomData, } -impl From> for SecretKeyBytes { - fn from(sk: SecretKey) -> SecretKeyBytes { - SecretKeyBytes { - bytes: sk.sk.to_bytes(), - _marker: PhantomData, - } +impl From> for [u8; 32] { + fn from(sk: SecretKey) -> [u8; 32] { + sk.sk.to_bytes() } } -// XXX could this be a From impl? -// not unless there's an infallible conversion from bytes to scalars, -// which is not currently present in jubjub -impl TryFrom> for SecretKey { +impl TryFrom<[u8; 32]> for SecretKey { type Error = Error; - fn try_from(bytes: SecretKeyBytes) -> Result { + fn try_from(bytes: [u8; 32]) -> Result { // XXX-jubjub: it does not make sense for this to be a CtOption... // XXX-jubjub: this takes a borrow but point deser doesn't - let maybe_sk = Scalar::from_bytes(&bytes.bytes); + let maybe_sk = Scalar::from_bytes(&bytes); if maybe_sk.is_some().into() { Ok(SecretKey { sk: maybe_sk.unwrap(), @@ -65,6 +37,7 @@ impl TryFrom> for SecretKey { } } +/* impl From for SecretKey where R: RngCore + CryptoRng, @@ -79,6 +52,7 @@ where } } } +*/ impl<'a> From<&'a SecretKey> for PublicKey { fn from(sk: &'a SecretKey) -> PublicKey { @@ -107,12 +81,11 @@ fn pk_from_sk_inner( basepoint: jubjub::ExtendedPoint, ) -> PublicKey { let point = &basepoint * &sk.sk; - let bytes = jubjub::AffinePoint::from(&point).to_bytes(); - PublicKey { - point, - bytes, + let bytes = PublicKeyBytes { + bytes: jubjub::AffinePoint::from(&point).to_bytes(), _marker: PhantomData, - } + }; + PublicKey { bytes, point } } impl SecretKey { From 84b042003b55618416df866e7ca52c4965e724d3 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 18:20:45 -0800 Subject: [PATCH 3/9] Add methods to the Sealed trait, simplifying types. The motivation is as follows. The sealed trait pattern allows creating a type-level equivalent of an enum: the trait corresponds to the enum type and its implementors correspond to the enum variants; the `Sealed` restriction ensures that there is a fixed set of enum variants. In this picture, adding methods to the public trait corresponds to a public method on an enum, while adding methods to the private trait corresponds to a private method on an enum. This means that we can add a method to get the basepoint (whose possible choices are enumerated by SigType) and avoid having to do specialized impls. --- src/lib.rs | 22 ++++++++++++++--- src/public_key.rs | 11 ++++++++- src/secret_key.rs | 62 +++++++++-------------------------------------- 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a016790..a2d2e0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,9 +42,23 @@ impl SigType for Binding {} pub struct SpendAuth {} impl SigType for SpendAuth {} -mod private { +pub(crate) mod private { use super::*; - pub trait Sealed {} - impl Sealed for Binding {} - impl Sealed for SpendAuth {} + pub trait Sealed { + fn basepoint() -> jubjub::ExtendedPoint; + } + impl Sealed for Binding { + fn basepoint() -> jubjub::ExtendedPoint { + jubjub::AffinePoint::from_bytes(constants::BINDINGSIG_BASEPOINT_BYTES) + .unwrap() + .into() + } + } + impl Sealed for SpendAuth { + fn basepoint() -> jubjub::ExtendedPoint { + jubjub::AffinePoint::from_bytes(constants::SPENDAUTHSIG_BASEPOINT_BYTES) + .unwrap() + .into() + } + } } diff --git a/src/public_key.rs b/src/public_key.rs index b076238..4bbcadb 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -1,6 +1,6 @@ use std::{convert::TryFrom, marker::PhantomData}; -use crate::{Binding, Error, Randomizer, SigType, Signature, SpendAuth}; +use crate::{Binding, Error, Randomizer, Scalar, SigType, Signature, SpendAuth}; /// A refinement type for `[u8; 32]` indicating that the bytes represent /// an encoding of a RedJubJub public key. @@ -62,6 +62,15 @@ impl TryFrom> for PublicKey { } impl PublicKey { + pub(crate) fn from_secret(s: &Scalar) -> PublicKey { + let point = &T::basepoint() * s; + let bytes = PublicKeyBytes { + bytes: jubjub::AffinePoint::from(&point).to_bytes(), + _marker: PhantomData, + }; + PublicKey { bytes, point } + } + /// Randomize this public key with the given `randomizer`. pub fn randomize(&self, randomizer: Randomizer) -> PublicKey { unimplemented!(); diff --git a/src/secret_key.rs b/src/secret_key.rs index db8a913..5a913d7 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -10,7 +10,7 @@ use rand_core::{CryptoRng, RngCore}; #[derive(Copy, Clone, Debug)] pub struct SecretKey { sk: Scalar, - _marker: PhantomData, + pk: PublicKey, } impl From> for [u8; 32] { @@ -19,21 +19,17 @@ impl From> for [u8; 32] { } } -impl TryFrom<[u8; 32]> for SecretKey { - type Error = Error; - - fn try_from(bytes: [u8; 32]) -> Result { - // XXX-jubjub: it does not make sense for this to be a CtOption... - // XXX-jubjub: this takes a borrow but point deser doesn't - let maybe_sk = Scalar::from_bytes(&bytes); - if maybe_sk.is_some().into() { - Ok(SecretKey { - sk: maybe_sk.unwrap(), - _marker: PhantomData, - }) - } else { - Err(Error::MalformedSecretKey) - } +impl From<[u8; 32]> for SecretKey { + fn from(bytes: [u8; 32]) -> Self { + let sk = { + // XXX-jubjub: would be nice to unconditionally deser + // This incantation ensures deserialization is infallible. + let mut wide = [0; 64]; + wide[0..32].copy_from_slice(&bytes); + Scalar::from_bytes_wide(&wide) + }; + let pk = PublicKey::from_secret(&sk); + SecretKey { sk, pk } } } @@ -54,40 +50,6 @@ where } */ -impl<'a> From<&'a SecretKey> for PublicKey { - fn from(sk: &'a SecretKey) -> PublicKey { - // XXX-jubjub: this is pretty baroque - // XXX-jubjub: provide basepoint tables for generators - let basepoint: jubjub::ExtendedPoint = - jubjub::AffinePoint::from_bytes(crate::constants::SPENDAUTHSIG_BASEPOINT_BYTES) - .unwrap() - .into(); - pk_from_sk_inner(sk, basepoint) - } -} - -impl<'a> From<&'a SecretKey> for PublicKey { - fn from(sk: &'a SecretKey) -> PublicKey { - let basepoint: jubjub::ExtendedPoint = - jubjub::AffinePoint::from_bytes(crate::constants::BINDINGSIG_BASEPOINT_BYTES) - .unwrap() - .into(); - pk_from_sk_inner(sk, basepoint) - } -} - -fn pk_from_sk_inner( - sk: &SecretKey, - basepoint: jubjub::ExtendedPoint, -) -> PublicKey { - let point = &basepoint * &sk.sk; - let bytes = PublicKeyBytes { - bytes: jubjub::AffinePoint::from(&point).to_bytes(), - _marker: PhantomData, - }; - PublicKey { bytes, point } -} - impl SecretKey { /// Randomize this public key with the given `randomizer`. pub fn randomize(&self, randomizer: Randomizer) -> PublicKey { From b202a22826c1b96da43c2bd6f32baf5aa28686ec Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 19:03:44 -0800 Subject: [PATCH 4/9] Unfortunately keygen can't be a From impl because coherence rules. --- src/secret_key.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/secret_key.rs b/src/secret_key.rs index 5a913d7..811d763 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -33,24 +33,18 @@ impl From<[u8; 32]> for SecretKey { } } -/* -impl From for SecretKey -where - R: RngCore + CryptoRng, - T: SigType, -{ - fn from(mut rng: R) -> SecretKey { - let mut bytes = [0; 64]; - rng.fill_bytes(&mut bytes); - SecretKey { - sk: Scalar::from_bytes_wide(&bytes), - _marker: PhantomData, - } - } -} -*/ - impl SecretKey { + /// Generate a new secret key. + pub fn new(mut rng: R) -> SecretKey { + let sk = { + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes); + Scalar::from_bytes_wide(&bytes) + }; + let pk = PublicKey::from_secret(&sk); + SecretKey { sk, pk } + } + /// Randomize this public key with the given `randomizer`. pub fn randomize(&self, randomizer: Randomizer) -> PublicKey { unimplemented!(); From 710ac6fba9d7f56130ad48e915a1ab02f14a77a2 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 19:54:31 -0800 Subject: [PATCH 5/9] Add an hash-to-scalar implementation. --- src/hash.rs | 30 ++++++++++++++++++++++++++++++ src/lib.rs | 4 ++++ 2 files changed, 34 insertions(+) create mode 100644 src/hash.rs diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 0000000..2492322 --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,30 @@ +use blake2b_simd::{Params, State}; + +use crate::Scalar; + +/// Provides H^star, the hash-to-scalar function used by RedJubjub. +pub struct HStar { + state: State, +} + +impl Default for HStar { + fn default() -> Self { + let state = Params::new() + .hash_length(64) + .personal(b"Zcash_RedJubjubH") + .to_state(); + Self { state } + } +} + +impl HStar { + /// Add `data` to the hash. + pub fn update(&mut self, data: &[u8]) { + self.state.update(data); + } + + /// Consume `self` to compute the hash output. + pub fn finalize(mut self) -> Scalar { + Scalar::from_bytes_wide(self.state.finalize().as_array()) + } +} diff --git a/src/lib.rs b/src/lib.rs index a2d2e0b..ec359c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ mod constants; mod error; +mod hash; mod public_key; mod secret_key; mod signature; @@ -14,8 +15,11 @@ mod signature; pub type Randomizer = jubjub::Fr; /// A better name than Fr. +// XXX-jubjub: upstream this name type Scalar = jubjub::Fr; +use hash::HStar; + pub use error::Error; pub use public_key::{PublicKey, PublicKeyBytes}; pub use secret_key::SecretKey; From 2b37c71b57cbfa18fd05acb469349dce4a6ff13a Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 19:19:36 -0800 Subject: [PATCH 6/9] Implement signing. --- src/hash.rs | 5 +++-- src/secret_key.rs | 44 +++++++++++++++++++++++++++++--------------- src/signature.rs | 12 ++++++++++++ 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/hash.rs b/src/hash.rs index 2492322..b3beee6 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -18,9 +18,10 @@ impl Default for HStar { } impl HStar { - /// Add `data` to the hash. - pub fn update(&mut self, data: &[u8]) { + /// Add `data` to the hash, and return `Self` for chaining. + pub fn update(mut self, data: &[u8]) -> Self { self.state.update(data); + self } /// Consume `self` to compute the hash output. diff --git a/src/secret_key.rs b/src/secret_key.rs index 811d763..4370328 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -49,22 +49,36 @@ impl SecretKey { pub fn randomize(&self, randomizer: Randomizer) -> PublicKey { unimplemented!(); } -} -impl SecretKey { - /// Create a Zcash `BindingSig` on `msg` using this `SecretKey`. + /// Create a signature of type `T` on `msg` using this `SecretKey`. // Similar to signature::Signer but without boxed errors. - pub fn sign(&self, msg: &[u8]) -> Signature { - // could use sign_inner - unimplemented!(); - } -} - -impl SecretKey { - /// Create a Zcash `SpendAuthSig` on `msg` using this `SecretKey`. - // Similar to signature::Signer but without boxed errors. - pub fn sign(&self, msg: &[u8]) -> Signature { - // could use sign_inner - unimplemented!(); + pub fn sign(&self, mut rng: R, msg: &[u8]) -> Signature { + use crate::HStar; + + // Choose a byte sequence uniformly at random of length + // (\ell_H + 128)/8 bytes. For RedJubjub this is (512 + 128)/8 = 80. + let random_bytes = { + let mut bytes = [0; 80]; + rng.fill_bytes(&mut bytes); + bytes + }; + + let nonce = HStar::default() + .update(&random_bytes[..]) + .update(&self.pk.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + + let r_bytes = jubjub::AffinePoint::from(&T::basepoint() * &nonce).to_bytes(); + + let c = HStar::default() + .update(&r_bytes[..]) + .update(&self.pk.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + + let s_bytes = (&nonce + &(&c * &self.sk)).to_bytes(); + + Signature::from_parts(r_bytes, s_bytes) } } diff --git a/src/signature.rs b/src/signature.rs index e902eb1..8514e80 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -23,6 +23,18 @@ impl From> for [u8; 64] { } } +impl Signature { + pub(crate) fn from_parts(r_bytes: [u8; 32], s_bytes: [u8; 32]) -> Self { + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(&r_bytes[..]); + bytes[32..64].copy_from_slice(&s_bytes[..]); + Self { + bytes, + _marker: PhantomData, + } + } +} + // These impls all only exist because of array length restrictions. // XXX print the type variable From d3b20d0f2189e67d48d98c3109a3bcd321236696 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 20:49:48 -0800 Subject: [PATCH 7/9] Store signature bytes in two parts. --- src/secret_key.rs | 6 ++++- src/signature.rs | 60 +++++++++++------------------------------------ 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/secret_key.rs b/src/secret_key.rs index 4370328..031c803 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -79,6 +79,10 @@ impl SecretKey { let s_bytes = (&nonce + &(&c * &self.sk)).to_bytes(); - Signature::from_parts(r_bytes, s_bytes) + Signature{ + r_bytes, + s_bytes, + _marker: PhantomData, + } } } diff --git a/src/signature.rs b/src/signature.rs index 8514e80..6351676 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -3,65 +3,33 @@ use std::{convert, fmt, marker::PhantomData}; use crate::SigType; /// A RedJubJub signature. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Signature { - bytes: [u8; 64], - _marker: PhantomData, + pub(crate) r_bytes: [u8; 32], + pub(crate) s_bytes: [u8; 32], + pub(crate) _marker: PhantomData, } impl From<[u8; 64]> for Signature { fn from(bytes: [u8; 64]) -> Signature { + let mut r_bytes = [0; 32]; + r_bytes.copy_from_slice(&bytes[0..32]); + let mut s_bytes = [0; 32]; + s_bytes.copy_from_slice(&bytes[32..64]); Signature { - bytes, + r_bytes, + s_bytes, _marker: PhantomData, } } } impl From> for [u8; 64] { - fn from(s: Signature) -> [u8; 64] { - s.bytes - } -} - -impl Signature { - pub(crate) fn from_parts(r_bytes: [u8; 32], s_bytes: [u8; 32]) -> Self { + fn from(sig: Signature) -> [u8; 64] { let mut bytes = [0; 64]; - bytes[0..32].copy_from_slice(&r_bytes[..]); - bytes[32..64].copy_from_slice(&s_bytes[..]); - Self { - bytes, - _marker: PhantomData, - } + bytes[0..32].copy_from_slice(&sig.r_bytes[..]); + bytes[32..64].copy_from_slice(&sig.s_bytes[..]); + bytes } } -// These impls all only exist because of array length restrictions. - -// XXX print the type variable -impl fmt::Debug for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - //f.debug_tuple("Signature").field(&self.0[..]).finish() - f.debug_tuple("Signature").finish() - } -} - -impl Copy for Signature {} - -impl Clone for Signature { - fn clone(&self) -> Self { - let mut bytes = [0; 64]; - bytes[..].copy_from_slice(&self.bytes[..]); - Signature { - bytes, - _marker: PhantomData, - } - } -} - -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - self.bytes[..] == other.bytes[..] - } -} - -impl Eq for Signature {} From d7613165798295bb6f5cf4bac1cbc38c03be8611 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 22:32:30 -0800 Subject: [PATCH 8/9] Implement verification. --- src/error.rs | 5 ++++- src/public_key.rs | 54 +++++++++++++++++++++++++++++++++++------------ src/secret_key.rs | 8 ++++++- src/signature.rs | 1 - 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/error.rs b/src/error.rs index 617773a..d1f2842 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,7 @@ use thiserror::Error; /// An error related to RedJubJub signatures. -#[derive(Error, Debug)] +#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)] pub enum Error { /// The encoding of a secret key was malformed. #[error("Malformed secret key encoding.")] @@ -9,4 +9,7 @@ pub enum Error { /// The encoding of a public key was malformed. #[error("Malformed public key encoding.")] MalformedPublicKey, + /// Signature verification failed. + #[error("Invalid signature.")] + InvalidSignature, } diff --git a/src/public_key.rs b/src/public_key.rs index 4bbcadb..026c74d 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -75,22 +75,50 @@ impl PublicKey { pub fn randomize(&self, randomizer: Randomizer) -> PublicKey { unimplemented!(); } -} -impl PublicKey { - /// Verify a Zcash `BindingSig` over `msg` made by this public key. + /// Verify a purported `signature` over `msg` made by this public key. // This is similar to impl signature::Verifier but without boxed errors pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { - // this lets us specialize the basepoint parameter, could call a verify_inner - unimplemented!(); - } -} + use crate::HStar; -impl PublicKey { - /// Verify a Zcash `SpendAuthSig` over `msg` made by this public key. - // This is similar to impl signature::Verifier but without boxed errors - pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { - // this lets us specialize the basepoint parameter, could call a verify_inner - unimplemented!(); + let r = { + // XXX-jubjub: should not use CtOption here + // XXX-jubjub: inconsistent ownership in from_bytes + let maybe_point = jubjub::AffinePoint::from_bytes(signature.r_bytes); + if maybe_point.is_some().into() { + jubjub::ExtendedPoint::from(maybe_point.unwrap()) + } else { + return Err(Error::InvalidSignature); + } + }; + + let s = { + // XXX-jubjub: should not use CtOption here + let maybe_scalar = Scalar::from_bytes(&signature.s_bytes); + if maybe_scalar.is_some().into() { + maybe_scalar.unwrap() + } else { + return Err(Error::InvalidSignature); + } + }; + + let c = HStar::default() + .update(&signature.r_bytes[..]) + .update(&self.bytes.bytes[..]) // XXX ugly + .update(msg) + .finalize(); + + // 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) + } } } diff --git a/src/secret_key.rs b/src/secret_key.rs index 031c803..ee4c3b6 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -13,6 +13,12 @@ pub struct SecretKey { pk: PublicKey, } +impl<'a, T: SigType> From<&'a SecretKey> for PublicKey { + fn from(sk: &'a SecretKey) -> PublicKey { + sk.pk.clone() + } +} + impl From> for [u8; 32] { fn from(sk: SecretKey) -> [u8; 32] { sk.sk.to_bytes() @@ -79,7 +85,7 @@ impl SecretKey { let s_bytes = (&nonce + &(&c * &self.sk)).to_bytes(); - Signature{ + Signature { r_bytes, s_bytes, _marker: PhantomData, diff --git a/src/signature.rs b/src/signature.rs index 6351676..8035955 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -32,4 +32,3 @@ impl From> for [u8; 64] { bytes } } - From 8bcfeae920480e637f1b6031f3d8e4723250be3b Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 3 Dec 2019 22:33:27 -0800 Subject: [PATCH 9/9] Add a basic test. This ran into problems with Clone/Copy bounds -- it seems like the derived impls require that the phantom type T also be Clone / Copy / Debug for the type to be. This commit does a hacky fix that makes it work for now, but it should be cleaned up later. --- Cargo.toml | 3 +++ src/lib.rs | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d5d0dcc..a17c9c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,5 +10,8 @@ thiserror = "1.0" blake2b_simd = "0.5" jubjub = { git = "https://github.com/zkcrypto/jubjub", rev = "e83f7d2bd136498a27f9d943fea635d8682bf2c6" } +[dev-dependencies] +rand = "0.7" + [features] nightly = [] diff --git a/src/lib.rs b/src/lib.rs index ec359c4..ca9fabd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,16 +39,18 @@ pub use signature::Signature; pub trait SigType: private::Sealed {} /// A type variable corresponding to Zcash's `BindingSig`. +#[derive(Copy, Clone, Debug)] pub struct Binding {} impl SigType for Binding {} /// A type variable corresponding to Zcash's `SpendAuthSig`. +#[derive(Copy, Clone, Debug)] pub struct SpendAuth {} impl SigType for SpendAuth {} pub(crate) mod private { use super::*; - pub trait Sealed { + pub trait Sealed: Copy + Clone + std::fmt::Debug { fn basepoint() -> jubjub::ExtendedPoint; } impl Sealed for Binding { @@ -66,3 +68,18 @@ pub(crate) mod private { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sign_and_verify() { + let sk = SecretKey::::new(rand::thread_rng()); + let msg = b"test"; + let sig = sk.sign(rand::thread_rng(), msg); + let pk = PublicKey::from(&sk); + + assert_eq!(pk.verify(msg, &sig), Ok(())); + } +}