k256 0.14.0

secp256k1 elliptic curve library written in pure Rust with support for ECDSA signing/verification/public-key recovery, Taproot Schnorr signatures (BIP340), Elliptic Curve Diffie-Hellman (ECDH), and general-purpose secp256k1 elliptic curve group operations which can be used to implement arbitrary protocols
Documentation
//! Elliptic Curve Digital Signature Algorithm (ECDSA).
//!
//! This module contains support for computing and verifying ECDSA signatures.
//! To use it, you will need to enable one of the two following Cargo features:
//!
//! - `ecdsa-core`: provides only the [`Signature`] type (which represents an
//!   ECDSA/secp256k1 signature). Does not require the `arithmetic` feature.
//!   This is useful for 3rd-party crates which wish to use the `Signature`
//!   type for interoperability purposes (particularly in conjunction with the
//!   [`signature::Signer`] trait). Example use cases for this include other
//!   software implementations of ECDSA/secp256k1 and wrappers for cloud KMS
//!   services or hardware devices (HSM or crypto hardware wallet).
//! - `ecdsa`: provides `ecdsa-core` features plus the [`SigningKey`] and
//!   [`VerifyingKey`] types which natively implement ECDSA/secp256k1 signing and
//!   verification.
//!
//! ## Signing/Verification Example
//!
#![cfg_attr(all(feature = "ecdsa", feature = "getrandom"), doc = "```")]
#![cfg_attr(not(all(feature = "ecdsa", feature = "getrandom")), doc = "```ignore")]
//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
//! // NOTE: requires the `ecdsa` and `getrandom` crate features are enabled
//! use k256::{
//!     ecdsa::{SigningKey, Signature, signature::Signer},
//!     elliptic_curve::Generate,
//!     SecretKey,
//! };
//!
//! // Signing
//! let signing_key = SigningKey::generate(); // Serialize with `::to_bytes()`
//! let verifying_key_bytes = signing_key.verifying_key().to_sec1_point(true); // 33-bytes
//!
//! let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
//! let signature: Signature = signing_key.sign(message);
//!
//! // Verification
//! use k256::{Sec1Point, ecdsa::{VerifyingKey, signature::Verifier}};
//!
//! let verifying_key = VerifyingKey::from_sec1_bytes(verifying_key_bytes.as_ref())?;
//! verifying_key.verify(message, &signature)?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Recovering [`VerifyingKey`] from [`Signature`]
//!
//! ECDSA makes it possible to recover the public key used to verify a
//! signature with the assistance of 2-bits of additional information.
//!
//! This is helpful when there is already a trust relationship for a particular
//! key, and it's desirable to omit the full public key used to sign a
//! particular message.
//!
//! One common application of signature recovery with secp256k1 is Ethereum.
//!
//! ### Recovering a [`VerifyingKey`] from a signature
//!
//! ```
//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
//! use hex_literal::hex;
//! use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
//! use sha3::{Keccak256, Digest};
//! use elliptic_curve::sec1::ToSec1Point;
//!
//! let msg = b"example message";
//!
//! let signature = Signature::try_from(hex!(
//!     "46c05b6368a44b8810d79859441d819b8e7cdc8bfd371e35c53196f4bcacdb51
//!      35c7facce2a97b95eacba8a586d87b7958aaf8368ab29cee481f76e871dbd9cb"
//! ).as_slice())?;
//!
//! let recid = RecoveryId::try_from(1u8)?;
//!
//! let recovered_key = VerifyingKey::recover_from_digest(
//!     Keccak256::new_with_prefix(msg),
//!     &signature,
//!     recid
//! )?;
//!
//! let expected_key = VerifyingKey::from_sec1_bytes(
//!     &hex!("0200866db99873b09fc2fb1e3ba549b156e96d1a567e3284f5f0e859a83320cb8b")
//! )?;
//!
//! assert_eq!(recovered_key, expected_key);
//! # Ok(())
//! # }
//! ```

pub use ecdsa_core::{
    EcdsaCurve, RecoveryId,
    signature::{self, Error},
};

use crate::Secp256k1;
#[cfg(feature = "sha256")]
use ecdsa_core::DigestAlgorithm;

/// ECDSA/secp256k1 signature (fixed-size)
pub type Signature = ecdsa_core::Signature<Secp256k1>;

/// ECDSA/secp256k1 signature (ASN.1 DER encoded)
pub type DerSignature = ecdsa_core::der::Signature<Secp256k1>;

impl EcdsaCurve for Secp256k1 {
    const NORMALIZE_S: bool = true;
}

/// ECDSA/secp256k1 signing key
#[cfg(feature = "ecdsa")]
pub type SigningKey = ecdsa_core::SigningKey<Secp256k1>;

/// ECDSA/secp256k1 verification key (i.e. public key)
#[cfg(feature = "ecdsa")]
pub type VerifyingKey = ecdsa_core::VerifyingKey<Secp256k1>;

#[cfg(feature = "sha256")]
impl DigestAlgorithm for Secp256k1 {
    type Digest = sha2::Sha256;
}

#[cfg(all(test, feature = "ecdsa", feature = "arithmetic"))]
mod tests {
    mod normalize {
        use crate::ecdsa::Signature;

        // Test vectors generated using rust-secp256k1
        #[test]
        #[rustfmt::skip]
        fn s_high() {
            let sig_hi = Signature::try_from([
                0x20, 0xc0, 0x1a, 0x91, 0x0e, 0xbb, 0x26, 0x10,
                0xaf, 0x2d, 0x76, 0x3f, 0xa0, 0x9b, 0x3b, 0x30,
                0x92, 0x3c, 0x8e, 0x40, 0x8b, 0x11, 0xdf, 0x2c,
                0x61, 0xad, 0x76, 0xd9, 0x70, 0xa2, 0xf1, 0xbc,
                0xee, 0x2f, 0x11, 0xef, 0x8c, 0xb0, 0x0a, 0x49,
                0x61, 0x7d, 0x13, 0x57, 0xf4, 0xd5, 0x56, 0x41,
                0x09, 0x0a, 0x48, 0xf2, 0x01, 0xe9, 0xb9, 0x59,
                0xc4, 0x8f, 0x6f, 0x6b, 0xec, 0x6f, 0x93, 0x8f,
            ].as_slice()).unwrap();

            let sig_lo = Signature::try_from([
                0x20, 0xc0, 0x1a, 0x91, 0x0e, 0xbb, 0x26, 0x10,
                0xaf, 0x2d, 0x76, 0x3f, 0xa0, 0x9b, 0x3b, 0x30,
                0x92, 0x3c, 0x8e, 0x40, 0x8b, 0x11, 0xdf, 0x2c,
                0x61, 0xad, 0x76, 0xd9, 0x70, 0xa2, 0xf1, 0xbc,
                0x11, 0xd0, 0xee, 0x10, 0x73, 0x4f, 0xf5, 0xb6,
                0x9e, 0x82, 0xec, 0xa8, 0x0b, 0x2a, 0xa9, 0xbd,
                0xb1, 0xa4, 0x93, 0xf4, 0xad, 0x5e, 0xe6, 0xe1,
                0xfb, 0x42, 0xef, 0x20, 0xe3, 0xc6, 0xad, 0xb2,
            ].as_slice()).unwrap();

            let sig_normalized = sig_hi.normalize_s();
            assert_eq!(sig_lo, sig_normalized);
        }

        #[test]
        fn s_low() {
            #[rustfmt::skip]
            let sig = Signature::try_from([
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            ].as_slice()).unwrap();

            assert_eq!(sig.normalize_s(), sig);
        }
    }

    #[cfg(feature = "sha256")]
    mod recovery {
        use crate::{
            Sec1Point,
            ecdsa::{
                RecoveryId, Signature, SigningKey, VerifyingKey, signature::hazmat::PrehashVerifier,
            },
        };
        use hex_literal::hex;
        use sha2::{Digest, Sha256};
        use sha3::Keccak256;

        /// Signature recovery test vectors
        struct RecoveryTestVector {
            pk: [u8; 33],
            msg: &'static [u8],
            sig: [u8; 64],
            recid: RecoveryId,
        }

        const RECOVERY_TEST_VECTORS: &[RecoveryTestVector] = &[
            // Recovery ID 0
            RecoveryTestVector {
                pk: hex!("021a7a569e91dbf60581509c7fc946d1003b60c7dee85299538db6353538d59574"),
                msg: b"example message",
                sig: hex!(
                    "ce53abb3721bafc561408ce8ff99c909f7f0b18a2f788649d6470162ab1aa032
                     3971edc523a6d6453f3fb6128d318d9db1a5ff3386feb1047d9816e780039d52"
                ),
                recid: RecoveryId::new(false, false),
            },
            // Recovery ID 1
            RecoveryTestVector {
                pk: hex!("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2"),
                msg: b"example message",
                sig: hex!(
                    "46c05b6368a44b8810d79859441d819b8e7cdc8bfd371e35c53196f4bcacdb51
                     35c7facce2a97b95eacba8a586d87b7958aaf8368ab29cee481f76e871dbd9cb"
                ),
                recid: RecoveryId::new(true, false),
            },
        ];

        #[test]
        fn public_key_recovery() {
            for vector in RECOVERY_TEST_VECTORS {
                let digest = Sha256::new_with_prefix(vector.msg);
                let sig = Signature::try_from(vector.sig.as_slice()).unwrap();
                let recid = vector.recid;
                let pk = VerifyingKey::recover_from_digest(digest, &sig, recid).unwrap();
                assert_eq!(&vector.pk[..], Sec1Point::from(&pk).as_bytes());
            }
        }

        /// End-to-end example which ensures RFC6979 is implemented in the same
        /// way as other Ethereum libraries, using HMAC-DRBG-SHA-256 for RFC6979,
        /// and Keccak256 for hashing the message.
        ///
        /// Test vectors adapted from:
        /// <https://github.com/gakonst/ethers-rs/blob/ba00f549/ethers-signers/src/wallet/private_key.rs#L197>
        #[test]
        fn ethereum_end_to_end_example() {
            let signing_key = SigningKey::from_bytes(
                &hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318").into(),
            )
            .unwrap();

            let msg = hex!(
                "e9808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca0080018080"
            );

            let digest = Keccak256::new_with_prefix(msg);
            let (sig, recid) = signing_key.sign_digest_recoverable(digest.clone());
            assert_eq!(
                sig.to_bytes().as_slice(),
                &hex!(
                    "c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68"
                )
            );
            assert_eq!(recid, RecoveryId::from_byte(0).unwrap());

            let verifying_key =
                VerifyingKey::recover_from_digest(digest.clone(), &sig, recid).unwrap();

            assert_eq!(signing_key.verifying_key(), &verifying_key);
            assert!(
                verifying_key
                    .verify_prehash(&digest.finalize(), &sig)
                    .is_ok()
            );
        }
    }

    mod wycheproof {
        use crate::{Sec1Point, Secp256k1};
        use ecdsa_core::{Signature, signature::Verifier};
        use elliptic_curve::array::typenum::Unsigned;

        #[test]
        fn wycheproof() {
            // Build a field element but allow for too-short input (left pad with zeros)
            // or too-long input (check excess leftmost bytes are zeros).
            fn element_from_padded_slice<C: elliptic_curve::Curve>(
                data: &[u8],
            ) -> elliptic_curve::FieldBytes<C> {
                let point_len = C::FieldBytesSize::USIZE;
                if data.len() >= point_len {
                    let offset = data.len() - point_len;
                    for v in data.iter().take(offset) {
                        assert_eq!(*v, 0, "EcdsaVerifier: point too large");
                    }
                    elliptic_curve::FieldBytes::<C>::try_from(&data[offset..])
                        .expect("length mismatch")
                } else {
                    let mut point = elliptic_curve::FieldBytes::<C>::default();
                    let offset = point_len - data.len();
                    point[offset..].copy_from_slice(data);
                    point
                }
            }

            fn run_test(
                wx: &[u8],
                wy: &[u8],
                msg: &[u8],
                sig: &[u8],
                pass: bool,
                p1363_sig: bool,
            ) -> Option<&'static str> {
                let x = element_from_padded_slice::<Secp256k1>(wx);
                let y = element_from_padded_slice::<Secp256k1>(wy);
                let q_encoded =
                    Sec1Point::from_affine_coordinates(&x, &y, /* compress= */ false);
                let verifying_key = ecdsa_core::VerifyingKey::from_sec1_point(&q_encoded).unwrap();

                let sig = if p1363_sig {
                    match Signature::<Secp256k1>::from_slice(sig) {
                        Ok(s) => s.normalize_s(),
                        Err(_) if !pass => return None,
                        Err(_) => return Some("failed to parse signature P1363"),
                    }
                } else {
                    match Signature::<Secp256k1>::from_der(sig) {
                        Ok(s) => s.normalize_s(),
                        Err(_) if !pass => return None,
                        Err(_) => return Some("failed to parse signature ASN.1"),
                    }
                };

                match verifying_key.verify(msg, &sig) {
                    Ok(_) if pass => None,
                    Ok(_) => Some("signature verify unexpectedly succeeded"),
                    Err(_) if !pass => None,
                    Err(_) => Some("signature verify failed"),
                }
            }

            #[derive(Debug, Clone, Copy)]
            struct TestVector {
                /// X coordinates of the public key
                pub wx: &'static [u8],
                /// Y coordinates of the public key
                pub wy: &'static [u8],
                /// Payload to verify
                pub msg: &'static [u8],
                /// Der encoding of the signature
                pub sig: &'static [u8],
                /// Whether the signature should verify (`[1]`) or fail (`[0]`)
                pub pass_: &'static [u8],
            }

            impl TestVector {
                pub fn pass(&self) -> bool {
                    self.pass_[0] == 1
                }
            }

            macro_rules! run_test {
                ($blob: expr, $p1363_sig: expr) => {
                    {
                        ecdsa_core::dev::blobby::parse_into_structs!(
                            include_bytes!($blob);
                            static TEST_VECTORS: &[
                                TestVector { wx, wy, msg, sig, pass_ }
                            ];
                        );


                        for (i, tv) in TEST_VECTORS.iter().enumerate() {
                            if let Some(desc) = run_test(tv.wx, tv.wy, tv.msg, tv.sig, tv.pass(), $p1363_sig) {
                                panic!(
                                    "\n\
                                             Failed test №{}: {}\n\
                                             wx:\t{:?}\n\
                                             wy:\t{:?}\n\
                                             msg:\t{:?}\n\
                                             sig:\t{:?}\n\
                                             pass:\t{}\n",
                                    i,
                                    desc,
                                    hex::encode(tv.wx),
                                    hex::encode(tv.wy),
                                    hex::encode(tv.msg),
                                    hex::encode(tv.sig),
                                    tv.pass(),
                                );
                            }
                        }
                    }
                }
            }

            run_test!(concat!("test_vectors/data/", "wycheproof", ".blb"), false);
            run_test!(
                concat!("test_vectors/data/", "wycheproof-p1316", ".blb"),
                true
            );
        }
    }
}