p521/
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-521 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-521 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-521 signing and
15//!   verification.
16//!
17//! ## Signing/Verification Example
18//!
19//! This example requires the `ecdsa` Cargo feature is enabled:
20//!
21//! ```
22//! # #[cfg(feature = "ecdsa")]
23//! # {
24//! use p521::ecdsa::{signature::Signer, Signature, SigningKey};
25//! use rand_core::OsRng; // requires 'getrandom' feature
26//!
27//! // Signing
28//! let signing_key = SigningKey::random(&mut OsRng); // Serialize with `::to_bytes()`
29//! let message = b"ECDSA proves knowledge of a secret number in the context of a single message";
30//! let signature: Signature = signing_key.sign(message);
31//!
32//! // Verification
33//! use p521::ecdsa::{signature::Verifier, VerifyingKey};
34//!
35//! let verifying_key = VerifyingKey::from(&signing_key); // Serialize with `::to_encoded_point()`
36//! assert!(verifying_key.verify(message, &signature).is_ok());
37//! # }
38//! ```
39
40// TODO(tarcieri): use RFC6979 + upstream types from the `ecdsa` crate
41
42pub use ecdsa_core::signature::{self, Error, Result};
43
44#[cfg(feature = "ecdsa")]
45use {
46    crate::{AffinePoint, EncodedPoint, FieldBytes, NonZeroScalar, Scalar},
47    ecdsa_core::{
48        hazmat::{bits2field, sign_prehashed, SignPrimitive, VerifyPrimitive},
49        signature::{
50            hazmat::{PrehashVerifier, RandomizedPrehashSigner},
51            rand_core::CryptoRngCore,
52            RandomizedSigner, Verifier,
53        },
54    },
55    elliptic_curve::Field,
56    sha2::{Digest, Sha512},
57};
58
59#[cfg(all(feature = "ecdsa", feature = "getrandom"))]
60use {
61    ecdsa_core::signature::{hazmat::PrehashSigner, Signer},
62    rand_core::OsRng,
63};
64
65use super::NistP521;
66
67/// ECDSA/P-521 signature (fixed-size)
68pub type Signature = ecdsa_core::Signature<NistP521>;
69
70/// ECDSA/P-521 signature (ASN.1 DER encoded)
71pub type DerSignature = ecdsa_core::der::Signature<NistP521>;
72
73#[cfg(feature = "ecdsa")]
74impl SignPrimitive<NistP521> for Scalar {}
75
76#[cfg(feature = "ecdsa")]
77impl VerifyPrimitive<NistP521> for AffinePoint {}
78
79/// ECDSA/P-521 signing key
80#[cfg(feature = "ecdsa")]
81#[derive(Clone)]
82pub struct SigningKey(ecdsa_core::SigningKey<NistP521>);
83
84#[cfg(feature = "ecdsa")]
85impl SigningKey {
86    /// Generate a cryptographically random [`SigningKey`].
87    pub fn random(rng: &mut impl CryptoRngCore) -> Self {
88        ecdsa_core::SigningKey::<NistP521>::random(rng).into()
89    }
90
91    /// Initialize signing key from a raw scalar serialized as a byte array.
92    pub fn from_bytes(bytes: &FieldBytes) -> Result<Self> {
93        ecdsa_core::SigningKey::<NistP521>::from_bytes(bytes).map(Into::into)
94    }
95
96    /// Initialize signing key from a raw scalar serialized as a byte slice.
97    pub fn from_slice(bytes: &[u8]) -> Result<Self> {
98        ecdsa_core::SigningKey::<NistP521>::from_slice(bytes).map(Into::into)
99    }
100
101    /// Serialize this [`SigningKey`] as bytes
102    pub fn to_bytes(&self) -> FieldBytes {
103        self.0.to_bytes()
104    }
105
106    /// Borrow the secret [`NonZeroScalar`] value for this key.
107    ///
108    /// # ⚠️ Warning
109    ///
110    /// This value is key material.
111    ///
112    /// Please treat it with the care it deserves!
113    pub fn as_nonzero_scalar(&self) -> &NonZeroScalar {
114        self.0.as_nonzero_scalar()
115    }
116
117    /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`].
118    #[cfg(feature = "verifying")]
119    pub fn verifying_key(&self) -> VerifyingKey {
120        VerifyingKey::from(self)
121    }
122}
123
124#[cfg(feature = "ecdsa")]
125impl From<ecdsa_core::SigningKey<NistP521>> for SigningKey {
126    fn from(inner: ecdsa_core::SigningKey<NistP521>) -> SigningKey {
127        SigningKey(inner)
128    }
129}
130
131#[cfg(all(feature = "ecdsa", feature = "getrandom"))]
132impl PrehashSigner<Signature> for SigningKey {
133    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature> {
134        self.sign_prehash_with_rng(&mut OsRng, prehash)
135    }
136}
137
138#[cfg(feature = "ecdsa")]
139impl RandomizedPrehashSigner<Signature> for SigningKey {
140    fn sign_prehash_with_rng(
141        &self,
142        rng: &mut impl CryptoRngCore,
143        prehash: &[u8],
144    ) -> Result<Signature> {
145        let z = bits2field::<NistP521>(prehash)?;
146        let k = Scalar::random(rng);
147        sign_prehashed(self.0.as_nonzero_scalar().as_ref(), k, &z).map(|sig| sig.0)
148    }
149}
150
151#[cfg(feature = "ecdsa")]
152impl RandomizedSigner<Signature> for SigningKey {
153    fn try_sign_with_rng(&self, rng: &mut impl CryptoRngCore, msg: &[u8]) -> Result<Signature> {
154        self.sign_prehash_with_rng(rng, &Sha512::digest(msg))
155    }
156}
157
158#[cfg(all(feature = "ecdsa", feature = "getrandom"))]
159impl Signer<Signature> for SigningKey {
160    fn try_sign(&self, msg: &[u8]) -> Result<Signature> {
161        self.try_sign_with_rng(&mut OsRng, msg)
162    }
163}
164
165/// ECDSA/P-521 verification key (i.e. public key)
166#[cfg(feature = "ecdsa")]
167#[derive(Clone)]
168pub struct VerifyingKey(ecdsa_core::VerifyingKey<NistP521>);
169
170#[cfg(feature = "ecdsa")]
171impl VerifyingKey {
172    /// Initialize [`VerifyingKey`] from a SEC1-encoded public key.
173    pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
174        ecdsa_core::VerifyingKey::<NistP521>::from_sec1_bytes(bytes).map(Into::into)
175    }
176
177    /// Initialize [`VerifyingKey`] from an affine point.
178    ///
179    /// Returns an [`Error`] if the given affine point is the additive identity
180    /// (a.k.a. point at infinity).
181    pub fn from_affine(affine: AffinePoint) -> Result<Self> {
182        ecdsa_core::VerifyingKey::<NistP521>::from_affine(affine).map(Into::into)
183    }
184
185    /// Initialize [`VerifyingKey`] from an [`EncodedPoint`].
186    pub fn from_encoded_point(public_key: &EncodedPoint) -> Result<Self> {
187        ecdsa_core::VerifyingKey::<NistP521>::from_encoded_point(public_key).map(Into::into)
188    }
189
190    /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally
191    /// applying point compression.
192    pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
193        self.0.to_encoded_point(compress)
194    }
195
196    /// Borrow the inner [`AffinePoint`] for this public key.
197    pub fn as_affine(&self) -> &AffinePoint {
198        self.0.as_affine()
199    }
200}
201
202#[cfg(feature = "ecdsa")]
203impl From<&SigningKey> for VerifyingKey {
204    fn from(signing_key: &SigningKey) -> VerifyingKey {
205        Self::from(*signing_key.0.verifying_key())
206    }
207}
208
209#[cfg(feature = "ecdsa")]
210impl From<ecdsa_core::VerifyingKey<NistP521>> for VerifyingKey {
211    fn from(inner: ecdsa_core::VerifyingKey<NistP521>) -> VerifyingKey {
212        VerifyingKey(inner)
213    }
214}
215
216#[cfg(feature = "ecdsa")]
217impl PrehashVerifier<Signature> for VerifyingKey {
218    fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> Result<()> {
219        self.0.verify_prehash(prehash, signature)
220    }
221}
222
223#[cfg(feature = "ecdsa")]
224impl Verifier<Signature> for VerifyingKey {
225    fn verify(&self, msg: &[u8], signature: &Signature) -> Result<()> {
226        self.verify_prehash(&Sha512::digest(msg), signature)
227    }
228}
229
230#[cfg(all(test, feature = "ecdsa", feature = "getrandom"))]
231mod tests {
232    // TODO(tarcieri): RFC6979 support + test vectors
233
234    mod sign {
235        use crate::{test_vectors::ecdsa::ECDSA_TEST_VECTORS, NistP521};
236        ecdsa_core::new_signing_test!(NistP521, ECDSA_TEST_VECTORS);
237    }
238
239    mod verify {
240        use crate::{test_vectors::ecdsa::ECDSA_TEST_VECTORS, NistP521};
241        ecdsa_core::new_verification_test!(NistP521, ECDSA_TEST_VECTORS);
242    }
243
244    mod wycheproof {
245        use crate::{
246            ecdsa::{Signature, Verifier, VerifyingKey},
247            EncodedPoint, NistP521,
248        };
249
250        // TODO: use ecdsa_core::new_wycheproof_test!(wycheproof, "wycheproof", NistP521);
251        #[test]
252        fn wycheproof() {
253            use blobby::Blob5Iterator;
254            use elliptic_curve::generic_array::typenum::Unsigned;
255
256            // Build a field element but allow for too-short input (left pad with zeros)
257            // or too-long input (check excess leftmost bytes are zeros).
258            fn element_from_padded_slice<C: elliptic_curve::Curve>(
259                data: &[u8],
260            ) -> elliptic_curve::FieldBytes<C> {
261                let point_len = C::FieldBytesSize::USIZE;
262                if data.len() >= point_len {
263                    let offset = data.len() - point_len;
264                    for v in data.iter().take(offset) {
265                        assert_eq!(*v, 0, "EcdsaVerifier: point too large");
266                    }
267                    elliptic_curve::FieldBytes::<C>::clone_from_slice(&data[offset..])
268                } else {
269                    // Provided slice is too short and needs to be padded with zeros
270                    // on the left.  Build a combined exact iterator to do this.
271                    let iter = core::iter::repeat(0)
272                        .take(point_len - data.len())
273                        .chain(data.iter().cloned());
274                    elliptic_curve::FieldBytes::<C>::from_exact_iter(iter).unwrap()
275                }
276            }
277
278            fn run_test(
279                wx: &[u8],
280                wy: &[u8],
281                msg: &[u8],
282                sig: &[u8],
283                pass: bool,
284            ) -> Option<&'static str> {
285                let x = element_from_padded_slice::<NistP521>(wx);
286                let y = element_from_padded_slice::<NistP521>(wy);
287                let q_encoded =
288                    EncodedPoint::from_affine_coordinates(&x, &y, /* compress= */ false);
289                let verifying_key = VerifyingKey::from_encoded_point(&q_encoded).unwrap();
290
291                let sig = match Signature::from_der(sig) {
292                    Ok(s) => s,
293                    Err(_) if !pass => return None,
294                    Err(_) => return Some("failed to parse signature ASN.1"),
295                };
296
297                match verifying_key.verify(msg, &sig) {
298                    Ok(_) if pass => None,
299                    Ok(_) => Some("signature verify unexpectedly succeeded"),
300                    Err(_) if !pass => None,
301                    Err(_) => Some("signature verify failed"),
302                }
303            }
304
305            let data = include_bytes!(concat!("test_vectors/data/wycheproof.blb"));
306
307            for (i, row) in Blob5Iterator::new(data).unwrap().enumerate() {
308                let [wx, wy, msg, sig, status] = row.unwrap();
309                let pass = match status[0] {
310                    0 => false,
311                    1 => true,
312                    _ => panic!("invalid value for pass flag"),
313                };
314                if let Some(desc) = run_test(wx, wy, msg, sig, pass) {
315                    panic!(
316                        "\n\
317                                 Failed test №{}: {}\n\
318                                 wx:\t{:?}\n\
319                                 wy:\t{:?}\n\
320                                 msg:\t{:?}\n\
321                                 sig:\t{:?}\n\
322                                 pass:\t{}\n",
323                        i, desc, wx, wy, msg, sig, pass,
324                    );
325                }
326            }
327        }
328    }
329}