Skip to main content

p384/
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-384 signature). Does not require the `arithmetic` feature. This is
8//!   useful for 3rd-party crates which wish to use the `Signature` type for
9//!   interoperability purposes (particularly in conjunction with the
10//!   [`signature::Signer`] trait. Example use cases for this include other
11//!   software implementations of ECDSA/P-384 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-384 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 p384::{
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); // 97-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 p384::{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::signature::{self, Error};
46
47use super::NistP384;
48use ecdsa_core::EcdsaCurve;
49
50/// ECDSA/P-384 signature (fixed-size)
51pub type Signature = ecdsa_core::Signature<NistP384>;
52
53/// ECDSA/P-384 signature (ASN.1 DER encoded)
54pub type DerSignature = ecdsa_core::der::Signature<NistP384>;
55
56impl EcdsaCurve for NistP384 {
57    const NORMALIZE_S: bool = false;
58}
59
60/// ECDSA/P-384 signing key
61#[cfg(feature = "ecdsa")]
62pub type SigningKey = ecdsa_core::SigningKey<NistP384>;
63
64/// ECDSA/P-384 verification key (i.e. public key)
65#[cfg(feature = "ecdsa")]
66pub type VerifyingKey = ecdsa_core::VerifyingKey<NistP384>;
67
68#[cfg(feature = "sha384")]
69impl ecdsa_core::hazmat::DigestAlgorithm for NistP384 {
70    type Digest = sha2::Sha384;
71}
72
73#[cfg(all(test, feature = "ecdsa"))]
74mod tests {
75    use crate::{
76        AffinePoint, Sec1Point, SecretKey,
77        ecdsa::{
78            Signature, SigningKey, VerifyingKey,
79            signature::Signer,
80            signature::hazmat::{PrehashSigner, PrehashVerifier},
81        },
82    };
83
84    use elliptic_curve::sec1::FromSec1Point;
85    use hex_literal::hex;
86    use sha2::Digest;
87
88    // Test vector from RFC 6979 Appendix 2.6 (NIST P-384 + SHA-384)
89    // <https://tools.ietf.org/html/rfc6979#appendix-A.2.6>
90    #[test]
91    fn rfc6979() {
92        let x = hex!(
93            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d896d5724e4c70a825f872c9ea60d2edf5"
94        );
95        let signer = SigningKey::from_bytes(&x.into()).unwrap();
96        let signature: Signature = signer.sign(b"sample");
97        assert_eq!(
98            signature.to_bytes().as_slice(),
99            &hex!(
100                "94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c81a648152e44acf96e36dd1e80fabe46
101                99ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94fa329c145786e679e7b82c71a38628ac8"
102            )
103        );
104
105        let signature: Signature = signer.sign(b"test");
106        assert_eq!(
107            signature.to_bytes().as_slice(),
108            &hex!(
109                "8203b63d3c853e8d77227fb377bcf7b7b772e97892a80f36ab775d509d7a5feb0542a7f0812998da8f1dd3ca3cf023db
110                ddd0760448d42d8a43af45af836fce4de8be06b485e9b61b827c2f13173923e06a739f040649a667bf3b828246baa5a5"
111            )
112        );
113    }
114
115    // Test signing with PrehashSigner using SHA-256 whose output is smaller than P-384 field size.
116    #[test]
117    fn prehash_signer_signing_with_sha256() {
118        let x = hex!(
119            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d896d5724e4c70a825f872c9ea60d2edf5"
120        );
121        let signer = SigningKey::from_bytes(&x.into()).unwrap();
122        let digest = sha2::Sha256::digest(b"test");
123        let signature: Signature = signer.sign_prehash(&digest).unwrap();
124        assert_eq!(
125            signature.to_bytes().as_slice(),
126            &hex!(
127                "010c3ab1a300f8c9d63eafa9a41813f0c5416c08814bdfc0236458d6c2603d71c4941f4696e60aff5717476170bb6ab4
128                03c4ad6274c61691346b2178def879424726909af308596ffb6355a042f48a114e2eb28eaa6918592b4727961057c0c1"
129            )
130        );
131    }
132
133    // Test verifying with PrehashVerifier using SHA-256 whose output is smaller than P-384 field size.
134    #[test]
135    fn prehash_signer_verification_with_sha256() {
136        // The following test vector adapted from the FIPS 186-4 ECDSA test vectors
137        // (P-384, SHA-256, from `SigGen.txt` in `186-4ecdsatestvectors.zip`)
138        // <https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/digital-signatures>
139        let verifier = VerifyingKey::from_affine(
140            AffinePoint::from_sec1_point(
141                &Sec1Point::from_affine_coordinates(
142                    &hex!("0400193b21f07cd059826e9453d3e96dd145041c97d49ff6b7047f86bb0b0439e909274cb9c282bfab88674c0765bc75").into(),
143                    &hex! ("f70d89c52acbc70468d2c5ae75c76d7f69b76af62dcf95e99eba5dd11adf8f42ec9a425b0c5ec98e2f234a926b82a147").into(),
144                    false,
145                ),
146            ).unwrap()
147        ).unwrap();
148        let signature = Signature::from_scalars(
149            hex!("b11db00cdaf53286d4483f38cd02785948477ed7ebc2ad609054551da0ab0359978c61851788aa2ec3267946d440e878"),
150            hex!("16007873c5b0604ce68112a8fee973e8e2b6e3319c683a762ff5065a076512d7c98b27e74b7887671048ac027df8cbf2"),
151        ).unwrap();
152        let result = verifier.verify_prehash(
153            &hex!("bbbd0a5f645d3fda10e288d172b299455f9dff00e0fbc2833e18cd017d7f3ed1"),
154            &signature,
155        );
156        assert!(result.is_ok());
157    }
158
159    #[test]
160    fn signing_secret_key_equivalent() {
161        let raw_sk: [u8; 48] = [
162            32, 52, 118, 9, 96, 116, 119, 172, 168, 251, 251, 197, 230, 33, 132, 85, 243, 25, 150,
163            105, 121, 46, 248, 180, 102, 250, 168, 123, 220, 103, 121, 129, 68, 200, 72, 221, 3,
164            102, 30, 237, 90, 198, 36, 97, 52, 12, 234, 150,
165        ];
166
167        let seck = SecretKey::from_bytes(&raw_sk.into()).unwrap();
168        let sigk = SigningKey::from_bytes(&raw_sk.into()).unwrap();
169
170        assert_eq!(seck.to_bytes().as_slice(), &raw_sk);
171        assert_eq!(sigk.to_bytes().as_slice(), &raw_sk);
172    }
173
174    mod sign {
175        use crate::{NistP384, test_vectors::ecdsa::ECDSA_TEST_VECTORS};
176        ecdsa_core::new_signing_test!(NistP384, ECDSA_TEST_VECTORS);
177    }
178
179    mod verify {
180        use crate::{NistP384, test_vectors::ecdsa::ECDSA_TEST_VECTORS};
181        ecdsa_core::new_verification_test!(NistP384, ECDSA_TEST_VECTORS);
182    }
183
184    mod wycheproof {
185        use crate::NistP384;
186        ecdsa_core::new_wycheproof_test!(wycheproof, "wycheproof", NistP384);
187    }
188}