1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8
9#[cfg(feature = "alloc")]
37extern crate alloc;
38
39mod recovery;
40
41#[cfg(feature = "der")]
42pub mod der;
43#[cfg(feature = "dev")]
44pub mod dev;
45#[cfg(feature = "algorithm")]
46pub mod hazmat;
47#[cfg(feature = "algorithm")]
48mod signing;
49#[cfg(feature = "algorithm")]
50mod verifying;
51
52pub use crate::recovery::RecoveryId;
53
54pub use elliptic_curve::{self, PrimeCurve, sec1::Sec1Point};
56
57pub use signature::{self, Error, Result, SignatureEncoding};
59use zeroize::Zeroize;
60
61#[cfg(feature = "algorithm")]
62pub use crate::signing::SigningKey;
63#[cfg(feature = "algorithm")]
64pub use crate::verifying::VerifyingKey;
65
66use core::{fmt, ops::Add};
67use elliptic_curve::{
68 Curve, FieldBytes, FieldBytesSize, ScalarValue,
69 array::{Array, ArraySize, typenum::Unsigned},
70};
71
72#[cfg(feature = "alloc")]
73use alloc::vec::Vec;
74#[cfg(feature = "digest")]
75use digest::{
76 Digest, FixedOutput,
77 common::BlockSizeUser,
78 const_oid::{AssociatedOid, ObjectIdentifier},
79};
80#[cfg(all(feature = "alloc", feature = "pkcs8"))]
81use elliptic_curve::pkcs8::spki::{
82 self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier,
83};
84#[cfg(feature = "pkcs8")]
85use elliptic_curve::pkcs8::spki::{
86 AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, der::AnyRef,
87};
88#[cfg(feature = "serde")]
89use serdect::serde::{Deserialize, Serialize, de, ser};
90#[cfg(feature = "algorithm")]
91use {
92 core::str,
93 elliptic_curve::{
94 CurveArithmetic, NonZeroScalar, scalar::IsHigh, subtle::ConditionallySelectable,
95 },
96};
97
98#[cfg(feature = "digest")]
106pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1");
107
108#[cfg(feature = "digest")]
115pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
116
117#[cfg(feature = "digest")]
124pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3");
125
126#[cfg(feature = "digest")]
133pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
134
135#[cfg(feature = "digest")]
136const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
137#[cfg(feature = "digest")]
138const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
139#[cfg(feature = "digest")]
140const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
141#[cfg(feature = "digest")]
142const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
143
144pub trait EcdsaCurve:
146 Curve<FieldBytesSize: Add<Output: ArraySize<ArrayType<u8>: Copy>>> + PrimeCurve
147{
148 const NORMALIZE_S: bool;
152}
153
154pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
156
157pub type SignatureBytes<C> = Array<u8, SignatureSize<C>>;
159
160#[derive(Clone, Copy, Eq, PartialEq)]
185pub struct Signature<C: EcdsaCurve> {
186 r: ScalarValue<C>,
187 s: ScalarValue<C>,
188}
189
190impl<C> Signature<C>
191where
192 C: EcdsaCurve,
193{
194 pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
201 let chunks = FieldBytes::<C>::slice_as_chunks(bytes).0;
202 let r = chunks[0];
203 let s = chunks[1];
204 Self::from_scalars(r, s)
205 }
206
207 pub fn from_slice(slice: &[u8]) -> Result<Self> {
213 <&SignatureBytes<C>>::try_from(slice)
214 .map_err(|_| Error::new())
215 .and_then(Self::from_bytes)
216 }
217
218 #[cfg(feature = "der")]
223 pub fn from_der(bytes: &[u8]) -> Result<Self>
224 where
225 der::MaxSize<C>: ArraySize,
226 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
227 {
228 der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
229 }
230
231 pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
238 let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?;
239 let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?;
240
241 if r.is_zero().into() || s.is_zero().into() {
242 return Err(Error::new());
243 }
244
245 Ok(Self { r, s })
246 }
247
248 pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
250 (self.r.to_bytes(), self.s.to_bytes())
251 }
252
253 pub fn to_bytes(&self) -> SignatureBytes<C> {
255 let mut bytes = SignatureBytes::<C>::default();
256 let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
257 r_bytes.copy_from_slice(&self.r.to_bytes());
258 s_bytes.copy_from_slice(&self.s.to_bytes());
259 bytes
260 }
261
262 #[cfg(feature = "der")]
264 #[allow(clippy::missing_panics_doc, reason = "should not panic in practice")]
265 pub fn to_der(&self) -> der::Signature<C>
266 where
267 der::MaxSize<C>: ArraySize,
268 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
269 {
270 let (r, s) = self.split_bytes();
271 der::Signature::from_components(&r, &s).expect("DER encoding error")
272 }
273
274 #[cfg(feature = "alloc")]
276 pub fn to_vec(&self) -> Vec<u8> {
277 self.to_bytes().to_vec()
278 }
279}
280
281#[cfg(feature = "algorithm")]
282impl<C> Signature<C>
283where
284 C: EcdsaCurve + CurveArithmetic,
285{
286 pub fn r(&self) -> NonZeroScalar<C> {
288 NonZeroScalar::new(self.r.into()).unwrap()
289 }
290
291 pub fn s(&self) -> NonZeroScalar<C> {
293 NonZeroScalar::new(self.s.into()).unwrap()
294 }
295
296 pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
298 (self.r(), self.s())
299 }
300
301 #[must_use]
305 pub fn normalize_s(&self) -> Self {
306 let mut result = *self;
307 let s_inv = ScalarValue::from(-self.s());
308 result.s.conditional_assign(&s_inv, self.s.is_high());
309 result
310 }
311}
312
313impl<C> From<Signature<C>> for SignatureBytes<C>
314where
315 C: EcdsaCurve,
316{
317 fn from(signature: Signature<C>) -> SignatureBytes<C> {
318 signature.to_bytes()
319 }
320}
321
322impl<C> SignatureEncoding for Signature<C>
323where
324 C: EcdsaCurve,
325{
326 type Repr = SignatureBytes<C>;
327}
328
329impl<C> TryFrom<&[u8]> for Signature<C>
330where
331 C: EcdsaCurve,
332{
333 type Error = Error;
334
335 fn try_from(slice: &[u8]) -> Result<Self> {
336 Self::from_slice(slice)
337 }
338}
339
340impl<C> fmt::Debug for Signature<C>
341where
342 C: EcdsaCurve,
343{
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
346
347 for byte in self.to_bytes() {
348 write!(f, "{byte:02X}")?;
349 }
350
351 write!(f, ")")
352 }
353}
354
355impl<C> fmt::Display for Signature<C>
356where
357 C: EcdsaCurve,
358{
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 write!(f, "{self:X}")
361 }
362}
363
364impl<C> core::hash::Hash for Signature<C>
365where
366 C: EcdsaCurve,
367{
368 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
369 self.to_bytes().hash(state);
370 }
371}
372
373impl<C> fmt::LowerHex for Signature<C>
374where
375 C: EcdsaCurve,
376{
377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378 for byte in self.to_bytes() {
379 write!(f, "{byte:02x}")?;
380 }
381 Ok(())
382 }
383}
384
385impl<C> fmt::UpperHex for Signature<C>
386where
387 C: EcdsaCurve,
388{
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 for byte in self.to_bytes() {
391 write!(f, "{byte:02X}")?;
392 }
393 Ok(())
394 }
395}
396
397#[cfg(feature = "algorithm")]
398impl<C> str::FromStr for Signature<C>
399where
400 C: EcdsaCurve + CurveArithmetic,
401{
402 type Err = Error;
403
404 fn from_str(hex: &str) -> Result<Self> {
405 if hex.len() != C::FieldBytesSize::USIZE * 4 {
406 return Err(Error::new());
407 }
408
409 let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
410
411 let r = r_hex
412 .parse::<NonZeroScalar<C>>()
413 .map_err(|_| Error::new())?;
414
415 let s = s_hex
416 .parse::<NonZeroScalar<C>>()
417 .map_err(|_| Error::new())?;
418
419 Self::from_scalars(r, s)
420 }
421}
422
423#[cfg(feature = "digest")]
428impl<C> AssociatedOid for Signature<C>
429where
430 C: DigestAlgorithm,
431 C::Digest: AssociatedOid,
432{
433 const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) {
434 Some(oid) => oid,
435 None => panic!("no RFC5758 ECDSA OID defined for DigestAlgorithm::Digest"),
436 };
437}
438
439#[cfg(feature = "pkcs8")]
442impl<C> AssociatedAlgorithmIdentifier for Signature<C>
443where
444 C: EcdsaCurve,
445 Self: AssociatedOid,
446{
447 type Params = AnyRef<'static>;
448
449 const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
450 oid: Self::OID,
451 parameters: None,
452 };
453}
454
455#[cfg(feature = "serde")]
456impl<C> Serialize for Signature<C>
457where
458 C: EcdsaCurve,
459{
460 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
461 where
462 S: ser::Serializer,
463 {
464 serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
465 }
466}
467
468#[cfg(feature = "serde")]
469impl<'de, C> Deserialize<'de> for Signature<C>
470where
471 C: EcdsaCurve,
472{
473 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
474 where
475 D: de::Deserializer<'de>,
476 {
477 let mut bytes = SignatureBytes::<C>::default();
478 serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
479 Self::try_from(bytes.as_slice()).map_err(de::Error::custom)
480 }
481}
482
483impl<C: EcdsaCurve> Zeroize for Signature<C> {
484 fn zeroize(&mut self) {
485 self.r = ScalarValue::ONE;
486 self.s = ScalarValue::ONE;
487 }
488}
489
490#[cfg(feature = "digest")]
502#[derive(Clone, Copy, Debug, Eq, PartialEq)]
503pub struct SignatureWithOid<C: EcdsaCurve> {
504 signature: Signature<C>,
506
507 oid: ObjectIdentifier,
513}
514
515#[cfg(feature = "digest")]
516impl<C> SignatureWithOid<C>
517where
518 C: EcdsaCurve,
519{
520 pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
529 if !oid.starts_with(ObjectIdentifier::new_unwrap("1.2.840.10045.4")) {
530 return Err(Error::new());
531 }
532
533 Ok(Self { signature, oid })
534 }
535
536 pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
546 where
547 D: AssociatedOid + Digest,
548 {
549 let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
550 Ok(Self { signature, oid })
551 }
552
553 pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
559 where
560 D: AssociatedOid + Digest,
561 {
562 Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
563 }
564
565 pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
571 where
572 D: AssociatedOid + Digest,
573 {
574 Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
575 }
576
577 #[cfg(feature = "der")]
583 pub fn from_der_with_digest<D>(der_bytes: &[u8]) -> Result<Self>
584 where
585 D: AssociatedOid + Digest,
586 der::MaxSize<C>: ArraySize,
587 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
588 {
589 Self::new_with_digest::<D>(Signature::<C>::from_der(der_bytes)?)
590 }
591
592 #[cfg(feature = "der")]
598 pub fn from_der_with_oid(der_bytes: &[u8], oid: ObjectIdentifier) -> Result<Self>
599 where
600 der::MaxSize<C>: ArraySize,
601 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
602 {
603 Self::new(Signature::<C>::from_der(der_bytes)?, oid)
604 }
605
606 pub fn signature(&self) -> &Signature<C> {
608 &self.signature
609 }
610
611 pub fn oid(&self) -> ObjectIdentifier {
613 self.oid
614 }
615
616 pub fn to_bytes(&self) -> SignatureBytes<C>
618where {
619 self.signature.to_bytes()
620 }
621
622 #[cfg(feature = "der")]
628 pub fn to_der(&self) -> der::Signature<C>
629 where
630 der::MaxSize<C>: ArraySize,
631 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
632 {
633 self.signature.into()
634 }
635}
636
637#[cfg(feature = "digest")]
642pub trait DigestAlgorithm: EcdsaCurve {
643 type Digest: BlockSizeUser + Digest + FixedOutput;
646}
647
648#[cfg(feature = "digest")]
649impl<C> core::hash::Hash for SignatureWithOid<C>
650where
651 C: EcdsaCurve,
652{
653 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
654 self.signature.hash(state);
655 self.oid.hash(state);
656 }
657}
658
659#[cfg(feature = "digest")]
660impl<C> From<SignatureWithOid<C>> for Signature<C>
661where
662 C: EcdsaCurve,
663{
664 fn from(sig: SignatureWithOid<C>) -> Signature<C> {
665 sig.signature
666 }
667}
668
669#[cfg(feature = "digest")]
670impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
671where
672 C: EcdsaCurve,
673{
674 fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
675 signature.to_bytes()
676 }
677}
678
679#[cfg(all(feature = "der", feature = "digest"))]
680impl<C> From<SignatureWithOid<C>> for der::Signature<C>
681where
682 C: EcdsaCurve,
683 der::MaxSize<C>: ArraySize,
684 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
685{
686 fn from(sig: SignatureWithOid<C>) -> der::Signature<C> {
687 sig.to_der()
688 }
689}
690
691#[cfg(all(feature = "der", feature = "digest"))]
692impl<C> From<&SignatureWithOid<C>> for der::Signature<C>
693where
694 C: EcdsaCurve,
695 der::MaxSize<C>: ArraySize,
696 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
697{
698 fn from(sig: &SignatureWithOid<C>) -> der::Signature<C> {
699 sig.to_der()
700 }
701}
702
703#[cfg(feature = "digest")]
709impl<C> SignatureEncoding for SignatureWithOid<C>
710where
711 C: DigestAlgorithm,
712 C::Digest: AssociatedOid,
713{
714 type Repr = SignatureBytes<C>;
715}
716
717#[cfg(feature = "digest")]
723impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
724where
725 C: DigestAlgorithm,
726 C::Digest: AssociatedOid,
727{
728 type Error = Error;
729
730 fn try_from(slice: &[u8]) -> Result<Self> {
731 Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
732 }
733}
734
735#[cfg(all(feature = "alloc", feature = "pkcs8"))]
736impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
737where
738 C: EcdsaCurve,
739{
740 fn algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
741 Ok(AlgorithmIdentifierOwned {
742 oid: self.oid,
743 parameters: None,
744 })
745 }
746}
747
748#[cfg(feature = "digest")]
750const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
751 match digest_oid {
752 SHA224_OID => Some(ECDSA_SHA224_OID),
753 SHA256_OID => Some(ECDSA_SHA256_OID),
754 SHA384_OID => Some(ECDSA_SHA384_OID),
755 SHA512_OID => Some(ECDSA_SHA512_OID),
756 _ => None,
757 }
758}