@@ -55,21 +55,25 @@ pub trait CertificationScheme {
5555 /// The signature type produced by this scheme
5656 type Signature : Clone + core:: fmt:: Debug ;
5757
58+ /// The output produced by successful verification
59+ type Output : Clone + core:: fmt:: Debug ;
60+
5861 /// Sign the AggKeygenInput with the given keypair
5962 fn certify ( & self , keypair : & KeyPair , agg_input : & encpedpop:: AggKeygenInput ) -> Self :: Signature ;
6063
61- /// Verify a certification signature
64+ /// Verify a certification signature and return the output
6265 fn verify_cert (
6366 & self ,
6467 cert_key : Point ,
6568 agg_input : & encpedpop:: AggKeygenInput ,
6669 signature : & Self :: Signature ,
67- ) -> bool ;
70+ ) -> Option < Self :: Output > ;
6871}
6972
7073/// Standard Schnorr (BIP340) implementation of the CertificationScheme trait
7174impl < H : Hash32 , NG : NonceGen > CertificationScheme for Schnorr < H , NG > {
7275 type Signature = crate :: Signature ;
76+ type Output = ( ) ;
7377
7478 fn certify ( & self , keypair : & KeyPair , agg_input : & encpedpop:: AggKeygenInput ) -> Self :: Signature {
7579 let cert_bytes = agg_input. cert_bytes ( ) ;
@@ -83,11 +87,15 @@ impl<H: Hash32, NG: NonceGen> CertificationScheme for Schnorr<H, NG> {
8387 cert_key : Point ,
8488 agg_input : & encpedpop:: AggKeygenInput ,
8589 signature : & Self :: Signature ,
86- ) -> bool {
90+ ) -> Option < Self :: Output > {
8791 let cert_bytes = agg_input. cert_bytes ( ) ;
8892 let message = crate :: Message :: new ( "BIP DKG/cert" , cert_bytes. as_ref ( ) ) ;
8993 let cert_key_even_y = cert_key. into_point_with_even_y ( ) . 0 ;
90- self . verify ( & cert_key_even_y, message, signature)
94+ if self . verify ( & cert_key_even_y, message, signature) {
95+ Some ( ( ) )
96+ } else {
97+ None
98+ }
9199 }
92100}
93101
@@ -97,29 +105,20 @@ pub mod vrf_cert {
97105 use super :: * ;
98106 use vrf_fun:: VrfProof ;
99107
100- /// A wrapper type for using VRF as a certification scheme
101- pub struct VrfCertifier < V > {
102- /// The underlying VRF instance
103- pub vrf : V ,
104- }
105-
106- /// Create a VRF certification scheme with the RFC 9381 SSWU suite using SHA256
107- pub fn sswu_vrf_sha256 ( ) -> VrfCertifier < vrf_fun:: Rfc9381SswuVrf < sha2:: Sha256 > > {
108- VrfCertifier {
109- vrf : vrf_fun:: Rfc9381SswuVrf :: default ( ) ,
110- }
111- }
108+ /// VRF certification scheme using SSWU VRF
109+ pub struct VrfCertifier ;
112110
113- /// Create a VRF certification scheme with the RFC 9381 TAI suite using SHA256
114- pub fn tai_vrf_sha256 ( ) -> VrfCertifier < vrf_fun :: Rfc9381TaiVrf < sha2 :: Sha256 > > {
115- VrfCertifier {
116- vrf : vrf_fun :: Rfc9381TaiVrf :: default ( ) ,
117- }
111+ /// The output from VRF verification containing the gamma point
112+ # [ derive ( Clone , Debug ) ]
113+ pub struct VrfOutput {
114+ /// The VRF output point (gamma)
115+ pub gamma : Point ,
118116 }
119117
120- /// Implement CertificationScheme for VrfCertifier wrapping the SSWU VRF
121- impl CertificationScheme for VrfCertifier < vrf_fun :: Rfc9381SswuVrf < sha2 :: Sha256 > > {
118+ /// Implement CertificationScheme for VrfCertifier
119+ impl CertificationScheme for VrfCertifier {
122120 type Signature = VrfProof ;
121+ type Output = VrfOutput ;
123122
124123 fn certify (
125124 & self ,
@@ -136,67 +135,15 @@ pub mod vrf_cert {
136135 cert_key : Point ,
137136 agg_input : & encpedpop:: AggKeygenInput ,
138137 signature : & Self :: Signature ,
139- ) -> bool {
140- // Use the certification bytes as the VRF input
141- let cert_bytes = agg_input. cert_bytes ( ) ;
142- vrf_fun:: rfc9381:: sswu:: verify :: < sha2:: Sha256 > ( cert_key, & cert_bytes, signature)
143- . is_some ( )
144- }
145- }
146-
147- /// Implement CertificationScheme for VrfCertifier wrapping the TAI VRF
148- impl CertificationScheme for VrfCertifier < vrf_fun:: Rfc9381TaiVrf < sha2:: Sha256 > > {
149- type Signature = VrfProof ;
150-
151- fn certify (
152- & self ,
153- keypair : & KeyPair ,
154- agg_input : & encpedpop:: AggKeygenInput ,
155- ) -> Self :: Signature {
156- // Use the certification bytes as the VRF input
157- let cert_bytes = agg_input. cert_bytes ( ) ;
158- vrf_fun:: rfc9381:: tai:: prove :: < sha2:: Sha256 > ( keypair, & cert_bytes)
159- }
160-
161- fn verify_cert (
162- & self ,
163- cert_key : Point ,
164- agg_input : & encpedpop:: AggKeygenInput ,
165- signature : & Self :: Signature ,
166- ) -> bool {
138+ ) -> Option < Self :: Output > {
167139 // Use the certification bytes as the VRF input
168140 let cert_bytes = agg_input. cert_bytes ( ) ;
169- vrf_fun:: rfc9381:: tai:: verify :: < sha2:: Sha256 > ( cert_key, & cert_bytes, signature)
170- . is_some ( )
171- }
172- }
173-
174- /// Compute a randomness beacon from a set of VRF outputs
175- ///
176- /// This function takes all the VRF outputs (gamma points) from the certificate
177- /// and hashes them together to produce unpredictable randomness that no single
178- /// party could have controlled (as long as at least one party is honest).
179- pub fn compute_randomness_beacon < S > ( certificate : & certpedpop:: Certificate < S > ) -> [ u8 ; 32 ]
180- where
181- S : CertificationScheme ,
182- S :: Signature : AsRef < VrfProof > ,
183- {
184- use sha2:: { Digest , Sha256 } ;
185-
186- let mut hasher = Sha256 :: new ( ) ;
187-
188- // Sort by public key to ensure deterministic ordering
189- let mut sorted_entries: Vec < _ > = certificate. iter ( ) . collect ( ) ;
190- sorted_entries. sort_by_key ( |( pk, _) | pk. to_bytes ( ) ) ;
191-
192- // Hash all the VRF gamma points
193- for ( _, vrf_proof) in sorted_entries {
194- let gamma = vrf_proof. as_ref ( ) . gamma ;
195- hasher. update ( gamma. to_bytes ( ) ) ;
141+ vrf_fun:: rfc9381:: sswu:: verify :: < sha2:: Sha256 > ( cert_key, & cert_bytes, signature) . map (
142+ |output| VrfOutput {
143+ gamma : output. gamma ,
144+ } ,
145+ )
196146 }
197-
198- // Get the hash output
199- hasher. finalize ( ) . into ( )
200147 }
201148}
202149
@@ -1166,8 +1113,37 @@ pub mod certpedpop {
11661113 pub type KeygenInput = encpedpop:: KeygenInput ;
11671114 /// Key generation inputs after being aggregated by the coordinator
11681115 pub type AggKeygenInput = encpedpop:: AggKeygenInput ;
1169- /// The certification signatures from each certifying party (both contributors and share receivers).
1170- pub type Certificate < S > = BTreeMap < Point , <S as CertificationScheme >:: Signature > ;
1116+ /// A certificate containing signatures or proofs from certifying parties
1117+ #[ derive( Clone , Debug ) ]
1118+ pub struct Certificate < Sig > ( BTreeMap < Point , Sig > ) ;
1119+
1120+ impl < Sig > Default for Certificate < Sig > {
1121+ fn default ( ) -> Self {
1122+ Self ( BTreeMap :: new ( ) )
1123+ }
1124+ }
1125+
1126+ impl < Sig > Certificate < Sig > {
1127+ /// Create a new empty certificate
1128+ pub fn new ( ) -> Self {
1129+ Self :: default ( )
1130+ }
1131+
1132+ /// Insert a signature/proof for a public key
1133+ pub fn insert ( & mut self , key : Point , sig : Sig ) {
1134+ self . 0 . insert ( key, sig) ;
1135+ }
1136+
1137+ /// Get the signature/proof for a public key
1138+ pub fn get ( & self , key : & Point ) -> Option < & Sig > {
1139+ self . 0 . get ( key)
1140+ }
1141+
1142+ /// Iterate over all entries in the certificate
1143+ pub fn iter ( & self ) -> impl Iterator < Item = ( & Point , & Sig ) > {
1144+ self . 0 . iter ( )
1145+ }
1146+ }
11711147
11721148 impl Contributor {
11731149 /// Generates the keygen input for a party at `my_index`. Note that `my_index`
@@ -1215,7 +1191,9 @@ pub mod certpedpop {
12151191 #[ derive( Clone , Debug ) ]
12161192 pub struct CertifiedKeygen < S : CertificationScheme > {
12171193 input : AggKeygenInput ,
1218- certificate : Certificate < S > ,
1194+ certificate : Certificate < S :: Signature > ,
1195+ /// The outputs from successful verification, indexed by certifying party's public key
1196+ outputs : BTreeMap < Point , S :: Output > ,
12191197 }
12201198
12211199 impl < S : CertificationScheme > CertifiedKeygen < S > {
@@ -1233,7 +1211,10 @@ pub mod certpedpop {
12331211 . certificate
12341212 . get ( & cert_key)
12351213 . ok_or ( "I haven't certified this keygen" ) ?;
1236- if !cert_scheme. verify_cert ( cert_key, & self . input , my_cert) {
1214+ if cert_scheme
1215+ . verify_cert ( cert_key, & self . input , my_cert)
1216+ . is_none ( )
1217+ {
12371218 return Err ( "my certification was invalid" ) ;
12381219 }
12391220 self . input . recover_share :: < H > ( share_index, & keypair)
@@ -1243,6 +1224,37 @@ pub mod certpedpop {
12431224 pub fn inner ( & self ) -> & AggKeygenInput {
12441225 & self . input
12451226 }
1227+
1228+ /// Gets the certificate.
1229+ pub fn certificate ( & self ) -> & Certificate < S :: Signature > {
1230+ & self . certificate
1231+ }
1232+
1233+ /// Gets the verification outputs.
1234+ pub fn outputs ( & self ) -> & BTreeMap < Point , S :: Output > {
1235+ & self . outputs
1236+ }
1237+ }
1238+
1239+ #[ cfg( feature = "vrf_cert_keygen" ) ]
1240+ impl CertifiedKeygen < vrf_cert:: VrfCertifier > {
1241+ /// Compute a randomness beacon from the VRF outputs
1242+ ///
1243+ /// This function hashes all the VRF gamma points together to produce
1244+ /// unpredictable randomness that no single party could have controlled
1245+ /// (as long as at least one party is honest).
1246+ pub fn compute_randomness_beacon ( & self ) -> [ u8 ; 32 ] {
1247+ use sha2:: { Digest , Sha256 } ;
1248+
1249+ let mut hasher = Sha256 :: new ( ) ;
1250+
1251+ // BTreeMap already maintains sorted order by key
1252+ for output in self . outputs . values ( ) {
1253+ hasher. update ( output. gamma . to_bytes ( ) ) ;
1254+ }
1255+
1256+ hasher. finalize ( ) . into ( )
1257+ }
12461258 }
12471259
12481260 pub use encpedpop:: Coordinator ;
@@ -1289,28 +1301,31 @@ pub mod certpedpop {
12891301 pub fn finalize < S : CertificationScheme > (
12901302 self ,
12911303 cert_scheme : & S ,
1292- certificate : Certificate < S > ,
1304+ certificate : Certificate < S :: Signature > ,
12931305 contributor_keys : & [ Point ] ,
12941306 ) -> Result < ( CertifiedKeygen < S > , PairedSecretShare < Normal , Zero > ) , & ' static str > {
1307+ let mut outputs = BTreeMap :: new ( ) ;
12951308 let cert_keys = self
12961309 . agg_input
12971310 . encryption_keys ( )
12981311 . map ( |( _, encryption_key) | encryption_key)
12991312 . chain ( contributor_keys. iter ( ) . cloned ( ) ) ;
13001313 for cert_key in cert_keys {
13011314 match certificate. get ( & cert_key) {
1302- Some ( sig) => {
1303- if !cert_scheme . verify_cert ( cert_key , & self . agg_input , sig ) {
1304- return Err ( "certification signature was invalid" ) ;
1315+ Some ( sig) => match cert_scheme . verify_cert ( cert_key , & self . agg_input , sig ) {
1316+ Some ( output ) => {
1317+ outputs . insert ( cert_key , output ) ;
13051318 }
1306- }
1319+ None => return Err ( "certification signature was invalid" ) ,
1320+ } ,
13071321 None => return Err ( "missing certification signature" ) ,
13081322 }
13091323 }
13101324
13111325 let certified_keygen = CertifiedKeygen {
13121326 input : self . agg_input ,
13131327 certificate,
1328+ outputs,
13141329 } ;
13151330
13161331 Ok ( ( certified_keygen, self . paired_secret_share ) )
@@ -1377,8 +1392,7 @@ pub mod certpedpop {
13771392
13781393 // Apply fingerprint grinding
13791394 agg_input. grind_fingerprint :: < H > ( fingerprint) ;
1380-
1381- let mut certificate = BTreeMap :: default ( ) ;
1395+ let mut certificate = Certificate :: new ( ) ;
13821396
13831397 for ( contributor, keypair) in contributors. into_iter ( ) . zip ( contributor_keys. iter ( ) ) {
13841398 let sig = contributor
@@ -1402,9 +1416,18 @@ pub mod certpedpop {
14021416 share_receivers. push ( share_receiver) ;
14031417 }
14041418
1419+ // Collect outputs by verifying all certificates
1420+ let mut outputs = BTreeMap :: new ( ) ;
1421+ for ( key, sig) in certificate. iter ( ) {
1422+ if let Some ( output) = cert_scheme. verify_cert ( * key, & agg_input, sig) {
1423+ outputs. insert ( * key, output) ;
1424+ }
1425+ }
1426+
14051427 let certified_keygen = CertifiedKeygen {
14061428 input : agg_input. clone ( ) ,
14071429 certificate : certificate. clone ( ) ,
1430+ outputs,
14081431 } ;
14091432
14101433 for share_receiver in share_receivers {
@@ -1549,4 +1572,41 @@ mod test {
15491572 assert!( shared_key. check_fingerprint:: <sha2:: Sha256 >( & fingerprint) , "fingerprint was grinded correctly" ) ;
15501573 }
15511574 }
1575+
1576+ #[ test]
1577+ #[ cfg( feature = "vrf_cert_keygen" ) ]
1578+ fn vrf_certified_keygen_randomness_beacon ( ) {
1579+ use proptest:: test_runner:: { RngAlgorithm , TestRng } ;
1580+
1581+ let schnorr = crate :: new_with_deterministic_nonces :: < sha2:: Sha256 > ( ) ;
1582+ let vrf_certifier = vrf_cert:: VrfCertifier ;
1583+ let mut rng = TestRng :: deterministic_rng ( RngAlgorithm :: ChaCha ) ;
1584+
1585+ let threshold = 2 ;
1586+ let n_receivers = 3 ;
1587+ let n_generators = 2 ;
1588+
1589+ let ( certified_keygen, _) = certpedpop:: simulate_keygen (
1590+ & schnorr,
1591+ & vrf_certifier,
1592+ threshold,
1593+ n_receivers,
1594+ n_generators,
1595+ Fingerprint :: none ( ) ,
1596+ & mut rng,
1597+ ) ;
1598+
1599+ // Compute randomness beacon from the VRF outputs
1600+ let randomness = certified_keygen. compute_randomness_beacon ( ) ;
1601+
1602+ // Verify the randomness is deterministic
1603+ let randomness2 = certified_keygen. compute_randomness_beacon ( ) ;
1604+ assert_eq ! ( randomness, randomness2) ;
1605+
1606+ // Verify we have the expected number of VRF outputs
1607+ assert_eq ! (
1608+ certified_keygen. outputs( ) . len( ) ,
1609+ ( n_receivers + n_generators) as usize
1610+ ) ;
1611+ }
15521612}
0 commit comments