Ensure SecretKey encoding is canonical.
This commit is contained in:
parent
80239164f2
commit
92cb99f966
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in New Issue