1use rand_core::OsRng;
20
21use p256::ecdsa::signature::{Signer as _, Verifier as _};
24
25use crate::asymmetric::KeyPairHex;
26use crate::contract::{Identified, Signer};
27use crate::error::{Error, Result};
28use crate::internal::{hex_decode, hex_encode};
29
30macro_rules! ecdsa_signer {
32 ($(#[$doc:meta])* $name:ident, $label:literal, $identifier:literal, $curve:ident) => {
33 $(#[$doc])*
34 #[derive(Debug, Default, Clone, Copy)]
35 pub struct $name;
36
37 impl $name {
38 pub const IDENTIFIER: &'static str = $identifier;
39
40 pub fn new() -> Self {
41 Self
42 }
43
44 fn sign_hex(message: &[u8], private_key_hex: &str) -> Result<String> {
46 let private_bytes = hex_decode(private_key_hex)
47 .map_err(|_| Error::InvalidKey { label: $label })?;
48 let signing_key = $curve::ecdsa::SigningKey::from_slice(&private_bytes)
49 .map_err(|_| Error::InvalidKey { label: $label })?;
50 let signature: $curve::ecdsa::Signature = signing_key
53 .try_sign(message)
54 .map_err(|_| Error::SignFailed { label: $label })?;
55 let signature = signature.normalize_s().unwrap_or(signature);
57 Ok(hex_encode(signature.to_bytes().as_slice()))
58 }
59
60 fn verify_hex(
63 message: &[u8],
64 signature_hex: &str,
65 public_key_hex: &str,
66 ) -> Result<()> {
67 let public_bytes = hex_decode(public_key_hex)
68 .map_err(|_| Error::VerificationFailed { label: $label })?;
69 let verifying_key = $curve::ecdsa::VerifyingKey::from_sec1_bytes(&public_bytes)
70 .map_err(|_| Error::VerificationFailed { label: $label })?;
71 let signature_bytes = hex_decode(signature_hex)
72 .map_err(|_| Error::VerificationFailed { label: $label })?;
73 let signature = $curve::ecdsa::Signature::from_slice(&signature_bytes)
74 .map_err(|_| Error::VerificationFailed { label: $label })?;
75 verifying_key
76 .verify(message, &signature)
77 .map_err(|_| Error::VerificationFailed { label: $label })
78 }
79
80 pub fn generate_key_pair_hex() -> Result<KeyPairHex> {
83 let signing_key = $curve::ecdsa::SigningKey::random(&mut OsRng);
84 let public_key = signing_key.verifying_key().to_encoded_point(false);
85 Ok(KeyPairHex {
86 private_key_hex: hex_encode(signing_key.to_bytes().as_slice()),
87 public_key_hex: hex_encode(public_key.as_bytes()),
88 })
89 }
90 }
91
92 impl Identified for $name {
93 fn identifier(&self) -> &str {
94 Self::IDENTIFIER
95 }
96 }
97
98 impl Signer for $name {
99 fn sign(&self, message: &[u8], private_key_hex: &str) -> Result<String> {
100 Self::sign_hex(message, private_key_hex)
101 }
102
103 fn verify(
104 &self,
105 message: &[u8],
106 signature_hex: &str,
107 public_key_hex: &str,
108 ) -> Result<()> {
109 Self::verify_hex(message, signature_hex, public_key_hex)
110 }
111 }
112 };
113}
114
115ecdsa_signer!(
116 EcdsaP256Signer,
118 "ECDSA P-256",
119 "ecdsa-p256-sha256",
120 p256
121);
122
123ecdsa_signer!(
124 EcdsaP384Signer,
126 "ECDSA P-384",
127 "ecdsa-p384-sha384",
128 p384
129);
130
131ecdsa_signer!(
132 EcdsaSecp256k1Signer,
134 "ECDSA secp256k1",
135 "ecdsa-secp256k1-sha256",
136 k256
137);
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 fn assert_roundtrip(signer: &dyn Signer, pair: &KeyPairHex, other: &KeyPairHex) {
145 assert_eq!(
146 pair.private_key_hex.len() * 2 + 2,
147 pair.public_key_hex.len()
148 );
149 assert!(pair.public_key_hex.starts_with("04"));
150
151 let signature = signer.sign(b"payload", &pair.private_key_hex).unwrap();
152 assert_eq!(signature.len(), pair.private_key_hex.len() * 2);
153 signer
154 .verify(b"payload", &signature, &pair.public_key_hex)
155 .unwrap();
156
157 let error = signer
159 .verify(b"payloaD", &signature, &pair.public_key_hex)
160 .unwrap_err();
161 assert!(matches!(error, Error::VerificationFailed { .. }));
162
163 let error = signer
165 .verify(b"payload", &signature, &other.public_key_hex)
166 .unwrap_err();
167 assert!(matches!(error, Error::VerificationFailed { .. }));
168 }
169
170 #[test]
171 fn p256_roundtrip_and_hex_shapes() {
172 let pair = EcdsaP256Signer::generate_key_pair_hex().unwrap();
173 let other = EcdsaP256Signer::generate_key_pair_hex().unwrap();
174 assert_eq!(pair.private_key_hex.len(), 64);
175 assert_eq!(pair.public_key_hex.len(), 130);
176 assert_roundtrip(&EcdsaP256Signer::new(), &pair, &other);
177 }
178
179 #[test]
180 fn p384_roundtrip_and_hex_shapes() {
181 let pair = EcdsaP384Signer::generate_key_pair_hex().unwrap();
182 let other = EcdsaP384Signer::generate_key_pair_hex().unwrap();
183 assert_eq!(pair.private_key_hex.len(), 96);
184 assert_eq!(pair.public_key_hex.len(), 194);
185 assert_roundtrip(&EcdsaP384Signer::new(), &pair, &other);
186 }
187
188 #[test]
189 fn secp256k1_roundtrip_and_hex_shapes() {
190 let pair = EcdsaSecp256k1Signer::generate_key_pair_hex().unwrap();
191 let other = EcdsaSecp256k1Signer::generate_key_pair_hex().unwrap();
192 assert_eq!(pair.private_key_hex.len(), 64);
193 assert_eq!(pair.public_key_hex.len(), 130);
194 assert_roundtrip(&EcdsaSecp256k1Signer::new(), &pair, &other);
195 }
196
197 #[test]
200 fn rfc6979_p256_sample_matches_vector() {
201 const X: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
202 const PUBLIC_KEY: &str = concat!(
203 "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
204 "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
205 );
206 const EXPECTED: &str = concat!(
209 "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
210 "0834e36ad29a83bf2bc9385e491d6099c8fdf9d1ed67aa7ea5f51f93782857a9",
211 );
212 const VECTOR: &str = concat!(
214 "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
215 "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
216 );
217
218 let signer = EcdsaP256Signer::new();
219 let signature = signer.sign(b"sample", X).unwrap();
220 assert_eq!(signature, EXPECTED);
221
222 signer.verify(b"sample", &signature, PUBLIC_KEY).unwrap();
224 signer.verify(b"sample", VECTOR, PUBLIC_KEY).unwrap();
225 assert!(signer.verify(b"sampl3", &signature, PUBLIC_KEY).is_err());
226 }
227
228 #[test]
230 fn rfc6979_p256_test_message_matches_vector() {
231 const X: &str = "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
232 const EXPECTED: &str = concat!(
233 "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
234 "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
235 );
236
237 let signature = EcdsaP256Signer::new().sign(b"test", X).unwrap();
238 assert_eq!(signature, EXPECTED);
239 }
240
241 #[test]
243 fn rfc6979_p384_sample_matches_vector() {
244 const X: &str = concat!(
245 "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8",
246 "96d5724e4c70a825f872c9ea60d2edf5",
247 );
248 const EXPECTED: &str = concat!(
251 "94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c",
252 "81a648152e44acf96e36dd1e80fabe46",
253 "6610b514ea0e87315e01bf24d9fcec70ecf18bf5e69dbad9a727ea302393848f",
254 "b4f04c6cd0423fdc7169525094629eab",
255 );
256
257 let signature = EcdsaP384Signer::new().sign(b"sample", X).unwrap();
258 assert_eq!(signature, EXPECTED);
259 }
260
261 #[test]
266 fn rfc6979_secp256k1_satoshi_vector_matches() {
267 const X: &str = "0000000000000000000000000000000000000000000000000000000000000001";
268 const EXPECTED: &str = concat!(
269 "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8",
270 "2442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5",
271 );
272 const PUBLIC_KEY: &str = concat!(
274 "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
275 "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
276 );
277
278 let signer = EcdsaSecp256k1Signer::new();
279 let signature = signer.sign(b"Satoshi Nakamoto", X).unwrap();
280 assert_eq!(signature, EXPECTED);
281
282 signer
283 .verify(b"Satoshi Nakamoto", &signature, PUBLIC_KEY)
284 .unwrap();
285 assert!(
286 signer
287 .verify(b"Satoshi Nakamot0", &signature, PUBLIC_KEY)
288 .is_err()
289 );
290 }
291
292 #[test]
294 fn signatures_are_deterministic() {
295 let pair = EcdsaP256Signer::generate_key_pair_hex().unwrap();
296 let signer = EcdsaP256Signer::new();
297 assert_eq!(
298 signer.sign(b"same", &pair.private_key_hex).unwrap(),
299 signer.sign(b"same", &pair.private_key_hex).unwrap(),
300 );
301 }
302
303 #[test]
304 fn malformed_keys_are_rejected() {
305 let pair = EcdsaP256Signer::generate_key_pair_hex().unwrap();
306 let signer = EcdsaP256Signer::new();
307
308 assert_eq!(
310 signer.sign(b"x", "zz").unwrap_err(),
311 Error::InvalidKey {
312 label: "ECDSA P-256"
313 }
314 );
315 assert_eq!(
316 signer.sign(b"x", "abcd").unwrap_err(),
317 Error::InvalidKey {
318 label: "ECDSA P-256"
319 }
320 );
321 let signature = signer.sign(b"x", &pair.private_key_hex).unwrap();
323 assert_eq!(
324 signer.verify(b"x", &signature, "04deadbeef").unwrap_err(),
325 Error::VerificationFailed {
326 label: "ECDSA P-256"
327 }
328 );
329 assert_eq!(
330 signer.verify(b"x", "00", &pair.public_key_hex).unwrap_err(),
331 Error::VerificationFailed {
332 label: "ECDSA P-256"
333 }
334 );
335 }
336
337 #[test]
338 fn identifiers_are_stable() {
339 assert_eq!(EcdsaP256Signer::new().identifier(), "ecdsa-p256-sha256");
340 assert_eq!(EcdsaP384Signer::new().identifier(), "ecdsa-p384-sha384");
341 assert_eq!(
342 EcdsaSecp256k1Signer::new().identifier(),
343 "ecdsa-secp256k1-sha256"
344 );
345 }
346}