fuel_crypto/secp256/backend/r1/
p256.rs1#[cfg(feature = "test-helpers")]
4use crate::secp256::signature_format::encode_signature;
5use crate::{
6 message::Message,
7 secp256::signature_format::decode_signature,
8 Error,
9};
10#[cfg(feature = "test-helpers")]
11use ecdsa::RecoveryId;
12use fuel_types::Bytes64;
13use p256::ecdsa::VerifyingKey;
14
15#[cfg(feature = "test-helpers")]
17pub fn sign_prehashed(
18 signing_key: &p256::ecdsa::SigningKey,
19 message: &Message,
20) -> Result<Bytes64, Error> {
21 let (signature, _) = signing_key
22 .sign_prehash_recoverable(&**message)
23 .map_err(|_| Error::FailedToSign)?;
24
25 let signature = signature.normalize_s().unwrap_or(signature);
26
27 let recid1 = RecoveryId::new(false, false);
34 let recid2 = RecoveryId::new(true, false);
35
36 let rec1 = VerifyingKey::recover_from_prehash(&**message, &signature, recid1);
37 let rec2 = VerifyingKey::recover_from_prehash(&**message, &signature, recid2);
38
39 let actual = signing_key.verifying_key();
40
41 let recovery_id = if rec1.map(|r| r == *actual).unwrap_or(false) {
42 recid1
43 } else if rec2.map(|r| r == *actual).unwrap_or(false) {
44 recid2
45 } else {
46 unreachable!("Invalid signature generated");
47 };
48
49 let recovery_id = recovery_id
50 .try_into()
51 .expect("reduced-x recovery ids are never generated");
52 Ok(Bytes64::from(encode_signature(
53 signature.to_bytes().into(),
54 recovery_id,
55 )))
56}
57
58#[cfg(feature = "test-helpers")]
61pub fn encode_pubkey(key: VerifyingKey) -> [u8; 64] {
62 let point = key.to_encoded_point(false);
63 let mut result = [0u8; 64];
64 result[..32].copy_from_slice(point.x().unwrap());
65 result[32..].copy_from_slice(point.y().unwrap());
66 result
67}
68
69pub fn recover(signature: &Bytes64, message: &Message) -> Result<Bytes64, Error> {
72 let (sig, recid) = decode_signature(**signature);
73 let sig =
74 p256::ecdsa::Signature::from_slice(&sig).map_err(|_| Error::InvalidSignature)?;
75 let vk = VerifyingKey::recover_from_prehash(&**message, &sig, recid.into())
76 .map_err(|_| Error::InvalidSignature)?;
77 let point = vk.to_encoded_point(false);
78 let mut raw = Bytes64::zeroed();
79 raw[..32].copy_from_slice(point.x().unwrap());
80 raw[32..].copy_from_slice(point.y().unwrap());
81 Ok(raw)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 use p256::ecdsa::SigningKey;
89 use rand::{
90 rngs::StdRng,
91 Rng,
92 SeedableRng,
93 };
94
95 #[test]
96 fn test_raw_recover() {
97 let mut rng = &mut StdRng::seed_from_u64(1234);
98
99 let signing_key = SigningKey::random(&mut rng);
100 let verifying_key = signing_key.verifying_key();
101
102 let message = Message::new([rng.gen(); 100]);
103
104 let (signature, recovery_id) =
105 signing_key.sign_prehash_recoverable(&*message).unwrap();
106
107 let recovered =
108 VerifyingKey::recover_from_prehash(&*message, &signature, recovery_id)
109 .expect("Unable to recover the public key");
110
111 assert_eq!(recovered, *verifying_key);
112 }
113
114 #[test]
115 fn test_secp256r1_recover_from_msg() {
116 let mut rng = &mut StdRng::seed_from_u64(1234);
117
118 for _ in 0..100 {
119 let signing_key = SigningKey::random(&mut rng);
120 let verifying_key = signing_key.verifying_key();
121
122 let message = Message::new([rng.gen(); 100]);
123 let signature =
124 sign_prehashed(&signing_key, &message).expect("Couldn't sign");
125
126 let Ok(recovered) = recover(&signature, &message) else {
127 panic!("Failed to recover public key from the message");
128 };
129
130 assert_eq!(*recovered, encode_pubkey(*verifying_key));
131 }
132 }
133
134 #[test]
135 fn test_signature_and_recovery_id_encoding_roundtrip() {
136 let mut rng = &mut StdRng::seed_from_u64(1234);
137
138 for _ in 0..100 {
139 let message = Message::new([rng.gen(); 100]);
140 let signing_key = SigningKey::random(&mut rng);
141 let (signature, _) = signing_key.sign_prehash_recoverable(&*message).unwrap();
142 let signature = signature.normalize_s().unwrap_or(signature);
143 let signature: [u8; 64] = signature.to_bytes().into();
144
145 let recovery_id = RecoveryId::from_byte(0).unwrap().try_into().unwrap();
146 let encoded = encode_signature(signature, recovery_id);
147
148 let (de_sig, de_recid) = decode_signature(encoded);
149 assert_eq!(signature, de_sig);
150 assert_eq!(recovery_id, de_recid);
151
152 let recovery_id = RecoveryId::from_byte(1).unwrap().try_into().unwrap();
153 let encoded = encode_signature(signature, recovery_id);
154
155 let (de_sig, de_recid) = decode_signature(encoded);
156 assert_eq!(signature, de_sig);
157 assert_eq!(recovery_id, de_recid);
158 }
159 }
160}