1use crate::{
4 EcdsaCurve, Error, Result, Signature, SignatureSize, SignatureWithOid, ecdsa_oid_for_digest,
5 hazmat::{DigestAlgorithm, bits2field, sign_prehashed_rfc6979},
6};
7use core::fmt::{self, Debug};
8use digest::{Update, block_api::EagerHash, const_oid::AssociatedOid};
9use elliptic_curve::{
10 CurveArithmetic, FieldBytes, Generate, NonZeroScalar, Scalar, SecretKey,
11 array::ArraySize,
12 group::ff::PrimeField,
13 ops::Invert,
14 rand_core::CryptoRng,
15 subtle::{Choice, ConstantTimeEq, CtOption},
16 zeroize::{Zeroize, ZeroizeOnDrop},
17};
18use signature::{
19 DigestSigner, MultipartSigner, RandomizedDigestSigner, RandomizedMultipartSigner,
20 RandomizedSigner, Signer,
21 hazmat::{PrehashSigner, RandomizedPrehashSigner},
22 rand_core::TryCryptoRng,
23};
24
25#[cfg(feature = "der")]
26use {crate::der, core::ops::Add};
27
28#[cfg(feature = "pem")]
29use {core::str::FromStr, elliptic_curve::pkcs8::DecodePrivateKey};
30
31#[cfg(any(feature = "der", feature = "pem"))]
32use elliptic_curve::FieldBytesSize;
33
34#[cfg(feature = "pkcs8")]
35use crate::elliptic_curve::{
36 AffinePoint,
37 pkcs8::{
38 self, ObjectIdentifier,
39 der::AnyRef,
40 spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
41 },
42 sec1::{self, FromSec1Point, ToSec1Point},
43};
44
45#[cfg(feature = "algorithm")]
46use {crate::VerifyingKey, elliptic_curve::PublicKey, signature::KeypairRef};
47
48#[cfg(all(feature = "alloc", feature = "pkcs8"))]
49use elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument};
50
51#[derive(Clone)]
68pub struct SigningKey<C>
69where
70 C: EcdsaCurve + CurveArithmetic,
71{
72 secret_scalar: NonZeroScalar<C>,
74
75 #[cfg(feature = "algorithm")]
77 verifying_key: VerifyingKey<C>,
78}
79
80impl<C> SigningKey<C>
81where
82 C: EcdsaCurve + CurveArithmetic,
83{
84 pub fn from_bytes(bytes: &FieldBytes<C>) -> Result<Self> {
86 SecretKey::<C>::from_bytes(bytes)
87 .map(Into::into)
88 .map_err(|_| Error::new())
89 }
90
91 pub fn from_slice(bytes: &[u8]) -> Result<Self> {
93 SecretKey::<C>::from_slice(bytes)
94 .map(Into::into)
95 .map_err(|_| Error::new())
96 }
97
98 pub fn to_bytes(&self) -> FieldBytes<C> {
100 self.secret_scalar.to_repr()
101 }
102
103 pub fn as_nonzero_scalar(&self) -> &NonZeroScalar<C> {
111 &self.secret_scalar
112 }
113
114 #[cfg(feature = "algorithm")]
116 pub fn verifying_key(&self) -> &VerifyingKey<C> {
117 &self.verifying_key
118 }
119
120 #[deprecated(since = "0.17.0", note = "use the `Generate` trait instead")]
122 pub fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
123 Self::generate_from_rng(rng)
124 }
125}
126
127impl<C> Generate for SigningKey<C>
128where
129 C: EcdsaCurve + CurveArithmetic,
130{
131 fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(
132 rng: &mut R,
133 ) -> core::result::Result<Self, R::Error> {
134 NonZeroScalar::<C>::try_generate_from_rng(rng).map(Into::into)
135 }
136}
137
138impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C>
147where
148 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
149 D: EagerHash + Update,
150 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
151 SignatureSize<C>: ArraySize,
152{
153 fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<Signature<C>> {
154 let mut digest = D::new();
155 f(&mut digest)?;
156 self.sign_prehash(&digest.finalize())
157 }
158}
159
160impl<C> PrehashSigner<Signature<C>> for SigningKey<C>
165where
166 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
167 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
168 SignatureSize<C>: ArraySize,
169{
170 fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature<C>> {
171 let z = bits2field::<C>(prehash)?;
172 Ok(sign_prehashed_rfc6979::<C, C::Digest>(&self.secret_scalar, &z, &[])?.0)
173 }
174}
175
176impl<C> Signer<Signature<C>> for SigningKey<C>
181where
182 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
183 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
184 SignatureSize<C>: ArraySize,
185{
186 fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> {
187 self.try_multipart_sign(&[msg])
188 }
189}
190
191impl<C> MultipartSigner<Signature<C>> for SigningKey<C>
192where
193 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
194 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
195 SignatureSize<C>: ArraySize,
196{
197 fn try_multipart_sign(&self, msg: &[&[u8]]) -> core::result::Result<Signature<C>, Error> {
198 self.try_sign_digest(|digest: &mut C::Digest| {
199 msg.iter().for_each(|slice| digest.update(slice));
200 Ok(())
201 })
202 }
203}
204
205impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C>
206where
207 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
208 D: EagerHash + Update,
209 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
210 SignatureSize<C>: ArraySize,
211{
212 fn try_sign_digest_with_rng<R: TryCryptoRng + ?Sized, F: Fn(&mut D) -> Result<()>>(
213 &self,
214 rng: &mut R,
215 f: F,
216 ) -> Result<Signature<C>> {
217 let mut digest = D::new();
218 f(&mut digest)?;
219 self.sign_prehash_with_rng(rng, &digest.finalize())
220 }
221}
222
223impl<C> RandomizedPrehashSigner<Signature<C>> for SigningKey<C>
224where
225 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
226 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
227 SignatureSize<C>: ArraySize,
228{
229 fn sign_prehash_with_rng<R: TryCryptoRng + ?Sized>(
230 &self,
231 rng: &mut R,
232 prehash: &[u8],
233 ) -> Result<Signature<C>> {
234 let z = bits2field::<C>(prehash)?;
235
236 loop {
237 let mut ad = FieldBytes::<C>::default();
238 rng.try_fill_bytes(&mut ad).map_err(|_| Error::new())?;
239
240 if let Ok((signature, _)) =
241 sign_prehashed_rfc6979::<C, C::Digest>(&self.secret_scalar, &z, &ad)
242 {
243 break Ok(signature);
244 }
245 }
246 }
247}
248
249impl<C> RandomizedSigner<Signature<C>> for SigningKey<C>
250where
251 Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
252 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
253 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
254 SignatureSize<C>: ArraySize,
255{
256 fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
257 &self,
258 rng: &mut R,
259 msg: &[u8],
260 ) -> Result<Signature<C>> {
261 self.try_multipart_sign_with_rng(rng, &[msg])
262 }
263}
264
265impl<C> RandomizedMultipartSigner<Signature<C>> for SigningKey<C>
266where
267 Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
268 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
269 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
270 SignatureSize<C>: ArraySize,
271{
272 fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
273 &self,
274 rng: &mut R,
275 msg: &[&[u8]],
276 ) -> Result<Signature<C>> {
277 self.try_sign_digest_with_rng(rng, |digest: &mut C::Digest| {
278 msg.iter().for_each(|slice| digest.update(slice));
279 Ok(())
280 })
281 }
282}
283
284impl<C, D> DigestSigner<D, SignatureWithOid<C>> for SigningKey<C>
285where
286 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
287 D: AssociatedOid + EagerHash + Update,
288 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
289 SignatureSize<C>: ArraySize,
290{
291 fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<SignatureWithOid<C>> {
292 let signature: Signature<C> = self.try_sign_digest(f)?;
293 let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
294 SignatureWithOid::new(signature, oid)
295 }
296}
297
298impl<C> Signer<SignatureWithOid<C>> for SigningKey<C>
299where
300 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
301 C::Digest: AssociatedOid,
302 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
303 SignatureSize<C>: ArraySize,
304{
305 fn try_sign(&self, msg: &[u8]) -> Result<SignatureWithOid<C>> {
306 self.try_multipart_sign(&[msg])
307 }
308}
309
310impl<C> MultipartSigner<SignatureWithOid<C>> for SigningKey<C>
311where
312 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
313 C::Digest: AssociatedOid,
314 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
315 SignatureSize<C>: ArraySize,
316{
317 fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<SignatureWithOid<C>> {
318 self.try_sign_digest(|digest: &mut C::Digest| {
319 msg.iter().for_each(|slice| digest.update(slice));
320 Ok(())
321 })
322 }
323}
324
325#[cfg(feature = "der")]
326impl<C> PrehashSigner<der::Signature<C>> for SigningKey<C>
327where
328 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
329 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
330 SignatureSize<C>: ArraySize,
331 der::MaxSize<C>: ArraySize,
332 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
333{
334 fn sign_prehash(&self, prehash: &[u8]) -> Result<der::Signature<C>> {
335 PrehashSigner::<Signature<C>>::sign_prehash(self, prehash).map(Into::into)
336 }
337}
338
339#[cfg(feature = "der")]
340impl<C> Signer<der::Signature<C>> for SigningKey<C>
341where
342 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
343 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
344 SignatureSize<C>: ArraySize,
345 der::MaxSize<C>: ArraySize,
346 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
347{
348 fn try_sign(&self, msg: &[u8]) -> Result<der::Signature<C>> {
349 Signer::<Signature<C>>::try_sign(self, msg).map(Into::into)
350 }
351}
352
353#[cfg(feature = "der")]
354impl<C, D> RandomizedDigestSigner<D, der::Signature<C>> for SigningKey<C>
355where
356 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
357 D: EagerHash + Update,
358 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
359 SignatureSize<C>: ArraySize,
360 der::MaxSize<C>: ArraySize,
361 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
362{
363 fn try_sign_digest_with_rng<R: TryCryptoRng + ?Sized, F: Fn(&mut D) -> Result<()>>(
364 &self,
365 rng: &mut R,
366 f: F,
367 ) -> Result<der::Signature<C>> {
368 RandomizedDigestSigner::<D, Signature<C>>::try_sign_digest_with_rng(self, rng, f)
369 .map(Into::into)
370 }
371}
372
373#[cfg(feature = "der")]
374impl<C> RandomizedPrehashSigner<der::Signature<C>> for SigningKey<C>
375where
376 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
377 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
378 SignatureSize<C>: ArraySize,
379 der::MaxSize<C>: ArraySize,
380 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
381{
382 fn sign_prehash_with_rng<R: TryCryptoRng + ?Sized>(
383 &self,
384 rng: &mut R,
385 prehash: &[u8],
386 ) -> Result<der::Signature<C>> {
387 RandomizedPrehashSigner::<Signature<C>>::sign_prehash_with_rng(self, rng, prehash)
388 .map(Into::into)
389 }
390}
391
392#[cfg(feature = "der")]
393impl<D, C> DigestSigner<D, der::Signature<C>> for SigningKey<C>
394where
395 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
396 D: EagerHash + Update,
397 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
398 SignatureSize<C>: ArraySize,
399 der::MaxSize<C>: ArraySize,
400 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
401{
402 fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<der::Signature<C>> {
403 DigestSigner::<D, Signature<C>>::try_sign_digest(self, f).map(Into::into)
404 }
405}
406
407#[cfg(feature = "der")]
408impl<C> RandomizedSigner<der::Signature<C>> for SigningKey<C>
409where
410 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
411 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
412 SignatureSize<C>: ArraySize,
413 der::MaxSize<C>: ArraySize,
414 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
415{
416 fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
417 &self,
418 rng: &mut R,
419 msg: &[u8],
420 ) -> Result<der::Signature<C>> {
421 RandomizedSigner::<Signature<C>>::try_sign_with_rng(self, rng, msg).map(Into::into)
422 }
423}
424
425#[cfg(feature = "der")]
426impl<C> RandomizedMultipartSigner<der::Signature<C>> for SigningKey<C>
427where
428 C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
429 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
430 SignatureSize<C>: ArraySize,
431 der::MaxSize<C>: ArraySize,
432 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
433{
434 fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
435 &self,
436 rng: &mut R,
437 msg: &[&[u8]],
438 ) -> Result<der::Signature<C>> {
439 RandomizedMultipartSigner::<Signature<C>>::try_multipart_sign_with_rng(self, rng, msg)
440 .map(Into::into)
441 }
442}
443
444#[cfg(feature = "algorithm")]
449impl<C> AsRef<VerifyingKey<C>> for SigningKey<C>
450where
451 C: EcdsaCurve + CurveArithmetic,
452 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
453 SignatureSize<C>: ArraySize,
454{
455 fn as_ref(&self) -> &VerifyingKey<C> {
456 &self.verifying_key
457 }
458}
459
460impl<C> ConstantTimeEq for SigningKey<C>
461where
462 C: EcdsaCurve + CurveArithmetic,
463 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
464 SignatureSize<C>: ArraySize,
465{
466 fn ct_eq(&self, other: &Self) -> Choice {
467 self.secret_scalar.ct_eq(&other.secret_scalar)
468 }
469}
470
471impl<C> Debug for SigningKey<C>
472where
473 C: EcdsaCurve + CurveArithmetic,
474{
475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476 f.debug_struct("SigningKey").finish_non_exhaustive()
477 }
478}
479
480impl<C> Drop for SigningKey<C>
481where
482 C: EcdsaCurve + CurveArithmetic,
483{
484 fn drop(&mut self) {
485 self.secret_scalar.zeroize();
486 }
487}
488
489impl<C> Eq for SigningKey<C>
491where
492 C: EcdsaCurve + CurveArithmetic,
493 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
494 SignatureSize<C>: ArraySize,
495{
496}
497impl<C> PartialEq for SigningKey<C>
498where
499 C: EcdsaCurve + CurveArithmetic,
500 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
501 SignatureSize<C>: ArraySize,
502{
503 fn eq(&self, other: &SigningKey<C>) -> bool {
504 self.ct_eq(other).into()
505 }
506}
507
508impl<C> From<NonZeroScalar<C>> for SigningKey<C>
509where
510 C: EcdsaCurve + CurveArithmetic,
511{
512 fn from(secret_scalar: NonZeroScalar<C>) -> Self {
513 #[cfg(feature = "algorithm")]
514 let public_key = PublicKey::from_secret_scalar(&secret_scalar);
515
516 Self {
517 secret_scalar,
518 #[cfg(feature = "algorithm")]
519 verifying_key: public_key.into(),
520 }
521 }
522}
523
524impl<C> From<SecretKey<C>> for SigningKey<C>
525where
526 C: EcdsaCurve + CurveArithmetic,
527{
528 fn from(secret_key: SecretKey<C>) -> Self {
529 Self::from(&secret_key)
530 }
531}
532
533impl<C> From<&SecretKey<C>> for SigningKey<C>
534where
535 C: EcdsaCurve + CurveArithmetic,
536{
537 fn from(secret_key: &SecretKey<C>) -> Self {
538 secret_key.to_nonzero_scalar().into()
539 }
540}
541
542impl<C> From<SigningKey<C>> for SecretKey<C>
543where
544 C: EcdsaCurve + CurveArithmetic,
545 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
546 SignatureSize<C>: ArraySize,
547{
548 fn from(key: SigningKey<C>) -> Self {
549 key.secret_scalar.into()
550 }
551}
552
553impl<C> From<&SigningKey<C>> for SecretKey<C>
554where
555 C: EcdsaCurve + CurveArithmetic,
556{
557 fn from(secret_key: &SigningKey<C>) -> Self {
558 secret_key.secret_scalar.into()
559 }
560}
561
562impl<C> TryFrom<&[u8]> for SigningKey<C>
563where
564 C: EcdsaCurve + CurveArithmetic,
565{
566 type Error = Error;
567
568 fn try_from(bytes: &[u8]) -> Result<Self> {
569 Self::from_slice(bytes)
570 }
571}
572
573impl<C> ZeroizeOnDrop for SigningKey<C> where C: EcdsaCurve + CurveArithmetic {}
574
575#[cfg(feature = "algorithm")]
576impl<C> From<SigningKey<C>> for VerifyingKey<C>
577where
578 C: EcdsaCurve + CurveArithmetic,
579 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
580 SignatureSize<C>: ArraySize,
581{
582 fn from(signing_key: SigningKey<C>) -> VerifyingKey<C> {
583 signing_key.verifying_key
584 }
585}
586
587#[cfg(feature = "algorithm")]
588impl<C> From<&SigningKey<C>> for VerifyingKey<C>
589where
590 C: EcdsaCurve + CurveArithmetic,
591 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
592 SignatureSize<C>: ArraySize,
593{
594 fn from(signing_key: &SigningKey<C>) -> VerifyingKey<C> {
595 signing_key.verifying_key
596 }
597}
598
599#[cfg(feature = "algorithm")]
600impl<C> KeypairRef for SigningKey<C>
601where
602 C: EcdsaCurve + CurveArithmetic,
603 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
604 SignatureSize<C>: ArraySize,
605{
606 type VerifyingKey = VerifyingKey<C>;
607}
608
609#[cfg(feature = "pkcs8")]
610impl<C> AssociatedAlgorithmIdentifier for SigningKey<C>
611where
612 C: EcdsaCurve + AssociatedOid + CurveArithmetic,
613 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
614 SignatureSize<C>: ArraySize,
615{
616 type Params = ObjectIdentifier;
617
618 const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> =
619 SecretKey::<C>::ALGORITHM_IDENTIFIER;
620}
621
622#[cfg(feature = "pkcs8")]
623impl<C> SignatureAlgorithmIdentifier for SigningKey<C>
624where
625 C: EcdsaCurve + CurveArithmetic,
626 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
627 SignatureSize<C>: ArraySize,
628 Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
629{
630 type Params = AnyRef<'static>;
631
632 const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
633 Signature::<C>::ALGORITHM_IDENTIFIER;
634}
635
636#[cfg(feature = "pkcs8")]
637impl<C> TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SigningKey<C>
638where
639 C: EcdsaCurve + AssociatedOid + CurveArithmetic,
640 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
641 FieldBytesSize<C>: sec1::ModulusSize,
642 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
643 SignatureSize<C>: ArraySize,
644{
645 type Error = pkcs8::Error;
646
647 fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
648 SecretKey::try_from(private_key_info).map(Into::into)
649 }
650}
651
652#[cfg(all(feature = "alloc", feature = "pkcs8"))]
653impl<C> EncodePrivateKey for SigningKey<C>
654where
655 C: EcdsaCurve + AssociatedOid + CurveArithmetic,
656 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
657 FieldBytesSize<C>: sec1::ModulusSize,
658 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
659 SignatureSize<C>: ArraySize,
660{
661 fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
662 SecretKey::from(self.secret_scalar).to_pkcs8_der()
663 }
664}
665
666#[cfg(feature = "pem")]
667impl<C> FromStr for SigningKey<C>
668where
669 C: EcdsaCurve + AssociatedOid + CurveArithmetic,
670 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
671 FieldBytesSize<C>: sec1::ModulusSize,
672 Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
673 SignatureSize<C>: ArraySize,
674{
675 type Err = Error;
676
677 fn from_str(s: &str) -> Result<Self> {
678 Self::from_pkcs8_pem(s).map_err(|_| Error::new())
679 }
680}