diff --git a/src/issuance.rs b/src/issuance.rs index b36c2350..4686a3c4 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -5,7 +5,7 @@ use nonempty::NonEmpty; use rand::{CryptoRng, RngCore}; use std::fmt; -use crate::keys::IssuerValidatingKey; +use crate::keys::{IssuerAuthorizingKey, IssuerValidatingKey}; use crate::note::note_type::MAX_ASSET_DESCRIPTION_SIZE; use crate::note::{NoteType, Nullifier}; use crate::value::NoteValue; @@ -31,13 +31,39 @@ impl IssueAction { /// inject the `sighash` for signature into the bundle. pub fn prepare(self, sighash: [u8; 32]) -> IssueAction { - return IssueAction { + IssueAction { ik: self.ik, asset_desc: self.asset_desc, notes: self.notes, finalize: self.finalize, authorization: Prepared { sighash }, - }; + } + } +} + +impl IssueAction { + /// Sign the sighash using the provided `IssuerValidatingKey`. + /// + /// # Panics + /// + /// Panics if the provided `IssuerAuthorizingKey` does not match the action's `IssuerValidatingKey`. + pub fn sign( + self, + mut rng: R, + isk: &IssuerAuthorizingKey, + ) -> IssueAction { + let expected_ik: IssuerValidatingKey = (isk).into(); + assert_eq!(expected_ik, self.ik); + + IssueAction { + ik: self.ik, + asset_desc: self.asset_desc, + notes: self.notes, + finalize: self.finalize, + authorization: Signed { + signature: isk.sign(&mut rng, &self.authorization.sighash), + }, + } } } @@ -353,12 +379,7 @@ impl IssueBundle { Ok(()) } - /// Loads the sighash into this bundle, preparing it for signing. - /// - /// This API ensures that all signatures are created over the same sighash. - /// pub fn prepare( - // self, - // mut rng: R, + /// Loads the sighash into each action in the bundle, as preparation for signing. pub fn prepare(self, sighash: [u8; 32]) -> IssueBundle { IssueBundle { actions: self @@ -370,6 +391,24 @@ impl IssueBundle { } } +impl IssueBundle { + /// Sign all the relevant actions + pub fn sign( + self, + mut rng: R, + isk: &IssuerAuthorizingKey, + ) -> IssueBundle { + // todo: check ik fits isk + IssueBundle { + actions: self + .actions + .into_iter() + .map(|a| a.sign(&mut rng, isk)) + .collect(), + } + } +} + /// Errors produced during the issuance process #[derive(Debug, PartialEq, Eq)] pub enum Error { @@ -388,6 +427,7 @@ mod tests { }; use crate::value::NoteValue; use rand::rngs::OsRng; + use rand::RngCore; #[test] fn issue_bundle_basic() { @@ -555,69 +595,48 @@ mod tests { ) .unwrap(); - let fake_sighash = [1; 32]; + let mut fake_sighash = [0; 32]; + rng.fill_bytes(&mut fake_sighash); + let prepared = bundle.prepare(fake_sighash); - let action = prepared + let action = prepared.get_action(ik, String::from("Frost")).unwrap(); + assert_eq!(action.authorization().sighash, fake_sighash); + } + + #[test] + fn issue_bundle_sign() { + let mut rng = OsRng; + let sk = SpendingKey::random(&mut rng); + let isk: IssuerAuthorizingKey = (&sk).into(); + let ik: IssuerValidatingKey = (&isk).into(); + + let fvk = FullViewingKey::from(&sk); + let recipient = fvk.address_at(0u32, Scope::External); + + let mut bundle = IssueBundle::new(); + + bundle + .add_recipient( + ik.clone(), + String::from("Frost"), + recipient, + NoteValue::from_raw(5), + false, + rng, + ) + .unwrap(); + + let mut rnd_sighash = [0; 32]; + rng.fill_bytes(&mut rnd_sighash); + + let signed = bundle.prepare(rnd_sighash).sign(rng, &isk); + + let action = signed .get_action(ik.clone(), String::from("Frost")) .unwrap(); - let auth = action.authorization(); - assert_eq!(auth.sighash, fake_sighash); + + ik.verify(&rnd_sighash, &action.authorization.signature) + .expect("signature should be valid"); } } - -// mod tests { -// use rand::rngs::OsRng; -// -// use super::Builder; -// // use crate::keys::{IssuerAuthorizingKey, IssuerValidatingKey}; -// use crate::note::NoteType; -// use crate::{ -// bundle::{Authorized, Bundle, Flags}, -// circuit::ProvingKey, -// constants::MERKLE_DEPTH_ORCHARD, -// keys::{FullViewingKey, Scope, SpendingKey}, -// tree::EMPTY_ROOTS, -// value::NoteValue, -// }; -// use crate::builder::Builder; -// -// #[test] -// fn shielding_bundle() { -// let pk = ProvingKey::build(); -// let mut rng = OsRng; -// -// let sk = SpendingKey::random(&mut rng); -// let fvk = FullViewingKey::from(&sk); -// let recipient = fvk.address_at(0u32, Scope::External); -// -// let mut builder = Builder::new( -// Flags::from_parts(true, true), -// EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(), -// ); -// -// builder -// .add_recipient( -// None, -// recipient, -// NoteValue::from_raw(5000), -// NoteType::native(), -// None, -// ) -// .unwrap(); -// -// -// let bundle: Bundle = builder -// .build(&mut rng) -// .unwrap() -// .create_proof(&pk, &mut rng) -// .unwrap() -// .prepare(&mut rng, [0; 32]) -// .sign() -// .finalize() -// .unwrap(); -// assert_eq!(bundle.value_balance(), &(-5000)); -// -// verify_bundle(&bundle, &vk) -// } -// } diff --git a/src/keys.rs b/src/keys.rs index 0e495207..2f1e49a5 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -209,9 +209,7 @@ impl IssuerAuthorizingKey { to_scalar(PrfExpand::ZsaIsk.expand(&sk.0)) } - /// RXXXX - /// - /// XXXXX + /// Sign the provided message using the `IssuerAuthorizingKey`. pub fn sign( &self, rng: &mut (impl RngCore + CryptoRng), @@ -280,6 +278,15 @@ impl IssuerValidatingKey { .and_then(check_structural_validity) .map(IssuerValidatingKey) } + + /// Verifies a purported `signature` over `msg` made by this verification key. + pub fn verify( + &self, + msg: &[u8], + signature: &redpallas::Signature, + ) -> Result<(), reddsa::Error> { + self.0.verify(msg, signature) + } } /// A function to check structural validity of the validating keys for authorizing transfers and