Ensure that derived esk is non-zero

There's a (negligble) chance that we could generate (or be sent
adversarially) a RandomSeed which derives esk == 0. It's not hard to
detect and reject, in order to satisfy the type system.
This commit is contained in:
Jack Grigg 2021-06-02 23:14:13 +01:00
parent 8a7ff1b28a
commit 11350339f5
2 changed files with 31 additions and 16 deletions

View File

@ -558,7 +558,7 @@ mod tests {
*,
};
use crate::{
note::{ExtractedNoteCommitment, Nullifier},
note::{ExtractedNoteCommitment, Nullifier, RandomSeed},
value::NoteValue,
Note,
};
@ -625,7 +625,7 @@ mod tests {
addr,
NoteValue::from_raw(tv.note_v),
rho,
tv.note_rseed.into(),
RandomSeed::from_bytes(tv.note_rseed, &rho).unwrap(),
);
let cmx: ExtractedNoteCommitment = note.commitment().into();

View File

@ -6,7 +6,7 @@ use subtle::CtOption;
use crate::{
keys::{FullViewingKey, SpendingKey},
spec::{to_base, to_scalar, PrfExpand},
spec::{to_base, to_scalar, NonZeroPallasScalar, PrfExpand},
value::NoteValue,
Address,
};
@ -21,17 +21,22 @@ pub use self::nullifier::Nullifier;
#[derive(Clone, Debug)]
pub(crate) struct RandomSeed([u8; 32]);
impl From<[u8; 32]> for RandomSeed {
fn from(rseed: [u8; 32]) -> Self {
RandomSeed(rseed)
}
}
impl RandomSeed {
pub(crate) fn random(rng: &mut impl RngCore) -> Self {
let mut bytes = [0; 32];
rng.fill_bytes(&mut bytes);
RandomSeed(bytes)
pub(crate) fn random(rng: &mut impl RngCore, rho: &Nullifier) -> Self {
loop {
let mut bytes = [0; 32];
rng.fill_bytes(&mut bytes);
let rseed = RandomSeed::from_bytes(bytes, rho);
if rseed.is_some().into() {
break rseed.unwrap();
}
}
}
pub(crate) fn from_bytes(rseed: [u8; 32], rho: &Nullifier) -> CtOption<Self> {
let rseed = RandomSeed(rseed);
let esk = rseed.esk_inner(rho);
CtOption::new(rseed, esk.is_some())
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
@ -44,8 +49,18 @@ impl RandomSeed {
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn esk(&self, rho: &Nullifier) -> pallas::Scalar {
to_scalar(PrfExpand::Esk.with_ad(&self.0, &rho.to_bytes()[..]))
fn esk_inner(&self, rho: &Nullifier) -> CtOption<NonZeroPallasScalar> {
NonZeroPallasScalar::from_scalar(to_scalar(
PrfExpand::Esk.with_ad(&self.0, &rho.to_bytes()[..]),
))
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
///
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
fn esk(&self, rho: &Nullifier) -> NonZeroPallasScalar {
// We can't construct a RandomSeed for which this unwrap fails.
self.esk_inner(rho).unwrap()
}
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
@ -108,7 +123,7 @@ impl Note {
recipient,
value,
rho,
rseed: RandomSeed::random(&mut rng),
rseed: RandomSeed::random(&mut rng, &rho),
};
if note.commitment_inner().is_some().into() {
break note;