Skip to main content

ecdsa/
signing.rs

1//! ECDSA signing: producing signatures using a [`SigningKey`].
2
3use crate::{
4    DigestAlgorithm, EcdsaCurve, Error, Result, Signature, SignatureWithOid, ecdsa_oid_for_digest,
5    hazmat::sign_prehashed_rfc6979,
6};
7use core::fmt::{self, Debug};
8use digest::{Digest, FixedOutput, const_oid::AssociatedOid};
9use elliptic_curve::{
10    CurveArithmetic, FieldBytes, Generate, NonZeroScalar, Scalar, SecretKey,
11    group::ff::PrimeField,
12    ops::Invert,
13    rand_core::CryptoRng,
14    subtle::{Choice, ConstantTimeEq, CtOption},
15    zeroize::{Zeroize, ZeroizeOnDrop},
16};
17use signature::{
18    DigestSigner, MultipartSigner, RandomizedDigestSigner, RandomizedMultipartSigner,
19    RandomizedSigner, Signer,
20    hazmat::{PrehashSigner, RandomizedPrehashSigner},
21    rand_core::TryCryptoRng,
22};
23
24#[cfg(feature = "pkcs8")]
25use crate::elliptic_curve::{
26    AffinePoint,
27    pkcs8::{
28        self, ObjectIdentifier,
29        der::AnyRef,
30        spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
31    },
32    sec1::{self, FromSec1Point, ToSec1Point},
33};
34#[cfg(any(feature = "der", feature = "pem"))]
35use elliptic_curve::FieldBytesSize;
36#[cfg(feature = "der")]
37use elliptic_curve::array::ArraySize;
38#[cfg(all(feature = "alloc", feature = "pkcs8"))]
39use elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument};
40#[cfg(feature = "algorithm")]
41use {crate::VerifyingKey, elliptic_curve::PublicKey, signature::KeypairRef};
42#[cfg(feature = "der")]
43use {crate::der, core::ops::Add};
44#[cfg(feature = "pem")]
45use {core::str::FromStr, elliptic_curve::pkcs8::DecodePrivateKey};
46
47/// ECDSA secret key used for signing.
48///
49/// Generic over prime order elliptic curves (e.g. NIST P-curves).
50/// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve.
51///
52/// ## Usage
53///
54/// The [`signature`] crate defines the following traits which are the
55/// primary API for signing:
56///
57/// - [`Signer`]: sign a message using this key
58/// - [`DigestSigner`]: sign the output of a digest using this key
59/// - [`PrehashSigner`]: sign the low-level raw output bytes of a message digest
60///
61/// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html)
62/// for examples of using this type with a concrete elliptic curve.
63#[derive(Clone)]
64pub struct SigningKey<C>
65where
66    C: EcdsaCurve + CurveArithmetic,
67{
68    /// ECDSA signing keys are non-zero elements of a given curve's scalar field.
69    secret_scalar: NonZeroScalar<C>,
70
71    /// Verifying key which corresponds to this signing key.
72    #[cfg(feature = "algorithm")]
73    verifying_key: VerifyingKey<C>,
74}
75
76impl<C> SigningKey<C>
77where
78    C: EcdsaCurve + CurveArithmetic,
79{
80    /// Initialize signing key from a raw scalar serialized as a byte array.
81    ///
82    /// # Errors
83    /// Returns an error if `bytes` is not a valid element of the scalar field for `C`.
84    pub fn from_bytes(bytes: &FieldBytes<C>) -> Result<Self> {
85        SecretKey::<C>::from_bytes(bytes)
86            .map(Into::into)
87            .map_err(|_| Error::new())
88    }
89
90    /// Initialize signing key from a raw scalar serialized as a byte slice.
91    ///
92    /// # Errors
93    /// Returns an error if `bytes` is not the length of [`FieldBytes`] for `C`, or if it is not
94    /// a valid element of the scalar field for `C`.
95    pub fn from_slice(bytes: &[u8]) -> Result<Self> {
96        SecretKey::<C>::from_slice(bytes)
97            .map(Into::into)
98            .map_err(|_| Error::new())
99    }
100
101    /// Serialize this [`SigningKey`] as bytes
102    pub fn to_bytes(&self) -> FieldBytes<C> {
103        self.secret_scalar.to_repr()
104    }
105
106    /// Borrow the secret [`NonZeroScalar`] value for this key.
107    ///
108    /// <div class="warning">
109    /// <b>Security Warning</b>
110    ///
111    /// This value is key material. Please treat it with the care it deserves!
112    /// </div>
113    pub fn as_nonzero_scalar(&self) -> &NonZeroScalar<C> {
114        &self.secret_scalar
115    }
116
117    /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`].
118    #[cfg(feature = "algorithm")]
119    pub fn verifying_key(&self) -> &VerifyingKey<C> {
120        &self.verifying_key
121    }
122
123    /// DEPRECATED: Generate a cryptographically random [`SigningKey`].
124    #[deprecated(since = "0.17.0", note = "use the `Generate` trait instead")]
125    pub fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
126        Self::generate_from_rng(rng)
127    }
128}
129
130impl<C> Generate for SigningKey<C>
131where
132    C: EcdsaCurve + CurveArithmetic,
133{
134    fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(
135        rng: &mut R,
136    ) -> core::result::Result<Self, R::Error> {
137        NonZeroScalar::<C>::try_generate_from_rng(rng).map(Into::into)
138    }
139}
140
141//
142// `*Signer` trait impls
143//
144
145/// Sign message digest using a deterministic ephemeral scalar (`k`)
146/// computed using the algorithm described in [RFC6979 § 3.2].
147///
148/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
149impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C>
150where
151    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
152    D: Digest + FixedOutput,
153    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
154{
155    fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<Signature<C>> {
156        let mut digest = D::new();
157        f(&mut digest)?;
158        self.sign_prehash(&digest.finalize_fixed())
159    }
160}
161
162/// Sign message prehash using a deterministic ephemeral scalar (`k`) computed using the algorithm
163/// described in [RFC6979 § 3.2].
164///
165/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
166impl<C> PrehashSigner<Signature<C>> for SigningKey<C>
167where
168    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
169    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
170{
171    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature<C>> {
172        Ok(sign_prehashed_rfc6979::<C, C::Digest>(&self.secret_scalar, prehash, &[]).0)
173    }
174}
175
176/// Sign message using a deterministic ephemeral scalar (`k`)
177/// computed using the algorithm described in [RFC6979 § 3.2].
178///
179/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
180impl<C> Signer<Signature<C>> for SigningKey<C>
181where
182    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
183    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
184{
185    fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> {
186        self.try_multipart_sign(&[msg])
187    }
188}
189
190impl<C> MultipartSigner<Signature<C>> for SigningKey<C>
191where
192    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
193    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
194{
195    fn try_multipart_sign(&self, msg: &[&[u8]]) -> core::result::Result<Signature<C>, Error> {
196        self.try_sign_digest(|digest: &mut C::Digest| {
197            msg.iter().for_each(|slice| digest.update(slice));
198            Ok(())
199        })
200    }
201}
202
203impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C>
204where
205    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
206    D: Digest + FixedOutput,
207    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
208{
209    fn try_sign_digest_with_rng<R: TryCryptoRng + ?Sized, F: Fn(&mut D) -> Result<()>>(
210        &self,
211        rng: &mut R,
212        f: F,
213    ) -> Result<Signature<C>> {
214        let mut digest = D::new();
215        f(&mut digest)?;
216        self.sign_prehash_with_rng(rng, &digest.finalize_fixed())
217    }
218}
219
220impl<C> RandomizedPrehashSigner<Signature<C>> for SigningKey<C>
221where
222    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
223    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
224{
225    fn sign_prehash_with_rng<R: TryCryptoRng + ?Sized>(
226        &self,
227        rng: &mut R,
228        prehash: &[u8],
229    ) -> Result<Signature<C>> {
230        let mut ad = FieldBytes::<C>::default();
231        rng.try_fill_bytes(&mut ad).map_err(|_| Error::new())?;
232        Ok(sign_prehashed_rfc6979::<C, C::Digest>(&self.secret_scalar, prehash, &ad).0)
233    }
234}
235
236impl<C> RandomizedSigner<Signature<C>> for SigningKey<C>
237where
238    Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
239    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
240    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
241{
242    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
243        &self,
244        rng: &mut R,
245        msg: &[u8],
246    ) -> Result<Signature<C>> {
247        self.try_multipart_sign_with_rng(rng, &[msg])
248    }
249}
250
251impl<C> RandomizedMultipartSigner<Signature<C>> for SigningKey<C>
252where
253    Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
254    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
255    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
256{
257    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
258        &self,
259        rng: &mut R,
260        msg: &[&[u8]],
261    ) -> Result<Signature<C>> {
262        self.try_sign_digest_with_rng(rng, |digest: &mut C::Digest| {
263            msg.iter().for_each(|slice| digest.update(slice));
264            Ok(())
265        })
266    }
267}
268
269impl<C, D> DigestSigner<D, SignatureWithOid<C>> for SigningKey<C>
270where
271    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
272    D: AssociatedOid + Digest + FixedOutput,
273    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
274{
275    fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<SignatureWithOid<C>> {
276        let signature: Signature<C> = self.try_sign_digest(f)?;
277        let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
278        SignatureWithOid::new(signature, oid)
279    }
280}
281
282impl<C> Signer<SignatureWithOid<C>> for SigningKey<C>
283where
284    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
285    C::Digest: AssociatedOid,
286    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
287{
288    fn try_sign(&self, msg: &[u8]) -> Result<SignatureWithOid<C>> {
289        self.try_multipart_sign(&[msg])
290    }
291}
292
293impl<C> MultipartSigner<SignatureWithOid<C>> for SigningKey<C>
294where
295    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
296    C::Digest: AssociatedOid,
297    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
298{
299    fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<SignatureWithOid<C>> {
300        self.try_sign_digest(|digest: &mut C::Digest| {
301            msg.iter().for_each(|slice| digest.update(slice));
302            Ok(())
303        })
304    }
305}
306
307#[cfg(feature = "der")]
308impl<C> PrehashSigner<der::Signature<C>> for SigningKey<C>
309where
310    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
311    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
312    der::MaxSize<C>: ArraySize,
313    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
314{
315    fn sign_prehash(&self, prehash: &[u8]) -> Result<der::Signature<C>> {
316        PrehashSigner::<Signature<C>>::sign_prehash(self, prehash).map(Into::into)
317    }
318}
319
320#[cfg(feature = "der")]
321impl<C> Signer<der::Signature<C>> for SigningKey<C>
322where
323    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
324    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
325    der::MaxSize<C>: ArraySize,
326    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
327{
328    fn try_sign(&self, msg: &[u8]) -> Result<der::Signature<C>> {
329        Signer::<Signature<C>>::try_sign(self, msg).map(Into::into)
330    }
331}
332
333#[cfg(feature = "der")]
334impl<C, D> RandomizedDigestSigner<D, der::Signature<C>> for SigningKey<C>
335where
336    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
337    D: Digest + FixedOutput,
338    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
339    der::MaxSize<C>: ArraySize,
340    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
341{
342    fn try_sign_digest_with_rng<R: TryCryptoRng + ?Sized, F: Fn(&mut D) -> Result<()>>(
343        &self,
344        rng: &mut R,
345        f: F,
346    ) -> Result<der::Signature<C>> {
347        RandomizedDigestSigner::<D, Signature<C>>::try_sign_digest_with_rng(self, rng, f)
348            .map(Into::into)
349    }
350}
351
352#[cfg(feature = "der")]
353impl<C> RandomizedPrehashSigner<der::Signature<C>> for SigningKey<C>
354where
355    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
356    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
357    der::MaxSize<C>: ArraySize,
358    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
359{
360    fn sign_prehash_with_rng<R: TryCryptoRng + ?Sized>(
361        &self,
362        rng: &mut R,
363        prehash: &[u8],
364    ) -> Result<der::Signature<C>> {
365        RandomizedPrehashSigner::<Signature<C>>::sign_prehash_with_rng(self, rng, prehash)
366            .map(Into::into)
367    }
368}
369
370#[cfg(feature = "der")]
371impl<D, C> DigestSigner<D, der::Signature<C>> for SigningKey<C>
372where
373    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
374    D: Digest + FixedOutput,
375    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
376    der::MaxSize<C>: ArraySize,
377    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
378{
379    fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<der::Signature<C>> {
380        DigestSigner::<D, Signature<C>>::try_sign_digest(self, f).map(Into::into)
381    }
382}
383
384#[cfg(feature = "der")]
385impl<C> RandomizedSigner<der::Signature<C>> for SigningKey<C>
386where
387    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
388    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
389    der::MaxSize<C>: ArraySize,
390    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
391{
392    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
393        &self,
394        rng: &mut R,
395        msg: &[u8],
396    ) -> Result<der::Signature<C>> {
397        RandomizedSigner::<Signature<C>>::try_sign_with_rng(self, rng, msg).map(Into::into)
398    }
399}
400
401#[cfg(feature = "der")]
402impl<C> RandomizedMultipartSigner<der::Signature<C>> for SigningKey<C>
403where
404    C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
405    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
406    der::MaxSize<C>: ArraySize,
407    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
408{
409    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
410        &self,
411        rng: &mut R,
412        msg: &[&[u8]],
413    ) -> Result<der::Signature<C>> {
414        RandomizedMultipartSigner::<Signature<C>>::try_multipart_sign_with_rng(self, rng, msg)
415            .map(Into::into)
416    }
417}
418
419//
420// Other trait impls
421//
422
423#[cfg(feature = "algorithm")]
424impl<C> AsRef<VerifyingKey<C>> for SigningKey<C>
425where
426    C: EcdsaCurve + CurveArithmetic,
427    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
428{
429    fn as_ref(&self) -> &VerifyingKey<C> {
430        &self.verifying_key
431    }
432}
433
434impl<C> ConstantTimeEq for SigningKey<C>
435where
436    C: EcdsaCurve + CurveArithmetic,
437    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
438{
439    fn ct_eq(&self, other: &Self) -> Choice {
440        self.secret_scalar.ct_eq(&other.secret_scalar)
441    }
442}
443
444impl<C> Debug for SigningKey<C>
445where
446    C: EcdsaCurve + CurveArithmetic,
447{
448    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449        f.debug_struct("SigningKey").finish_non_exhaustive()
450    }
451}
452
453impl<C> Drop for SigningKey<C>
454where
455    C: EcdsaCurve + CurveArithmetic,
456{
457    fn drop(&mut self) {
458        self.secret_scalar.zeroize();
459    }
460}
461
462/// Constant-time comparison
463impl<C> Eq for SigningKey<C>
464where
465    C: EcdsaCurve + CurveArithmetic,
466    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
467{
468}
469impl<C> PartialEq for SigningKey<C>
470where
471    C: EcdsaCurve + CurveArithmetic,
472    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
473{
474    fn eq(&self, other: &SigningKey<C>) -> bool {
475        self.ct_eq(other).into()
476    }
477}
478
479impl<C> From<NonZeroScalar<C>> for SigningKey<C>
480where
481    C: EcdsaCurve + CurveArithmetic,
482{
483    fn from(secret_scalar: NonZeroScalar<C>) -> Self {
484        #[cfg(feature = "algorithm")]
485        let public_key = PublicKey::from_secret_scalar(&secret_scalar);
486
487        Self {
488            secret_scalar,
489            #[cfg(feature = "algorithm")]
490            verifying_key: public_key.into(),
491        }
492    }
493}
494
495impl<C> From<SecretKey<C>> for SigningKey<C>
496where
497    C: EcdsaCurve + CurveArithmetic,
498{
499    fn from(secret_key: SecretKey<C>) -> Self {
500        Self::from(&secret_key)
501    }
502}
503
504impl<C> From<&SecretKey<C>> for SigningKey<C>
505where
506    C: EcdsaCurve + CurveArithmetic,
507{
508    fn from(secret_key: &SecretKey<C>) -> Self {
509        secret_key.to_nonzero_scalar().into()
510    }
511}
512
513impl<C> From<SigningKey<C>> for SecretKey<C>
514where
515    C: EcdsaCurve + CurveArithmetic,
516    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
517{
518    fn from(key: SigningKey<C>) -> Self {
519        key.secret_scalar.into()
520    }
521}
522
523impl<C> From<&SigningKey<C>> for SecretKey<C>
524where
525    C: EcdsaCurve + CurveArithmetic,
526{
527    fn from(secret_key: &SigningKey<C>) -> Self {
528        secret_key.secret_scalar.into()
529    }
530}
531
532impl<C> TryFrom<&[u8]> for SigningKey<C>
533where
534    C: EcdsaCurve + CurveArithmetic,
535{
536    type Error = Error;
537
538    fn try_from(bytes: &[u8]) -> Result<Self> {
539        Self::from_slice(bytes)
540    }
541}
542
543impl<C> ZeroizeOnDrop for SigningKey<C> where C: EcdsaCurve + CurveArithmetic {}
544
545#[cfg(feature = "algorithm")]
546impl<C> From<SigningKey<C>> for VerifyingKey<C>
547where
548    C: EcdsaCurve + CurveArithmetic,
549    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
550{
551    fn from(signing_key: SigningKey<C>) -> VerifyingKey<C> {
552        signing_key.verifying_key
553    }
554}
555
556#[cfg(feature = "algorithm")]
557impl<C> From<&SigningKey<C>> for VerifyingKey<C>
558where
559    C: EcdsaCurve + CurveArithmetic,
560    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
561{
562    fn from(signing_key: &SigningKey<C>) -> VerifyingKey<C> {
563        signing_key.verifying_key
564    }
565}
566
567#[cfg(feature = "algorithm")]
568impl<C> KeypairRef for SigningKey<C>
569where
570    C: EcdsaCurve + CurveArithmetic,
571    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
572{
573    type VerifyingKey = VerifyingKey<C>;
574}
575
576#[cfg(feature = "pkcs8")]
577impl<C> AssociatedAlgorithmIdentifier for SigningKey<C>
578where
579    C: EcdsaCurve + AssociatedOid + CurveArithmetic,
580    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
581{
582    type Params = ObjectIdentifier;
583
584    const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> =
585        SecretKey::<C>::ALGORITHM_IDENTIFIER;
586}
587
588#[cfg(feature = "pkcs8")]
589impl<C> SignatureAlgorithmIdentifier for SigningKey<C>
590where
591    C: EcdsaCurve + CurveArithmetic,
592    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
593    Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
594{
595    type Params = AnyRef<'static>;
596
597    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
598        Signature::<C>::ALGORITHM_IDENTIFIER;
599}
600
601#[cfg(feature = "pkcs8")]
602impl<C> TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SigningKey<C>
603where
604    C: EcdsaCurve + AssociatedOid + CurveArithmetic,
605    AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
606    FieldBytesSize<C>: sec1::ModulusSize,
607    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
608{
609    type Error = pkcs8::Error;
610
611    fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
612        SecretKey::try_from(private_key_info).map(Into::into)
613    }
614}
615
616#[cfg(all(feature = "alloc", feature = "pkcs8"))]
617impl<C> EncodePrivateKey for SigningKey<C>
618where
619    C: EcdsaCurve + AssociatedOid + CurveArithmetic,
620    AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
621    FieldBytesSize<C>: sec1::ModulusSize,
622    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
623{
624    fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
625        SecretKey::from(self.secret_scalar).to_pkcs8_der()
626    }
627}
628
629#[cfg(feature = "pem")]
630impl<C> FromStr for SigningKey<C>
631where
632    C: EcdsaCurve + AssociatedOid + CurveArithmetic,
633    AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
634    FieldBytesSize<C>: sec1::ModulusSize,
635    Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
636{
637    type Err = Error;
638
639    fn from_str(s: &str) -> Result<Self> {
640        Self::from_pkcs8_pem(s).map_err(|_| Error::new())
641    }
642}