Ensure SecretKey encoding is canonical.

This commit is contained in:
Henry de Valence 2019-12-09 12:08:13 -08:00
parent 80239164f2
commit 92cb99f966
2 changed files with 45 additions and 34 deletions

View File

@ -1,13 +1,16 @@
use std::marker::PhantomData; use std::{
convert::{TryFrom, TryInto},
marker::PhantomData,
};
use crate::{PublicKey, Randomizer, Scalar, SigType, Signature, SpendAuth}; use crate::{Error, PublicKey, Randomizer, Scalar, SigType, Signature, SpendAuth};
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
/// A RedJubJub secret key. /// A RedJubJub secret key.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(from = "SerdeHelper"))] #[cfg_attr(feature = "serde", serde(try_from = "SerdeHelper"))]
#[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))] #[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))]
#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))] #[cfg_attr(feature = "serde", serde(bound = "T: SigType"))]
pub struct SecretKey<T: SigType> { pub struct SecretKey<T: SigType> {
@ -27,26 +30,30 @@ impl<T: SigType> From<SecretKey<T>> for [u8; 32] {
} }
} }
impl<T: SigType> From<[u8; 32]> for SecretKey<T> { impl<T: SigType> TryFrom<[u8; 32]> for SecretKey<T> {
fn from(bytes: [u8; 32]) -> Self { type Error = Error;
let sk = {
// XXX-jubjub: would be nice to unconditionally deser fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
// This incantation ensures deserialization is infallible. // XXX-jubjub: this should not use CtOption
let mut wide = [0; 64]; let maybe_sk = Scalar::from_bytes(&bytes);
wide[0..32].copy_from_slice(&bytes); if maybe_sk.is_some().into() {
Scalar::from_bytes_wide(&wide) let sk = maybe_sk.unwrap();
}; let pk = PublicKey::from_secret(&sk);
let pk = PublicKey::from_secret(&sk); Ok(SecretKey { sk, pk })
SecretKey { sk, pk } } else {
Err(Error::MalformedSecretKey)
}
} }
} }
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
struct SerdeHelper([u8; 32]); struct SerdeHelper([u8; 32]);
impl<T: SigType> From<SerdeHelper> for SecretKey<T> { impl<T: SigType> TryFrom<SerdeHelper> for SecretKey<T> {
fn from(helper: SerdeHelper) -> Self { type Error = Error;
helper.0.into()
fn try_from(helper: SerdeHelper) -> Result<Self, Self::Error> {
helper.0.try_into()
} }
} }

View File

@ -9,26 +9,30 @@ proptest! {
fn secretkey_serialization( fn secretkey_serialization(
bytes in prop::array::uniform32(any::<u8>()), bytes in prop::array::uniform32(any::<u8>()),
) { ) {
let sk_from = SecretKey::<SpendAuth>::from(bytes); let sk_result_from = SecretKey::<SpendAuth>::try_from(bytes);
let sk_bincode: SecretKey::<SpendAuth> let sk_result_bincode: Result<SecretKey::<SpendAuth>, _>
= bincode::deserialize(&bytes[..]).unwrap(); = bincode::deserialize(&bytes[..]);
// Check 1: both decoding methods should have the same public key // Check 1: both decoding methods should agree
let pk_bytes_from = PublicKeyBytes::from(PublicKey::from(&sk_from)); match (sk_result_from, sk_result_bincode) {
let pk_bytes_bincode = PublicKeyBytes::from(PublicKey::from(&sk_bincode)); // Both agree on success
assert_eq!(pk_bytes_from, pk_bytes_bincode); (Ok(sk_from), Ok(sk_bincode)) => {
let pk_bytes_from = PublicKeyBytes::from(PublicKey::from(&sk_from));
let pk_bytes_bincode = PublicKeyBytes::from(PublicKey::from(&sk_bincode));
assert_eq!(pk_bytes_from, pk_bytes_bincode);
// The below tests fail because we do not require canonically-encoded secret keys. // Check 2: bincode encoding should match original bytes.
/* let bytes_bincode = bincode::serialize(&sk_from).unwrap();
assert_eq!(&bytes[..], &bytes_bincode[..]);
// Check 2: bincode encoding should match original bytes. // Check 3: From encoding should match original bytes.
let bytes_bincode = bincode::serialize(&sk_from).unwrap(); let bytes_from: [u8; 32] = sk_bincode.into();
assert_eq!(&bytes[..], &bytes_bincode[..]); assert_eq!(&bytes[..], &bytes_from[..]);
}
// Check 3: From encoding should match original bytes. // Both agree on failure
let bytes_from: [u8; 32] = sk_bincode.into(); (Err(_), Err(_)) => {},
assert_eq!(&bytes[..], &bytes_from[..]); _ => panic!("bincode and try_from do not agree"),
*/ }
} }
#[test] #[test]