diff --git a/src/builder.rs b/src/builder.rs index 6ed10616..0165332e 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -154,7 +154,7 @@ impl ActionInfo { cv_net, SigningMetadata { dummy_ask: self.spend.dummy_sk.as_ref().map(SpendAuthorizingKey::from), - ak, + parts: SigningParts { ak, alpha }, }, ), Circuit {}, @@ -325,6 +325,16 @@ impl Builder { } } +/// The parts needed to sign an [`Action`]. +#[derive(Debug)] +pub struct SigningParts { + /// The spend validating key for this action. Used to match spend authorizing keys to + /// actions they can create signatures for. + ak: SpendValidatingKey, + /// The randomization needed to derive the actual signing key for this note. + alpha: pallas::Scalar, +} + /// Marker for an unauthorized bundle, with a proof but no signatures. #[derive(Debug)] pub struct Unauthorized { @@ -345,9 +355,7 @@ pub struct SigningMetadata { /// These keys are used automatically in [`Bundle::prepare`] or /// [`Bundle::apply_signatures`] to sign dummy spends. dummy_ask: Option, - /// The spend validating key for this action. Used to match spend authorizing keys to - /// actions they can create signatures for. - ak: SpendValidatingKey, + parts: SigningParts, } /// Marker for a partially-authorized bundle, in the process of being signed. @@ -359,7 +367,27 @@ pub struct PartiallyAuthorized { } impl Authorization for PartiallyAuthorized { - type SpendAuth = (Option>, SpendValidatingKey); + type SpendAuth = MaybeSigned; +} + +/// A heisen[`Signature`] for a particular [`Action`]. +/// +/// [`Signature`]: redpallas::Signature +#[derive(Debug)] +pub enum MaybeSigned { + /// The information needed to sign this [`Action`]. + SigningMetadata(SigningParts), + /// The signature for this [`Action`]. + Signature(redpallas::Signature), +} + +impl MaybeSigned { + fn finalize(self) -> Result, Error> { + match self { + Self::Signature(sig) => Ok(sig), + _ => Err(Error::MissingSignatures), + } + } } impl Bundle { @@ -373,12 +401,12 @@ impl Bundle { ) -> Bundle { self.authorize( &mut rng, - |rng, _, SigningMetadata { dummy_ask, ak }| { - ( - // We can create signatures for dummy spends immediately. - dummy_ask.map(|ask| ask.sign(rng, &sighash)), - ak, - ) + |rng, _, SigningMetadata { dummy_ask, parts }| { + // We can create signatures for dummy spends immediately. + dummy_ask + .map(|ask| ask.randomize(&parts.alpha).sign(rng, &sighash)) + .map(MaybeSigned::Signature) + .unwrap_or(MaybeSigned::SigningMetadata(parts)) }, |rng, unauth| PartiallyAuthorized { proof: unauth.proof, @@ -412,17 +440,11 @@ impl Bundle { let expected_ak = ask.into(); self.authorize( &mut rng, - |rng, partial, (sig, ak)| { - ( - sig.or_else(|| { - if ak == expected_ak { - Some(ask.sign(rng, &partial.sighash)) - } else { - None - } - }), - ak, - ) + |rng, partial, maybe| match maybe { + MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => { + MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(rng, &partial.sighash)) + } + s => s, }, |_, partial| partial, ) @@ -434,10 +456,7 @@ impl Bundle { pub fn finalize(self) -> Result, Error> { self.try_authorize( &mut (), - |_, _, (sig, _)| match sig { - Some(sig) => Ok(sig), - None => Err(Error::MissingSignatures), - }, + |_, _, maybe| maybe.finalize(), |_, partial| { Ok(Authorized::from_parts( partial.proof, @@ -594,12 +613,12 @@ mod tests { builder .add_recipient(None, recipient, NoteValue::from_raw(5000), None) .unwrap(); - let bundle: Bundle = dbg!(builder + let bundle: Bundle = builder .build(&mut rng, &pk) .unwrap() - .prepare(&mut rng, [0; 32])) - .finalize() - .unwrap(); + .prepare(&mut rng, [0; 32]) + .finalize() + .unwrap(); assert_eq!(bundle.value_balance(), &(-5000)) } } diff --git a/src/keys.rs b/src/keys.rs index 03f56bc9..63890442 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -8,7 +8,7 @@ use fpe::ff1::{BinaryNumeralString, FF1}; use group::GroupEncoding; use halo2::arithmetic::FieldExt; use pasta_curves::pallas; -use rand::{CryptoRng, RngCore}; +use rand::RngCore; use subtle::CtOption; use crate::{ @@ -76,13 +76,11 @@ impl SpendAuthorizingKey { to_scalar(PrfExpand::OrchardAsk.expand(&sk.0)) } - /// Creates a spend authorization signature over the given message. - pub fn sign( - &self, - rng: R, - msg: &[u8], - ) -> redpallas::Signature { - self.0.sign(rng, msg) + /// Randomizes this spend authorizing key with the given `randomizer`. + /// + /// The resulting key can be used to actually sign a spend. + pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::SigningKey { + self.0.randomize(randomizer) } } diff --git a/src/primitives/redpallas.rs b/src/primitives/redpallas.rs index e03529aa..db4d88c8 100644 --- a/src/primitives/redpallas.rs +++ b/src/primitives/redpallas.rs @@ -40,6 +40,15 @@ impl TryFrom<[u8; 32]> for SigningKey { } } +impl SigningKey { + /// Randomizes this signing key with the given `randomizer`. + /// + /// Randomization is only supported for `SpendAuth` keys. + pub fn randomize(&self, randomizer: &pallas::Scalar) -> Self { + SigningKey(self.0.randomize(randomizer)) + } +} + impl SigningKey { /// Creates a signature of type `T` on `msg` using this `SigningKey`. pub fn sign(&self, rng: R, msg: &[u8]) -> Signature {