@@ -158,7 +158,10 @@ fn check_pubkey(data: &[u8]) -> Result<(), InvalidSecp256r1PubkeyFormat> {
158
158
#[ cfg( test) ]
159
159
mod tests {
160
160
use super :: * ;
161
+ use std:: fs:: File ;
162
+ use std:: io:: BufReader ;
161
163
164
+ use crate :: secp256r1_recover_pubkey;
162
165
use ecdsa:: RecoveryId ;
163
166
use p256:: {
164
167
ecdsa:: signature:: DigestSigner , ecdsa:: SigningKey , elliptic_curve:: rand_core:: OsRng ,
@@ -188,6 +191,15 @@ mod tests {
188
191
// Test data extracted from https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/digital-signatures
189
192
const COSMOS_SECP256R1_TESTS_JSON : & str = "./testdata/secp256r1_tests.json" ;
190
193
194
+ #[ derive( Deserialize , Debug ) ]
195
+ struct Encoded {
196
+ message : String ,
197
+ // message_hash: String,
198
+ signature : String ,
199
+ #[ serde( rename = "pubkey" ) ]
200
+ public_key : String ,
201
+ }
202
+
191
203
#[ test]
192
204
fn test_secp256r1_verify ( ) {
193
205
// Explicit / external hashing
@@ -310,4 +322,60 @@ mod tests {
310
322
) ;
311
323
}
312
324
}
325
+
326
+ #[ test]
327
+ fn secp256r1_recover_pubkey_works ( ) {
328
+ let file = File :: open ( crate :: secp256r1:: tests:: COSMOS_SECP256R1_TESTS_JSON ) . unwrap ( ) ;
329
+ let reader = BufReader :: new ( file) ;
330
+ let codes: Vec < crate :: secp256r1:: tests:: Encoded > = serde_json:: from_reader ( reader) . unwrap ( ) ;
331
+ for ( i, encoded) in ( 1 ..) . zip ( codes) {
332
+ let message = hex:: decode ( & encoded. message ) . unwrap ( ) ;
333
+ let signature = hex:: decode ( & encoded. signature ) . unwrap ( ) ;
334
+ let public_key = hex:: decode ( & encoded. public_key ) . unwrap ( ) ;
335
+ let message_hash = Sha256 :: digest ( message) ;
336
+
337
+ // Since the recovery param is missing in the test vectors, we try both 0 and 1
338
+ let try0 = secp256r1_recover_pubkey ( & message_hash, & signature, 0 ) ;
339
+ let try1 = secp256r1_recover_pubkey ( & message_hash, & signature, 1 ) ;
340
+ match ( try0, try1) {
341
+ ( Ok ( recovered0) , Ok ( recovered1) ) => {
342
+ // Got two different pubkeys. Without the recovery param, we don't know which one is the right one.
343
+ assert ! ( recovered0 == public_key || recovered1 == public_key)
344
+ } ,
345
+ ( Ok ( recovered) , Err ( _) ) => assert_eq ! ( recovered, public_key) ,
346
+ ( Err ( _) , Ok ( recovered) ) => assert_eq ! ( recovered, public_key) ,
347
+ ( Err ( _) , Err ( _) ) => panic ! ( "secp256r1_recover_pubkey failed (test case {i} in {COSMOS_SECP256R1_TESTS_JSON})" ) ,
348
+ }
349
+ }
350
+ }
351
+
352
+ #[ test]
353
+ fn secp256r1_recover_pubkey_fails_for_invalid_recovery_param ( ) {
354
+ let r_s = hex:: decode ( COSMOS_SECP256R1_SIGNATURE_HEX1 ) . unwrap ( ) ;
355
+ let message_hash = Sha256 :: digest ( hex:: decode ( COSMOS_SECP256R1_MSG_HEX1 ) . unwrap ( ) ) ;
356
+
357
+ // 2 and 3 are explicitly unsupported
358
+ let recovery_param: u8 = 2 ;
359
+ match secp256r1_recover_pubkey ( & message_hash, & r_s, recovery_param) . unwrap_err ( ) {
360
+ CryptoError :: InvalidRecoveryParam { .. } => { }
361
+ err => panic ! ( "Unexpected error: {err}" ) ,
362
+ }
363
+ let recovery_param: u8 = 3 ;
364
+ match secp256r1_recover_pubkey ( & message_hash, & r_s, recovery_param) . unwrap_err ( ) {
365
+ CryptoError :: InvalidRecoveryParam { .. } => { }
366
+ err => panic ! ( "Unexpected error: {err}" ) ,
367
+ }
368
+
369
+ // Other values are garbage
370
+ let recovery_param: u8 = 4 ;
371
+ match secp256r1_recover_pubkey ( & message_hash, & r_s, recovery_param) . unwrap_err ( ) {
372
+ CryptoError :: InvalidRecoveryParam { .. } => { }
373
+ err => panic ! ( "Unexpected error: {err}" ) ,
374
+ }
375
+ let recovery_param: u8 = 255 ;
376
+ match secp256r1_recover_pubkey ( & message_hash, & r_s, recovery_param) . unwrap_err ( ) {
377
+ CryptoError :: InvalidRecoveryParam { .. } => { }
378
+ err => panic ! ( "Unexpected error: {err}" ) ,
379
+ }
380
+ }
313
381
}
0 commit comments