k256/ecdsa.rs
1//! Elliptic Curve Digital Signature Algorithm (ECDSA).
2//!
3//! This module contains support for computing and verifying ECDSA signatures.
4//! To use it, you will need to enable one of the two following Cargo features:
5//!
6//! - `ecdsa-core`: provides only the [`Signature`] type (which represents an
7//! ECDSA/secp256k1 signature). Does not require the `arithmetic` feature.
8//! This is useful for 3rd-party crates which wish to use the `Signature`
9//! type for interoperability purposes (particularly in conjunction with the
10//! [`signature::Signer`] trait). Example use cases for this include other
11//! software implementations of ECDSA/secp256k1 and wrappers for cloud KMS
12//! services or hardware devices (HSM or crypto hardware wallet).
13//! - `ecdsa`: provides `ecdsa-core` features plus the [`SigningKey`] and
14//! [`VerifyingKey`] types which natively implement ECDSA/secp256k1 signing and
15//! verification.
16//!
17//! ## Signing/Verification Example
18//!
19#![cfg_attr(all(feature = "ecdsa", feature = "getrandom"), doc = "```")]
20#![cfg_attr(not(all(feature = "ecdsa", feature = "getrandom")), doc = "```ignore")]
21//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
22//! // NOTE: requires the `ecdsa` and `getrandom` crate features are enabled
23//! use k256::{
24//! ecdsa::{SigningKey, Signature, signature::Signer},
25//! elliptic_curve::Generate,
26//! SecretKey,
27//! };
28//!
29//! // Signing
30//! let signing_key = SigningKey::generate(); // Serialize with `::to_bytes()`
31//! let verifying_key_bytes = signing_key.verifying_key().to_sec1_point(true); // 33-bytes
32//!
33//! let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
34//! let signature: Signature = signing_key.sign(message);
35//!
36//! // Verification
37//! use k256::{Sec1Point, ecdsa::{VerifyingKey, signature::Verifier}};
38//!
39//! let verifying_key = VerifyingKey::from_sec1_bytes(verifying_key_bytes.as_ref())?;
40//! verifying_key.verify(message, &signature)?;
41//! # Ok(())
42//! # }
43//! ```
44//!
45//! ## Recovering [`VerifyingKey`] from [`Signature`]
46//!
47//! ECDSA makes it possible to recover the public key used to verify a
48//! signature with the assistance of 2-bits of additional information.
49//!
50//! This is helpful when there is already a trust relationship for a particular
51//! key, and it's desirable to omit the full public key used to sign a
52//! particular message.
53//!
54//! One common application of signature recovery with secp256k1 is Ethereum.
55//!
56//! ### Recovering a [`VerifyingKey`] from a signature
57//!
58//! ```
59//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
60//! use hex_literal::hex;
61//! use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
62//! use sha3::{Keccak256, Digest};
63//! use elliptic_curve::sec1::ToSec1Point;
64//!
65//! let msg = b"example message";
66//!
67//! let signature = Signature::try_from(hex!(
68//! "46c05b6368a44b8810d79859441d819b8e7cdc8bfd371e35c53196f4bcacdb51
69//! 35c7facce2a97b95eacba8a586d87b7958aaf8368ab29cee481f76e871dbd9cb"
70//! ).as_slice())?;
71//!
72//! let recid = RecoveryId::try_from(1u8)?;
73//!
74//! let recovered_key = VerifyingKey::recover_from_digest(
75//! Keccak256::new_with_prefix(msg),
76//! &signature,
77//! recid
78//! )?;
79//!
80//! let expected_key = VerifyingKey::from_sec1_bytes(
81//! &hex!("0200866db99873b09fc2fb1e3ba549b156e96d1a567e3284f5f0e859a83320cb8b")
82//! )?;
83//!
84//! assert_eq!(recovered_key, expected_key);
85//! # Ok(())
86//! # }
87//! ```
88
89pub use ecdsa_core::{
90 EcdsaCurve, RecoveryId,
91 signature::{self, Error},
92};
93
94use crate::Secp256k1;
95#[cfg(feature = "sha256")]
96use ecdsa_core::DigestAlgorithm;
97
98/// ECDSA/secp256k1 signature (fixed-size)
99pub type Signature = ecdsa_core::Signature<Secp256k1>;
100
101/// ECDSA/secp256k1 signature (ASN.1 DER encoded)
102pub type DerSignature = ecdsa_core::der::Signature<Secp256k1>;
103
104impl EcdsaCurve for Secp256k1 {
105 const NORMALIZE_S: bool = true;
106}
107
108/// ECDSA/secp256k1 signing key
109#[cfg(feature = "ecdsa")]
110pub type SigningKey = ecdsa_core::SigningKey<Secp256k1>;
111
112/// ECDSA/secp256k1 verification key (i.e. public key)
113#[cfg(feature = "ecdsa")]
114pub type VerifyingKey = ecdsa_core::VerifyingKey<Secp256k1>;
115
116#[cfg(feature = "sha256")]
117impl DigestAlgorithm for Secp256k1 {
118 type Digest = sha2::Sha256;
119}
120
121#[cfg(all(test, feature = "ecdsa", feature = "arithmetic"))]
122mod tests {
123 mod normalize {
124 use crate::ecdsa::Signature;
125
126 // Test vectors generated using rust-secp256k1
127 #[test]
128 #[rustfmt::skip]
129 fn s_high() {
130 let sig_hi = Signature::try_from([
131 0x20, 0xc0, 0x1a, 0x91, 0x0e, 0xbb, 0x26, 0x10,
132 0xaf, 0x2d, 0x76, 0x3f, 0xa0, 0x9b, 0x3b, 0x30,
133 0x92, 0x3c, 0x8e, 0x40, 0x8b, 0x11, 0xdf, 0x2c,
134 0x61, 0xad, 0x76, 0xd9, 0x70, 0xa2, 0xf1, 0xbc,
135 0xee, 0x2f, 0x11, 0xef, 0x8c, 0xb0, 0x0a, 0x49,
136 0x61, 0x7d, 0x13, 0x57, 0xf4, 0xd5, 0x56, 0x41,
137 0x09, 0x0a, 0x48, 0xf2, 0x01, 0xe9, 0xb9, 0x59,
138 0xc4, 0x8f, 0x6f, 0x6b, 0xec, 0x6f, 0x93, 0x8f,
139 ].as_slice()).unwrap();
140
141 let sig_lo = Signature::try_from([
142 0x20, 0xc0, 0x1a, 0x91, 0x0e, 0xbb, 0x26, 0x10,
143 0xaf, 0x2d, 0x76, 0x3f, 0xa0, 0x9b, 0x3b, 0x30,
144 0x92, 0x3c, 0x8e, 0x40, 0x8b, 0x11, 0xdf, 0x2c,
145 0x61, 0xad, 0x76, 0xd9, 0x70, 0xa2, 0xf1, 0xbc,
146 0x11, 0xd0, 0xee, 0x10, 0x73, 0x4f, 0xf5, 0xb6,
147 0x9e, 0x82, 0xec, 0xa8, 0x0b, 0x2a, 0xa9, 0xbd,
148 0xb1, 0xa4, 0x93, 0xf4, 0xad, 0x5e, 0xe6, 0xe1,
149 0xfb, 0x42, 0xef, 0x20, 0xe3, 0xc6, 0xad, 0xb2,
150 ].as_slice()).unwrap();
151
152 let sig_normalized = sig_hi.normalize_s();
153 assert_eq!(sig_lo, sig_normalized);
154 }
155
156 #[test]
157 fn s_low() {
158 #[rustfmt::skip]
159 let sig = Signature::try_from([
160 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164 ].as_slice()).unwrap();
165
166 assert_eq!(sig.normalize_s(), sig);
167 }
168 }
169
170 #[cfg(feature = "sha256")]
171 mod recovery {
172 use crate::{
173 Sec1Point,
174 ecdsa::{
175 RecoveryId, Signature, SigningKey, VerifyingKey, signature::hazmat::PrehashVerifier,
176 },
177 };
178 use hex_literal::hex;
179 use sha2::{Digest, Sha256};
180 use sha3::Keccak256;
181
182 /// Signature recovery test vectors
183 struct RecoveryTestVector {
184 pk: [u8; 33],
185 msg: &'static [u8],
186 sig: [u8; 64],
187 recid: RecoveryId,
188 }
189
190 const RECOVERY_TEST_VECTORS: &[RecoveryTestVector] = &[
191 // Recovery ID 0
192 RecoveryTestVector {
193 pk: hex!("021a7a569e91dbf60581509c7fc946d1003b60c7dee85299538db6353538d59574"),
194 msg: b"example message",
195 sig: hex!(
196 "ce53abb3721bafc561408ce8ff99c909f7f0b18a2f788649d6470162ab1aa032
197 3971edc523a6d6453f3fb6128d318d9db1a5ff3386feb1047d9816e780039d52"
198 ),
199 recid: RecoveryId::new(false, false),
200 },
201 // Recovery ID 1
202 RecoveryTestVector {
203 pk: hex!("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"),
204 msg: b"example message",
205 sig: hex!(
206 "46c05b6368a44b8810d79859441d819b8e7cdc8bfd371e35c53196f4bcacdb51
207 35c7facce2a97b95eacba8a586d87b7958aaf8368ab29cee481f76e871dbd9cb"
208 ),
209 recid: RecoveryId::new(true, false),
210 },
211 ];
212
213 #[test]
214 fn public_key_recovery() {
215 for vector in RECOVERY_TEST_VECTORS {
216 let digest = Sha256::new_with_prefix(vector.msg);
217 let sig = Signature::try_from(vector.sig.as_slice()).unwrap();
218 let recid = vector.recid;
219 let pk = VerifyingKey::recover_from_digest(digest, &sig, recid).unwrap();
220 assert_eq!(&vector.pk[..], Sec1Point::from(&pk).as_bytes());
221 }
222 }
223
224 /// End-to-end example which ensures RFC6979 is implemented in the same
225 /// way as other Ethereum libraries, using HMAC-DRBG-SHA-256 for RFC6979,
226 /// and Keccak256 for hashing the message.
227 ///
228 /// Test vectors adapted from:
229 /// <https://github.com/gakonst/ethers-rs/blob/ba00f549/ethers-signers/src/wallet/private_key.rs#L197>
230 #[test]
231 fn ethereum_end_to_end_example() {
232 let signing_key = SigningKey::from_bytes(
233 &hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318").into(),
234 )
235 .unwrap();
236
237 let msg = hex!(
238 "e9808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca0080018080"
239 );
240
241 let digest = Keccak256::new_with_prefix(msg);
242 let (sig, recid) = signing_key.sign_digest_recoverable(digest.clone());
243 assert_eq!(
244 sig.to_bytes().as_slice(),
245 &hex!(
246 "c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68"
247 )
248 );
249 assert_eq!(recid, RecoveryId::from_byte(0).unwrap());
250
251 let verifying_key =
252 VerifyingKey::recover_from_digest(digest.clone(), &sig, recid).unwrap();
253
254 assert_eq!(signing_key.verifying_key(), &verifying_key);
255 assert!(
256 verifying_key
257 .verify_prehash(&digest.finalize(), &sig)
258 .is_ok()
259 );
260 }
261 }
262
263 mod wycheproof {
264 use crate::{Sec1Point, Secp256k1};
265 use ecdsa_core::{Signature, signature::Verifier};
266 use elliptic_curve::array::typenum::Unsigned;
267
268 #[test]
269 fn wycheproof() {
270 // Build a field element but allow for too-short input (left pad with zeros)
271 // or too-long input (check excess leftmost bytes are zeros).
272 fn element_from_padded_slice<C: elliptic_curve::Curve>(
273 data: &[u8],
274 ) -> elliptic_curve::FieldBytes<C> {
275 let point_len = C::FieldBytesSize::USIZE;
276 if data.len() >= point_len {
277 let offset = data.len() - point_len;
278 for v in data.iter().take(offset) {
279 assert_eq!(*v, 0, "EcdsaVerifier: point too large");
280 }
281 elliptic_curve::FieldBytes::<C>::try_from(&data[offset..])
282 .expect("length mismatch")
283 } else {
284 let mut point = elliptic_curve::FieldBytes::<C>::default();
285 let offset = point_len - data.len();
286 point[offset..].copy_from_slice(data);
287 point
288 }
289 }
290
291 fn run_test(
292 wx: &[u8],
293 wy: &[u8],
294 msg: &[u8],
295 sig: &[u8],
296 pass: bool,
297 p1363_sig: bool,
298 ) -> Option<&'static str> {
299 let x = element_from_padded_slice::<Secp256k1>(wx);
300 let y = element_from_padded_slice::<Secp256k1>(wy);
301 let q_encoded =
302 Sec1Point::from_affine_coordinates(&x, &y, /* compress= */ false);
303 let verifying_key = ecdsa_core::VerifyingKey::from_sec1_point(&q_encoded).unwrap();
304
305 let sig = if p1363_sig {
306 match Signature::<Secp256k1>::from_slice(sig) {
307 Ok(s) => s.normalize_s(),
308 Err(_) if !pass => return None,
309 Err(_) => return Some("failed to parse signature P1363"),
310 }
311 } else {
312 match Signature::<Secp256k1>::from_der(sig) {
313 Ok(s) => s.normalize_s(),
314 Err(_) if !pass => return None,
315 Err(_) => return Some("failed to parse signature ASN.1"),
316 }
317 };
318
319 match verifying_key.verify(msg, &sig) {
320 Ok(_) if pass => None,
321 Ok(_) => Some("signature verify unexpectedly succeeded"),
322 Err(_) if !pass => None,
323 Err(_) => Some("signature verify failed"),
324 }
325 }
326
327 #[derive(Debug, Clone, Copy)]
328 struct TestVector {
329 /// X coordinates of the public key
330 pub wx: &'static [u8],
331 /// Y coordinates of the public key
332 pub wy: &'static [u8],
333 /// Payload to verify
334 pub msg: &'static [u8],
335 /// Der encoding of the signature
336 pub sig: &'static [u8],
337 /// Whether the signature should verify (`[1]`) or fail (`[0]`)
338 pub pass_: &'static [u8],
339 }
340
341 impl TestVector {
342 pub fn pass(&self) -> bool {
343 self.pass_[0] == 1
344 }
345 }
346
347 macro_rules! run_test {
348 ($blob: expr, $p1363_sig: expr) => {
349 {
350 ecdsa_core::dev::blobby::parse_into_structs!(
351 include_bytes!($blob);
352 static TEST_VECTORS: &[
353 TestVector { wx, wy, msg, sig, pass_ }
354 ];
355 );
356
357
358 for (i, tv) in TEST_VECTORS.iter().enumerate() {
359 if let Some(desc) = run_test(tv.wx, tv.wy, tv.msg, tv.sig, tv.pass(), $p1363_sig) {
360 panic!(
361 "\n\
362 Failed test №{}: {}\n\
363 wx:\t{:?}\n\
364 wy:\t{:?}\n\
365 msg:\t{:?}\n\
366 sig:\t{:?}\n\
367 pass:\t{}\n",
368 i,
369 desc,
370 hex::encode(tv.wx),
371 hex::encode(tv.wy),
372 hex::encode(tv.msg),
373 hex::encode(tv.sig),
374 tv.pass(),
375 );
376 }
377 }
378 }
379 }
380 }
381
382 run_test!(concat!("test_vectors/data/", "wycheproof", ".blb"), false);
383 run_test!(
384 concat!("test_vectors/data/", "wycheproof-p1316", ".blb"),
385 true
386 );
387 }
388 }
389}