From ead4727dedffd5ed683dabd45fe89e8777bfae86 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 4 Dec 2019 17:00:55 -0800 Subject: [PATCH] Implement randomization. --- src/public_key.rs | 22 ++++++++++++++++------ src/secret_key.rs | 16 ++++++++++------ tests/proptests.rs | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/public_key.rs b/src/public_key.rs index a43ba6d..4484eaa 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -1,6 +1,6 @@ use std::{convert::TryFrom, marker::PhantomData}; -use crate::{Error, Randomizer, Scalar, SigType, Signature}; +use crate::{Error, Randomizer, Scalar, SigType, Signature, SpendAuth}; /// A refinement type for `[u8; 32]` indicating that the bytes represent /// an encoding of a RedJubJub public key. @@ -61,6 +61,21 @@ impl TryFrom> for PublicKey { } } +impl PublicKey { + /// Randomize this public key with the given `randomizer`. + /// + /// Randomization is only supported for `SpendAuth` keys. + pub fn randomize(&self, randomizer: &Randomizer) -> PublicKey { + use crate::private::Sealed; + let point = &self.point + &(&SpendAuth::basepoint() * randomizer); + let bytes = PublicKeyBytes { + bytes: jubjub::AffinePoint::from(&point).to_bytes(), + _marker: PhantomData, + }; + PublicKey { bytes, point } + } +} + impl PublicKey { pub(crate) fn from_secret(s: &Scalar) -> PublicKey { let point = &T::basepoint() * s; @@ -71,11 +86,6 @@ impl PublicKey { PublicKey { bytes, point } } - /// Randomize this public key with the given `randomizer`. - pub fn randomize(&self, _randomizer: Randomizer) -> PublicKey { - unimplemented!(); - } - /// 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> { diff --git a/src/secret_key.rs b/src/secret_key.rs index 763c2f1..7e14204 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use crate::{PublicKey, Randomizer, Scalar, SigType, Signature}; +use crate::{PublicKey, Randomizer, Scalar, SigType, Signature, SpendAuth}; use rand_core::{CryptoRng, RngCore}; @@ -37,6 +37,15 @@ impl From<[u8; 32]> for SecretKey { } } +impl SecretKey { + /// Randomize this public key with the given `randomizer`. + pub fn randomize(&self, randomizer: &Randomizer) -> SecretKey { + let sk = &self.sk + randomizer; + let pk = PublicKey::from_secret(&sk); + SecretKey { sk, pk } + } +} + impl SecretKey { /// Generate a new secret key. pub fn new(mut rng: R) -> SecretKey { @@ -49,11 +58,6 @@ impl SecretKey { SecretKey { sk, pk } } - /// Randomize this public key with the given `randomizer`. - pub fn randomize(&self, _randomizer: Randomizer) -> PublicKey { - unimplemented!(); - } - /// Create a signature of type `T` on `msg` using this `SecretKey`. // Similar to signature::Signer but without boxed errors. pub fn sign(&self, mut rng: R, msg: &[u8]) -> Signature { diff --git a/tests/proptests.rs b/tests/proptests.rs index e83ccac..f6bf9f0 100644 --- a/tests/proptests.rs +++ b/tests/proptests.rs @@ -96,18 +96,20 @@ fn tweak_strategy() -> impl Strategy { ] } +use rand_chacha::ChaChaRng; +use rand_core::SeedableRng; + proptest! { + #[test] fn tweak_signature( tweaks in prop::collection::vec(tweak_strategy(), (0,5)), rng_seed in any::(), ) { - use rand_core::SeedableRng; - // Use a deterministic RNG so that test failures can be reproduced. // Seeding with 64 bits of entropy is INSECURE and this code should // not be copied outside of this test! - let mut rng = rand_chacha::ChaChaRng::seed_from_u64(rng_seed); + let mut rng = ChaChaRng::seed_from_u64(rng_seed); // Create a test case for each signature type. let msg = b"test message for proptests"; @@ -123,4 +125,30 @@ proptest! { assert!(binding.check()); assert!(spendauth.check()); } + + #[test] + fn randomization_commutes_with_pubkey_homomorphism(rng_seed in any::()) { + // Use a deterministic RNG so that test failures can be reproduced. + // Seeding with 64 bits of entropy is INSECURE and this code should + // not be copied outside of this test! + let mut rng = ChaChaRng::seed_from_u64(rng_seed); + + let r = { + // XXX-jubjub: better API for this + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes[..]); + Randomizer::from_bytes_wide(&bytes) + }; + + let sk = SecretKey::::new(&mut rng); + let pk = PublicKey::from(&sk); + + let sk_r = sk.randomize(&r); + let pk_r = pk.randomize(&r); + + let pk_r_via_sk_rand: [u8; 32] = PublicKeyBytes::from(PublicKey::from(&sk_r)).into(); + let pk_r_via_pk_rand: [u8; 32] = PublicKeyBytes::from(pk_r).into(); + + assert_eq!(pk_r_via_pk_rand, pk_r_via_sk_rand); + } }