builder: Store alpha and use it to derive rsk for signing spends

This was missed from zcash/orchard#49, but could not have caused a
consensus failure or loss-of-funds because `alpha` _was_ being sampled
and used to derive `rk`, meaning that the signatures would fail to
validate.
This commit is contained in:
Jack Grigg 2021-06-05 22:35:52 +01:00
parent cd1e72bbcd
commit cbf7c3825f
3 changed files with 60 additions and 34 deletions

View File

@ -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<Unauthorized>::prepare`] or
/// [`Bundle<Unauthorized>::apply_signatures`] to sign dummy spends.
dummy_ask: Option<SpendAuthorizingKey>,
/// 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<redpallas::Signature<SpendAuth>>, 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<SpendAuth>),
}
impl MaybeSigned {
fn finalize(self) -> Result<redpallas::Signature<SpendAuth>, Error> {
match self {
Self::Signature(sig) => Ok(sig),
_ => Err(Error::MissingSignatures),
}
}
}
impl<V> Bundle<Unauthorized, V> {
@ -373,12 +401,12 @@ impl<V> Bundle<Unauthorized, V> {
) -> Bundle<PartiallyAuthorized, V> {
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<V> Bundle<PartiallyAuthorized, V> {
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<V> Bundle<PartiallyAuthorized, V> {
pub fn finalize(self) -> Result<Bundle<Authorized, V>, 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,

View File

@ -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<R: RngCore + CryptoRng>(
&self,
rng: R,
msg: &[u8],
) -> redpallas::Signature<SpendAuth> {
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<SpendAuth> {
self.0.randomize(randomizer)
}
}

View File

@ -40,6 +40,15 @@ impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
}
}
impl SigningKey<SpendAuth> {
/// 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<T: SigType> SigningKey<T> {
/// Creates a signature of type `T` on `msg` using this `SigningKey`.
pub fn sign<R: RngCore + CryptoRng>(&self, rng: R, msg: &[u8]) -> Signature<T> {