ecdsa/
signing.rs

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