Skip to main content

p256/
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/P-256 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/P-256 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/P-256 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 p256::{
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(false); // 65-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 p256::{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
45pub use ecdsa_core::{
46    RecoveryId,
47    signature::{self, Error},
48};
49
50use super::NistP256;
51use ecdsa_core::EcdsaCurve;
52
53/// ECDSA/P-256 signature (fixed-size)
54pub type Signature = ecdsa_core::Signature<NistP256>;
55
56/// ECDSA/P-256 signature (ASN.1 DER encoded)
57pub type DerSignature = ecdsa_core::der::Signature<NistP256>;
58
59impl EcdsaCurve for NistP256 {
60    const NORMALIZE_S: bool = false;
61}
62
63/// ECDSA/P-256 signing key
64#[cfg(feature = "ecdsa")]
65pub type SigningKey = ecdsa_core::SigningKey<NistP256>;
66
67/// ECDSA/P-256 verification key (i.e. public key)
68#[cfg(feature = "ecdsa")]
69pub type VerifyingKey = ecdsa_core::VerifyingKey<NistP256>;
70
71#[cfg(feature = "sha256")]
72impl ecdsa_core::hazmat::DigestAlgorithm for NistP256 {
73    type Digest = sha2::Sha256;
74}
75#[cfg(all(test, feature = "ecdsa"))]
76mod tests {
77    use crate::{
78        AffinePoint, Sec1Point,
79        ecdsa::{
80            Signature, SigningKey, VerifyingKey,
81            signature::Signer,
82            signature::hazmat::{PrehashSigner, PrehashVerifier},
83        },
84    };
85    use elliptic_curve::sec1::FromSec1Point;
86    use hex_literal::hex;
87    use sha2::Digest;
88
89    // Test vector from RFC 6979 Appendix 2.5 (NIST P-256 + SHA-256)
90    // <https://tools.ietf.org/html/rfc6979#appendix-A.2.5>
91    #[test]
92    fn rfc6979() {
93        let x = hex!("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
94        let signer = SigningKey::from_bytes(&x.into()).unwrap();
95        let signature: Signature = signer.sign(b"sample");
96        assert_eq!(
97            signature.to_bytes().as_slice(),
98            &hex!(
99                "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716
100                 f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8"
101            )
102        );
103        let signature: Signature = signer.sign(b"test");
104        assert_eq!(
105            signature.to_bytes().as_slice(),
106            &hex!(
107                "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367
108                 019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083"
109            )
110        );
111    }
112
113    // Test signing with PrehashSigner using SHA-384 which output is larger than P-256 field size.
114    #[test]
115    fn prehash_signer_signing_with_sha384() {
116        let x = hex!("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
117        let signer = SigningKey::from_bytes(&x.into()).unwrap();
118        let digest = sha2::Sha384::digest(b"test");
119        let signature: Signature = signer.sign_prehash(&digest).unwrap();
120        assert_eq!(
121            signature.to_bytes().as_slice(),
122            &hex!(
123                "ebde85f1539af67e70dd7a8a6afeeb332aa7f08f01ebb6ab6e04e2a62d2fef75
124                 871af45800daddf55619b005a601a7a84f544260f1d2625b2ef5aa7a4f4dd76f"
125            )
126        );
127    }
128
129    // Test verifying with PrehashVerifier using SHA-256 which output is larger than P-256 field size.
130    #[test]
131    fn prehash_signer_verification_with_sha384() {
132        // The following test vector adapted from the FIPS 186-4 ECDSA test vectors
133        // (P-256, SHA-384, from `SigGen.txt` in `186-4ecdsatestvectors.zip`)
134        // <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/digital-signatures>
135        let verifier = VerifyingKey::from_affine(
136            AffinePoint::from_sec1_point(&Sec1Point::from_affine_coordinates(
137                &hex!("e0e7b99bc62d8dd67883e39ed9fa0657789c5ff556cc1fd8dd1e2a55e9e3f243").into(),
138                &hex!("63fbfd0232b95578075c903a4dbf85ad58f8350516e1ec89b0ee1f5e1362da69").into(),
139                false,
140            ))
141            .unwrap(),
142        )
143        .unwrap();
144        let signature = Signature::from_scalars(
145            hex!("f5087878e212b703578f5c66f434883f3ef414dc23e2e8d8ab6a8d159ed5ad83"),
146            hex!("306b4c6c20213707982dffbb30fba99b96e792163dd59dbe606e734328dd7c8a"),
147        )
148        .unwrap();
149        let result = verifier.verify_prehash(
150            &hex!("d9c83b92fa0979f4a5ddbd8dd22ab9377801c3c31bf50f932ace0d2146e2574da0d5552dbed4b18836280e9f94558ea6"),
151            &signature,
152        );
153        assert!(result.is_ok());
154    }
155
156    mod sign {
157        use crate::{NistP256, test_vectors::ecdsa::ECDSA_TEST_VECTORS};
158        ecdsa_core::new_signing_test!(NistP256, ECDSA_TEST_VECTORS);
159    }
160
161    mod verify {
162        use crate::{NistP256, test_vectors::ecdsa::ECDSA_TEST_VECTORS};
163        ecdsa_core::new_verification_test!(NistP256, ECDSA_TEST_VECTORS);
164    }
165
166    mod wycheproof {
167        use crate::NistP256;
168        ecdsa_core::new_wycheproof_test!(wycheproof, "wycheproof", NistP256);
169    }
170}