Skip to content

Commit 6723584

Browse files
committed
Add secp256r1_recover_pubkey tests
1 parent 9d236b3 commit 6723584

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

packages/crypto/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ pub use crate::errors::{CryptoError, CryptoResult};
2121
#[doc(hidden)]
2222
pub use crate::secp256k1::{secp256k1_recover_pubkey, secp256k1_verify};
2323
#[doc(hidden)]
24-
pub use crate::secp256r1::secp256r1_verify;
24+
pub use crate::secp256r1::{secp256r1_recover_pubkey, secp256r1_verify};
2525
pub(crate) use backtrace::BT;

packages/crypto/src/secp256r1.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,10 @@ fn check_pubkey(data: &[u8]) -> Result<(), InvalidSecp256r1PubkeyFormat> {
158158
#[cfg(test)]
159159
mod tests {
160160
use super::*;
161+
use std::fs::File;
162+
use std::io::BufReader;
161163

164+
use crate::secp256r1_recover_pubkey;
162165
use ecdsa::RecoveryId;
163166
use p256::{
164167
ecdsa::signature::DigestSigner, ecdsa::SigningKey, elliptic_curve::rand_core::OsRng,
@@ -188,6 +191,15 @@ mod tests {
188191
// Test data extracted from https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/digital-signatures
189192
const COSMOS_SECP256R1_TESTS_JSON: &str = "./testdata/secp256r1_tests.json";
190193

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+
191203
#[test]
192204
fn test_secp256r1_verify() {
193205
// Explicit / external hashing
@@ -310,4 +322,60 @@ mod tests {
310322
);
311323
}
312324
}
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+
}
313381
}

0 commit comments

Comments
 (0)