ecdsa/
sign.rs

1//! ECDSA signing key.
2
3// TODO(tarcieri): support for hardware crypto accelerators
4
5use crate::{
6    hazmat::{DigestPrimitive, SignPrimitive},
7    Error, Result, Signature, SignatureSize,
8};
9use core::fmt::{self, Debug};
10use elliptic_curve::{
11    generic_array::ArrayLength,
12    group::ff::PrimeField,
13    ops::{Invert, Reduce},
14    subtle::{Choice, ConstantTimeEq, CtOption},
15    zeroize::{Zeroize, ZeroizeOnDrop},
16    FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar, SecretKey,
17};
18use signature::{
19    digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset},
20    hazmat::PrehashSigner,
21    rand_core::{CryptoRng, RngCore},
22    DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer,
23};
24
25#[cfg(feature = "pem")]
26use {
27    crate::elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument},
28    core::str::FromStr,
29};
30
31#[cfg(feature = "pkcs8")]
32use crate::elliptic_curve::{
33    pkcs8::{self, AssociatedOid, DecodePrivateKey},
34    sec1::{self, FromEncodedPoint, ToEncodedPoint},
35    AffinePoint,
36};
37
38#[cfg(feature = "verify")]
39use {crate::verify::VerifyingKey, elliptic_curve::PublicKey, signature::Keypair};
40
41/// ECDSA signing key. Generic over elliptic curves.
42///
43/// Requires an [`elliptic_curve::ProjectiveArithmetic`] impl on the curve, and a
44/// [`SignPrimitive`] impl on its associated `Scalar` type.
45#[derive(Clone)]
46#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
47pub struct SigningKey<C>
48where
49    C: PrimeCurve + ProjectiveArithmetic,
50    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
51    SignatureSize<C>: ArrayLength<u8>,
52{
53    /// ECDSA signing keys are non-zero elements of a given curve's scalar field.
54    secret_scalar: NonZeroScalar<C>,
55
56    /// Verifying key which corresponds to this signing key.
57    #[cfg(feature = "verify")]
58    verifying_key: VerifyingKey<C>,
59}
60
61impl<C> SigningKey<C>
62where
63    C: PrimeCurve + ProjectiveArithmetic,
64    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
65    SignatureSize<C>: ArrayLength<u8>,
66{
67    /// Generate a cryptographically random [`SigningKey`].
68    pub fn random(rng: impl CryptoRng + RngCore) -> Self {
69        NonZeroScalar::<C>::random(rng).into()
70    }
71
72    /// Initialize signing key from a raw scalar serialized as a byte slice.
73    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
74        SecretKey::<C>::from_be_bytes(bytes)
75            .map(|sk| sk.to_nonzero_scalar().into())
76            .map_err(|_| Error::new())
77    }
78
79    /// Serialize this [`SigningKey`] as bytes
80    pub fn to_bytes(&self) -> FieldBytes<C> {
81        self.secret_scalar.to_repr()
82    }
83
84    /// Borrow the secret [`NonZeroScalar`] value for this key.
85    ///
86    /// # ⚠️ Warning
87    ///
88    /// This value is key material.
89    ///
90    /// Please treat it with the care it deserves!
91    pub fn as_nonzero_scalar(&self) -> &NonZeroScalar<C> {
92        &self.secret_scalar
93    }
94
95    /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`]
96    // TODO(tarcieri): make this return `&VerifyingKey<C>` in the next breaking release
97    #[cfg(feature = "verify")]
98    #[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
99    pub fn verifying_key(&self) -> VerifyingKey<C> {
100        self.verifying_key
101    }
102}
103
104#[cfg(feature = "verify")]
105#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
106impl<C> AsRef<VerifyingKey<C>> for SigningKey<C>
107where
108    C: PrimeCurve + ProjectiveArithmetic,
109    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
110    SignatureSize<C>: ArrayLength<u8>,
111{
112    fn as_ref(&self) -> &VerifyingKey<C> {
113        &self.verifying_key
114    }
115}
116
117impl<C> ConstantTimeEq for SigningKey<C>
118where
119    C: PrimeCurve + ProjectiveArithmetic,
120    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
121    SignatureSize<C>: ArrayLength<u8>,
122{
123    fn ct_eq(&self, other: &Self) -> Choice {
124        self.secret_scalar.ct_eq(&other.secret_scalar)
125    }
126}
127
128impl<C> Debug for SigningKey<C>
129where
130    C: PrimeCurve + ProjectiveArithmetic,
131    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
132    SignatureSize<C>: ArrayLength<u8>,
133{
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        f.debug_struct("SigningKey").finish_non_exhaustive()
136    }
137}
138
139impl<C> Drop for SigningKey<C>
140where
141    C: PrimeCurve + ProjectiveArithmetic,
142    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
143    SignatureSize<C>: ArrayLength<u8>,
144{
145    fn drop(&mut self) {
146        self.secret_scalar.zeroize();
147    }
148}
149
150impl<C> ZeroizeOnDrop for SigningKey<C>
151where
152    C: PrimeCurve + ProjectiveArithmetic,
153    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
154    SignatureSize<C>: ArrayLength<u8>,
155{
156}
157
158/// Constant-time comparison
159impl<C> Eq for SigningKey<C>
160where
161    C: PrimeCurve + ProjectiveArithmetic,
162    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
163    SignatureSize<C>: ArrayLength<u8>,
164{
165}
166
167/// Constant-time comparison
168impl<C> PartialEq for SigningKey<C>
169where
170    C: PrimeCurve + ProjectiveArithmetic,
171    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
172    SignatureSize<C>: ArrayLength<u8>,
173{
174    fn eq(&self, other: &SigningKey<C>) -> bool {
175        self.ct_eq(other).into()
176    }
177}
178
179impl<C> From<SecretKey<C>> for SigningKey<C>
180where
181    C: PrimeCurve + ProjectiveArithmetic,
182    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
183    SignatureSize<C>: ArrayLength<u8>,
184{
185    fn from(secret_key: SecretKey<C>) -> Self {
186        Self::from(&secret_key)
187    }
188}
189
190impl<C> From<&SecretKey<C>> for SigningKey<C>
191where
192    C: PrimeCurve + ProjectiveArithmetic,
193    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
194    SignatureSize<C>: ArrayLength<u8>,
195{
196    fn from(secret_key: &SecretKey<C>) -> Self {
197        secret_key.to_nonzero_scalar().into()
198    }
199}
200
201impl<C> From<SigningKey<C>> for SecretKey<C>
202where
203    C: PrimeCurve + ProjectiveArithmetic,
204    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
205    SignatureSize<C>: ArrayLength<u8>,
206{
207    fn from(key: SigningKey<C>) -> Self {
208        key.secret_scalar.into()
209    }
210}
211
212impl<C> From<&SigningKey<C>> for SecretKey<C>
213where
214    C: PrimeCurve + ProjectiveArithmetic,
215    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
216    SignatureSize<C>: ArrayLength<u8>,
217{
218    fn from(secret_key: &SigningKey<C>) -> Self {
219        secret_key.secret_scalar.into()
220    }
221}
222
223impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C>
224where
225    C: PrimeCurve + ProjectiveArithmetic,
226    C::UInt: for<'a> From<&'a Scalar<C>>,
227    D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
228    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
229
230    SignatureSize<C>: ArrayLength<u8>,
231{
232    /// Sign message digest using a deterministic ephemeral scalar (`k`)
233    /// computed using the algorithm described in [RFC6979 § 3.2].
234    ///
235    /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
236    fn try_sign_digest(&self, msg_digest: D) -> Result<Signature<C>> {
237        Ok(self
238            .secret_scalar
239            .try_sign_digest_rfc6979::<D>(msg_digest, &[])?
240            .0)
241    }
242}
243
244#[cfg(feature = "verify")]
245#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
246impl<C> Keypair<Signature<C>> for SigningKey<C>
247where
248    C: PrimeCurve + ProjectiveArithmetic,
249    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
250    SignatureSize<C>: ArrayLength<u8>,
251{
252    type VerifyingKey = VerifyingKey<C>;
253}
254
255impl<C> PrehashSigner<Signature<C>> for SigningKey<C>
256where
257    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
258    C::Digest: BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
259    C::UInt: for<'a> From<&'a Scalar<C>>,
260    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
261    SignatureSize<C>: ArrayLength<u8>,
262{
263    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature<C>> {
264        let prehash = C::prehash_to_field_bytes(prehash)?;
265
266        Ok(self
267            .secret_scalar
268            .try_sign_prehashed_rfc6979::<C::Digest>(prehash, &[])?
269            .0)
270    }
271}
272
273impl<C> Signer<Signature<C>> for SigningKey<C>
274where
275    Self: DigestSigner<C::Digest, Signature<C>>,
276    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
277    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
278    SignatureSize<C>: ArrayLength<u8>,
279{
280    fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> {
281        self.try_sign_digest(C::Digest::new_with_prefix(msg))
282    }
283}
284
285impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C>
286where
287    C: PrimeCurve + ProjectiveArithmetic,
288    C::UInt: for<'a> From<&'a Scalar<C>>,
289    D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
290    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
291    SignatureSize<C>: ArrayLength<u8>,
292{
293    /// Sign message prehash using an ephemeral scalar (`k`) derived according
294    /// to a variant of RFC 6979 (Section 3.6) which supplies additional
295    /// entropy from an RNG.
296    fn try_sign_digest_with_rng(
297        &self,
298        mut rng: impl CryptoRng + RngCore,
299        msg_digest: D,
300    ) -> Result<Signature<C>> {
301        let mut ad = FieldBytes::<C>::default();
302        rng.fill_bytes(&mut ad);
303        Ok(self
304            .secret_scalar
305            .try_sign_digest_rfc6979::<D>(msg_digest, &ad)?
306            .0)
307    }
308}
309
310impl<C> RandomizedSigner<Signature<C>> for SigningKey<C>
311where
312    Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
313    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
314    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
315    SignatureSize<C>: ArrayLength<u8>,
316{
317    fn try_sign_with_rng(&self, rng: impl CryptoRng + RngCore, msg: &[u8]) -> Result<Signature<C>> {
318        self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg))
319    }
320}
321
322impl<C> From<NonZeroScalar<C>> for SigningKey<C>
323where
324    C: PrimeCurve + ProjectiveArithmetic,
325    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
326    SignatureSize<C>: ArrayLength<u8>,
327{
328    fn from(secret_scalar: NonZeroScalar<C>) -> Self {
329        #[cfg(feature = "verify")]
330        let public_key = PublicKey::from_secret_scalar(&secret_scalar);
331
332        Self {
333            secret_scalar,
334            #[cfg(feature = "verify")]
335            verifying_key: public_key.into(),
336        }
337    }
338}
339
340impl<C> TryFrom<&[u8]> for SigningKey<C>
341where
342    C: PrimeCurve + ProjectiveArithmetic,
343    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
344    SignatureSize<C>: ArrayLength<u8>,
345{
346    type Error = Error;
347
348    fn try_from(bytes: &[u8]) -> Result<Self> {
349        Self::from_bytes(bytes)
350    }
351}
352
353#[cfg(feature = "verify")]
354impl<C> From<&SigningKey<C>> for VerifyingKey<C>
355where
356    C: PrimeCurve + ProjectiveArithmetic,
357    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
358    SignatureSize<C>: ArrayLength<u8>,
359{
360    fn from(signing_key: &SigningKey<C>) -> VerifyingKey<C> {
361        signing_key.verifying_key()
362    }
363}
364
365#[cfg(feature = "pkcs8")]
366#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
367impl<C> TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey<C>
368where
369    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic,
370    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
371    FieldSize<C>: sec1::ModulusSize,
372    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
373    SignatureSize<C>: ArrayLength<u8>,
374{
375    type Error = pkcs8::Error;
376
377    fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
378        SecretKey::try_from(private_key_info).map(Into::into)
379    }
380}
381
382#[cfg(feature = "pem")]
383#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
384impl<C> EncodePrivateKey for SigningKey<C>
385where
386    C: AssociatedOid + PrimeCurve + ProjectiveArithmetic,
387    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
388    FieldSize<C>: sec1::ModulusSize,
389    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
390    SignatureSize<C>: ArrayLength<u8>,
391{
392    fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
393        SecretKey::from(self.secret_scalar).to_pkcs8_der()
394    }
395}
396
397#[cfg(feature = "pkcs8")]
398#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
399impl<C> DecodePrivateKey for SigningKey<C>
400where
401    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic,
402    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
403    FieldSize<C>: sec1::ModulusSize,
404    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
405    SignatureSize<C>: ArrayLength<u8>,
406{
407}
408
409#[cfg(feature = "pem")]
410#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
411impl<C> FromStr for SigningKey<C>
412where
413    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic,
414    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
415    FieldSize<C>: sec1::ModulusSize,
416    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
417    SignatureSize<C>: ArrayLength<u8>,
418{
419    type Err = Error;
420
421    fn from_str(s: &str) -> Result<Self> {
422        Self::from_pkcs8_pem(s).map_err(|_| Error::new())
423    }
424}