diff --git a/src/keys.rs b/src/keys.rs index 3f75f728..e60f66e5 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -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(); diff --git a/src/note.rs b/src/note.rs index 18153f61..e6f57938 100644 --- a/src/note.rs +++ b/src/note.rs @@ -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 { + 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::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;