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//! Most users of this library who want to sign/verify signatures will want to
18//! enable the `ecdsa` and `sha256` Cargo features.
19//!
20//! ## Signing and Verifying Signatures
21//!
22//! This example requires the `ecdsa` and `sha256` Cargo features are enabled:
23//!
24//! ```
25//! # #[cfg(all(feature = "ecdsa", feature = "sha256"))]
26//! # {
27//! use k256::{
28//!     ecdsa::{SigningKey, Signature, signature::Signer},
29//!     SecretKey,
30//! };
31//! use rand_core::OsRng; // requires 'getrandom' feature
32//!
33//! // Signing
34//! let signing_key = SigningKey::random(&mut OsRng); // Serialize with `::to_bytes()`
35//! let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
36//!
37//! // Note: The signature type must be annotated or otherwise inferable as
38//! // `Signer` has many impls of the `Signer` trait (for both regular and
39//! // recoverable signature types).
40//! let signature: Signature = signing_key.sign(message);
41//!
42//! // Verification
43//! use k256::{EncodedPoint, ecdsa::{VerifyingKey, signature::Verifier}};
44//!
45//! let verifying_key = VerifyingKey::from(&signing_key); // Serialize with `::to_encoded_point()`
46//! assert!(verifying_key.verify(message, &signature).is_ok());
47//! # }
48//! ```
49//!
50//! ## Recovering [`VerifyingKey`] from [`Signature`]
51//!
52//! ECDSA makes it possible to recover the public key used to verify a
53//! signature with the assistance of 2-bits of additional information.
54//!
55//! This is helpful when there is already a trust relationship for a particular
56//! key, and it's desirable to omit the full public key used to sign a
57//! particular message.
58//!
59//! One common application of signature recovery with secp256k1 is Ethereum.
60//!
61//! ### Upgrading recoverable signature code from earlier versions of `k256`
62//!
63//! The v0.12 release of `k256` contains a brand new recoverable signature API
64//! from previous releases. Functionality has been upstreamed from `k256` to a
65//! generic implementation in the [`ecdsa`](`ecdsa_core`) crate.
66//!
67//! If you previously used `k256::ecdsa::recoverable::Signature`, the old
68//! functionality now uses a "detached" [`Signature`] and [`RecoveryId`].
69//! Here is where the various functionality went:
70//!
71//! - Signing now requires the use of the [`hazmat::SignPrimitive`] trait
72//!   (see examples immediately below).
73//! - Signature recovery is now implemented as methods of the [`VerifyingKey`]
74//!   type (i.e. `::recover_from_*`).
75//! - Trial recovery is now defined on the [`RecoveryId`] type
76//!   (i.e. `::trial_recovery_from_*`).
77//!
78//! ### Computing a signature with a [`RecoveryId`].
79//!
80//! This example shows how to compute a signature and its associated
81//! [`RecoveryId`] in a manner which is byte-for-byte compatible with
82//! Ethereum libraries, leveraging the [`SigningKey::sign_digest_recoverable`]
83//! API:
84//!
85#![cfg_attr(feature = "std", doc = "```")]
86#![cfg_attr(not(feature = "std"), doc = "```ignore")]
87//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
88//! use hex_literal::hex;
89//! use k256::ecdsa::{hazmat::SignPrimitive, RecoveryId, Signature, SigningKey};
90//! use sha2::Sha256;
91//! use sha3::{Keccak256, Digest};
92//!
93//! let signing_key = SigningKey::from_bytes(&hex!(
94//!     "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
95//! ).into())?;
96//!
97//! let msg = hex!("e9808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca0080018080");
98//! let digest = Keccak256::new_with_prefix(msg);
99//! let (signature, recid) = signing_key.sign_digest_recoverable(digest)?;
100//!
101//! assert_eq!(
102//!     signature.to_bytes().as_slice(),
103//!     &hex!("c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68")
104//! );
105//!
106//! assert_eq!(recid, RecoveryId::try_from(0u8).unwrap());
107//! # Ok(())
108//! # }
109//! ```
110//!
111//! ### Recovering a [`VerifyingKey`] from a signature
112//!
113#![cfg_attr(feature = "std", doc = "```")]
114#![cfg_attr(not(feature = "std"), doc = "```ignore")]
115//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
116//! use hex_literal::hex;
117//! use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
118//! use sha3::{Keccak256, Digest};
119//! use elliptic_curve::sec1::ToEncodedPoint;
120//!
121//! let msg = b"example message";
122//!
123//! let signature = Signature::try_from(hex!(
124//!     "46c05b6368a44b8810d79859441d819b8e7cdc8bfd371e35c53196f4bcacdb51
125//!      35c7facce2a97b95eacba8a586d87b7958aaf8368ab29cee481f76e871dbd9cb"
126//! ).as_slice())?;
127//!
128//! let recid = RecoveryId::try_from(1u8)?;
129//!
130//! let recovered_key = VerifyingKey::recover_from_digest(
131//!     Keccak256::new_with_prefix(msg),
132//!     &signature,
133//!     recid
134//! )?;
135//!
136//! let expected_key = VerifyingKey::from_sec1_bytes(
137//!     &hex!("0200866db99873b09fc2fb1e3ba549b156e96d1a567e3284f5f0e859a83320cb8b")
138//! )?;
139//!
140//! assert_eq!(recovered_key, expected_key);
141//! # Ok(())
142//! # }
143//! ```
144
145pub use ecdsa_core::{
146    signature::{self, Error},
147    RecoveryId,
148};
149
150#[cfg(any(feature = "ecdsa", feature = "sha256"))]
151pub use ecdsa_core::hazmat;
152
153use crate::Secp256k1;
154
155#[cfg(feature = "ecdsa")]
156use {
157    crate::{AffinePoint, FieldBytes, Scalar},
158    ecdsa_core::hazmat::{SignPrimitive, VerifyPrimitive},
159    elliptic_curve::{ops::Invert, scalar::IsHigh, subtle::CtOption},
160};
161
162/// ECDSA/secp256k1 signature (fixed-size)
163pub type Signature = ecdsa_core::Signature<Secp256k1>;
164
165/// ECDSA/secp256k1 signature (ASN.1 DER encoded)
166pub type DerSignature = ecdsa_core::der::Signature<Secp256k1>;
167
168/// ECDSA/secp256k1 signing key
169#[cfg(feature = "ecdsa")]
170pub type SigningKey = ecdsa_core::SigningKey<Secp256k1>;
171
172/// ECDSA/secp256k1 verification key (i.e. public key)
173#[cfg(feature = "ecdsa")]
174pub type VerifyingKey = ecdsa_core::VerifyingKey<Secp256k1>;
175
176#[cfg(feature = "sha256")]
177impl hazmat::DigestPrimitive for Secp256k1 {
178    type Digest = sha2::Sha256;
179}
180
181#[cfg(feature = "ecdsa")]
182impl SignPrimitive<Secp256k1> for Scalar {
183    #[allow(non_snake_case, clippy::many_single_char_names)]
184    fn try_sign_prehashed<K>(
185        &self,
186        k: K,
187        z: &FieldBytes,
188    ) -> Result<(Signature, Option<RecoveryId>), Error>
189    where
190        K: AsRef<Self> + Invert<Output = CtOption<Self>>,
191    {
192        let (sig, recid) = hazmat::sign_prehashed::<Secp256k1, K>(self, k, z)?;
193        let is_y_odd = recid.is_y_odd() ^ bool::from(sig.s().is_high());
194        let sig_low = sig.normalize_s().unwrap_or(sig);
195        let recid = RecoveryId::new(is_y_odd, recid.is_x_reduced());
196        Ok((sig_low, Some(recid)))
197    }
198}
199
200#[cfg(feature = "ecdsa")]
201impl VerifyPrimitive<Secp256k1> for AffinePoint {
202    fn verify_prehashed(&self, z: &FieldBytes, sig: &Signature) -> Result<(), Error> {
203        if sig.s().is_high().into() {
204            return Err(Error::new());
205        }
206
207        hazmat::verify_prehashed(&self.into(), z, sig)
208    }
209}
210
211#[cfg(all(test, feature = "ecdsa", feature = "arithmetic"))]
212mod tests {
213    mod normalize {
214        use crate::ecdsa::Signature;
215
216        // Test vectors generated using rust-secp256k1
217        #[test]
218        #[rustfmt::skip]
219        fn s_high() {
220            let sig_hi = Signature::try_from([
221                0x20, 0xc0, 0x1a, 0x91, 0x0e, 0xbb, 0x26, 0x10,
222                0xaf, 0x2d, 0x76, 0x3f, 0xa0, 0x9b, 0x3b, 0x30,
223                0x92, 0x3c, 0x8e, 0x40, 0x8b, 0x11, 0xdf, 0x2c,
224                0x61, 0xad, 0x76, 0xd9, 0x70, 0xa2, 0xf1, 0xbc,
225                0xee, 0x2f, 0x11, 0xef, 0x8c, 0xb0, 0x0a, 0x49,
226                0x61, 0x7d, 0x13, 0x57, 0xf4, 0xd5, 0x56, 0x41,
227                0x09, 0x0a, 0x48, 0xf2, 0x01, 0xe9, 0xb9, 0x59,
228                0xc4, 0x8f, 0x6f, 0x6b, 0xec, 0x6f, 0x93, 0x8f,
229            ].as_slice()).unwrap();
230
231            let sig_lo = Signature::try_from([
232                0x20, 0xc0, 0x1a, 0x91, 0x0e, 0xbb, 0x26, 0x10,
233                0xaf, 0x2d, 0x76, 0x3f, 0xa0, 0x9b, 0x3b, 0x30,
234                0x92, 0x3c, 0x8e, 0x40, 0x8b, 0x11, 0xdf, 0x2c,
235                0x61, 0xad, 0x76, 0xd9, 0x70, 0xa2, 0xf1, 0xbc,
236                0x11, 0xd0, 0xee, 0x10, 0x73, 0x4f, 0xf5, 0xb6,
237                0x9e, 0x82, 0xec, 0xa8, 0x0b, 0x2a, 0xa9, 0xbd,
238                0xb1, 0xa4, 0x93, 0xf4, 0xad, 0x5e, 0xe6, 0xe1,
239                0xfb, 0x42, 0xef, 0x20, 0xe3, 0xc6, 0xad, 0xb2,
240            ].as_slice()).unwrap();
241
242            let sig_normalized = sig_hi.normalize_s().unwrap();
243            assert_eq!(sig_lo, sig_normalized);
244        }
245
246        #[test]
247        fn s_low() {
248            #[rustfmt::skip]
249            let sig = Signature::try_from([
250                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
251                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
252                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
253                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
254            ].as_slice()).unwrap();
255
256            assert_eq!(sig.normalize_s(), None);
257        }
258    }
259
260    #[cfg(feature = "sha256")]
261    mod recovery {
262        use crate::{
263            ecdsa::{signature::DigestVerifier, RecoveryId, Signature, SigningKey, VerifyingKey},
264            EncodedPoint,
265        };
266        use hex_literal::hex;
267        use sha2::{Digest, Sha256};
268        use sha3::Keccak256;
269
270        /// Signature recovery test vectors
271        struct RecoveryTestVector {
272            pk: [u8; 33],
273            msg: &'static [u8],
274            sig: [u8; 64],
275            recid: RecoveryId,
276        }
277
278        const RECOVERY_TEST_VECTORS: &[RecoveryTestVector] = &[
279            // Recovery ID 0
280            RecoveryTestVector {
281                pk: hex!("021a7a569e91dbf60581509c7fc946d1003b60c7dee85299538db6353538d59574"),
282                msg: b"example message",
283                sig: hex!(
284                    "ce53abb3721bafc561408ce8ff99c909f7f0b18a2f788649d6470162ab1aa032
285                     3971edc523a6d6453f3fb6128d318d9db1a5ff3386feb1047d9816e780039d52"
286                ),
287                recid: RecoveryId::new(false, false),
288            },
289            // Recovery ID 1
290            RecoveryTestVector {
291                pk: hex!("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"),
292                msg: b"example message",
293                sig: hex!(
294                    "46c05b6368a44b8810d79859441d819b8e7cdc8bfd371e35c53196f4bcacdb51
295                     35c7facce2a97b95eacba8a586d87b7958aaf8368ab29cee481f76e871dbd9cb"
296                ),
297                recid: RecoveryId::new(true, false),
298            },
299        ];
300
301        #[test]
302        fn public_key_recovery() {
303            for vector in RECOVERY_TEST_VECTORS {
304                let digest = Sha256::new_with_prefix(vector.msg);
305                let sig = Signature::try_from(vector.sig.as_slice()).unwrap();
306                let recid = vector.recid;
307                let pk = VerifyingKey::recover_from_digest(digest, &sig, recid).unwrap();
308                assert_eq!(&vector.pk[..], EncodedPoint::from(&pk).as_bytes());
309            }
310        }
311
312        /// End-to-end example which ensures RFC6979 is implemented in the same
313        /// way as other Ethereum libraries, using HMAC-DRBG-SHA-256 for RFC6979,
314        /// and Keccak256 for hashing the message.
315        ///
316        /// Test vectors adapted from:
317        /// <https://github.com/gakonst/ethers-rs/blob/ba00f549/ethers-signers/src/wallet/private_key.rs#L197>
318        #[test]
319        fn ethereum_end_to_end_example() {
320            let signing_key = SigningKey::from_bytes(
321                &hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318").into(),
322            )
323            .unwrap();
324
325            let msg = hex!(
326                "e9808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca0080018080"
327            );
328            let digest = Keccak256::new_with_prefix(msg);
329
330            let (sig, recid) = signing_key.sign_digest_recoverable(digest.clone()).unwrap();
331            assert_eq!(
332                sig.to_bytes().as_slice(),
333                &hex!("c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68")
334            );
335            assert_eq!(recid, RecoveryId::from_byte(0).unwrap());
336
337            let verifying_key =
338                VerifyingKey::recover_from_digest(digest.clone(), &sig, recid).unwrap();
339
340            assert_eq!(signing_key.verifying_key(), &verifying_key);
341            assert!(verifying_key.verify_digest(digest, &sig).is_ok());
342        }
343    }
344
345    mod wycheproof {
346        use crate::{EncodedPoint, Secp256k1};
347        use ecdsa_core::{signature::Verifier, Signature};
348        use elliptic_curve::generic_array::typenum::Unsigned;
349
350        #[test]
351        fn wycheproof() {
352            use blobby::Blob5Iterator;
353
354            // Build a field element but allow for too-short input (left pad with zeros)
355            // or too-long input (check excess leftmost bytes are zeros).
356            fn element_from_padded_slice<C: elliptic_curve::Curve>(
357                data: &[u8],
358            ) -> elliptic_curve::FieldBytes<C> {
359                let point_len = C::FieldBytesSize::USIZE;
360                if data.len() >= point_len {
361                    let offset = data.len() - point_len;
362                    for v in data.iter().take(offset) {
363                        assert_eq!(*v, 0, "EcdsaVerifier: point too large");
364                    }
365                    elliptic_curve::FieldBytes::<C>::clone_from_slice(&data[offset..])
366                } else {
367                    let iter = core::iter::repeat(0)
368                        .take(point_len - data.len())
369                        .chain(data.iter().cloned());
370                    elliptic_curve::FieldBytes::<C>::from_exact_iter(iter).unwrap()
371                }
372            }
373
374            fn run_test(
375                wx: &[u8],
376                wy: &[u8],
377                msg: &[u8],
378                sig: &[u8],
379                pass: bool,
380            ) -> Option<&'static str> {
381                let x = element_from_padded_slice::<Secp256k1>(wx);
382                let y = element_from_padded_slice::<Secp256k1>(wy);
383                let q_encoded =
384                    EncodedPoint::from_affine_coordinates(&x, &y, /* compress= */ false);
385                let verifying_key =
386                    ecdsa_core::VerifyingKey::from_encoded_point(&q_encoded).unwrap();
387
388                let sig = match Signature::<Secp256k1>::from_der(sig) {
389                    Ok(s) => s.normalize_s().unwrap_or(s),
390                    Err(_) if !pass => return None,
391                    Err(_) => return Some("failed to parse signature ASN.1"),
392                };
393
394                match verifying_key.verify(msg, &sig) {
395                    Ok(_) if pass => None,
396                    Ok(_) => Some("signature verify unexpectedly succeeded"),
397                    Err(_) if !pass => None,
398                    Err(_) => Some("signature verify failed"),
399                }
400            }
401
402            let data = include_bytes!(concat!("test_vectors/data/", "wycheproof", ".blb"));
403
404            for (i, row) in Blob5Iterator::new(data).unwrap().enumerate() {
405                let [wx, wy, msg, sig, status] = row.unwrap();
406                let pass = match status[0] {
407                    0 => false,
408                    1 => true,
409                    _ => panic!("invalid value for pass flag"),
410                };
411                if let Some(desc) = run_test(wx, wy, msg, sig, pass) {
412                    panic!(
413                        "\n\
414                                 Failed test №{}: {}\n\
415                                 wx:\t{:?}\n\
416                                 wy:\t{:?}\n\
417                                 msg:\t{:?}\n\
418                                 sig:\t{:?}\n\
419                                 pass:\t{}\n",
420                        i, desc, wx, wy, msg, sig, pass,
421                    );
422                }
423            }
424        }
425    }
426}