Account for non-canonical base in serde deserialization.

This commit is contained in:
Kris Nuttycombe 2021-06-24 09:57:35 -06:00
parent 78adc9c810
commit 90d0c254cf
1 changed files with 15 additions and 4 deletions

View File

@ -13,7 +13,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
use ff::{Field, PrimeField, PrimeFieldBits}; use ff::{Field, PrimeField, PrimeFieldBits};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use rand::RngCore; use rand::RngCore;
use serde::de::Deserializer; use serde::de::{Deserializer, Error};
use serde::ser::Serializer; use serde::ser::Serializer;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::iter; use std::iter;
@ -164,6 +164,11 @@ fn hash_with_l(l: usize, pair: Pair) -> CtOption<pallas::Base> {
/// A newtype wrapper for leaves and internal nodes in the Orchard /// A newtype wrapper for leaves and internal nodes in the Orchard
/// incremental note commitment tree. /// incremental note commitment tree.
///
/// This wraps a CtOption<pallas::Base> because Sinsemilla hashes
/// can produce a bottom value which needs to be accounted for in
/// the production of a Merkle root. Leaf nodes are always wrapped
/// with the `Some` constructor.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OrchardIncrementalTreeDigest(CtOption<pallas::Base>); pub struct OrchardIncrementalTreeDigest(CtOption<pallas::Base>);
@ -181,8 +186,12 @@ impl OrchardIncrementalTreeDigest {
/// Parses a incremental tree leaf digest from the bytes of /// Parses a incremental tree leaf digest from the bytes of
/// a note commitment. /// a note commitment.
pub fn from_bytes(bytes: &[u8; 32]) -> Self { ///
OrchardIncrementalTreeDigest(pallas::Base::from_bytes(bytes)) /// Returns the empty `CtOption` if the provided bytes represent
/// a non-canonical encoding.
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
pallas::Base::from_bytes(bytes)
.map(|b| OrchardIncrementalTreeDigest(CtOption::new(b, 1.into())))
} }
} }
@ -232,7 +241,9 @@ impl Serialize for OrchardIncrementalTreeDigest {
impl<'de> Deserialize<'de> for OrchardIncrementalTreeDigest { impl<'de> Deserialize<'de> for OrchardIncrementalTreeDigest {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let parsed = <[u8; 32]>::deserialize(deserializer)?; let parsed = <[u8; 32]>::deserialize(deserializer)?;
Ok(Self::from_bytes(&parsed)) <Option<_>>::from(Self::from_bytes(&parsed)).ok_or(Error::custom(
"Attempted to deserialize non-canonical representaion of a Pallas base field element.",
))
} }
} }