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#![forbid(unsafe_code)]
9#![warn(
10 clippy::cast_lossless,
11 clippy::cast_possible_truncation,
12 clippy::cast_possible_wrap,
13 clippy::cast_precision_loss,
14 clippy::cast_sign_loss,
15 clippy::checked_conversions,
16 clippy::implicit_saturating_sub,
17 clippy::panic,
18 clippy::panic_in_result_fn,
19 clippy::unwrap_used,
20 missing_docs,
21 rust_2018_idioms,
22 unused_lifetimes,
23 unused_qualifications,
24 unreachable_pub
25)]
26
27#[cfg(feature = "alloc")]
59extern crate alloc;
60
61mod recovery;
62
63#[cfg(feature = "der")]
64pub mod der;
65#[cfg(feature = "dev")]
66pub mod dev;
67#[cfg(feature = "hazmat")]
68pub mod hazmat;
69#[cfg(feature = "algorithm")]
70mod signing;
71#[cfg(feature = "algorithm")]
72mod verifying;
73
74pub use crate::recovery::RecoveryId;
75
76pub use elliptic_curve::{self, PrimeCurve, sec1::Sec1Point};
78
79pub use signature::{self, Error, Result, SignatureEncoding};
81use zeroize::Zeroize;
82
83#[cfg(feature = "algorithm")]
84pub use crate::signing::SigningKey;
85#[cfg(feature = "algorithm")]
86pub use crate::verifying::VerifyingKey;
87
88use core::{fmt, ops::Add};
89use elliptic_curve::{
90 FieldBytes, FieldBytesSize, ScalarValue,
91 array::{Array, ArraySize, typenum::Unsigned},
92};
93
94#[cfg(feature = "alloc")]
95use alloc::vec::Vec;
96
97#[cfg(feature = "algorithm")]
98use {
99 core::str,
100 elliptic_curve::{
101 CurveArithmetic, NonZeroScalar, scalar::IsHigh, subtle::ConditionallySelectable,
102 },
103};
104
105#[cfg(feature = "digest")]
106use digest::{
107 Digest,
108 const_oid::{AssociatedOid, ObjectIdentifier},
109};
110
111#[cfg(feature = "pkcs8")]
112use elliptic_curve::pkcs8::spki::{
113 AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, der::AnyRef,
114};
115
116#[cfg(feature = "serde")]
117use serdect::serde::{Deserialize, Serialize, de, ser};
118
119#[cfg(all(feature = "alloc", feature = "pkcs8"))]
120use elliptic_curve::pkcs8::spki::{
121 self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier,
122};
123
124#[cfg(feature = "digest")]
132pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1");
133
134#[cfg(feature = "digest")]
141pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
142
143#[cfg(feature = "digest")]
150pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3");
151
152#[cfg(feature = "digest")]
159pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
160
161#[cfg(feature = "digest")]
162const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
163#[cfg(feature = "digest")]
164const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
165#[cfg(feature = "digest")]
166const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
167#[cfg(feature = "digest")]
168const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
169
170pub trait EcdsaCurve: PrimeCurve {
172 const NORMALIZE_S: bool;
176}
177
178pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
180
181pub type SignatureBytes<C> = Array<u8, SignatureSize<C>>;
183
184#[derive(Clone, Eq, PartialEq)]
211pub struct Signature<C: EcdsaCurve> {
212 r: ScalarValue<C>,
213 s: ScalarValue<C>,
214}
215
216impl<C> Signature<C>
217where
218 C: EcdsaCurve,
219 SignatureSize<C>: ArraySize,
220{
221 pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
230 let chunks = FieldBytes::<C>::slice_as_chunks(bytes).0;
231 let r = chunks[0];
232 let s = chunks[1];
233 Self::from_scalars(r, s)
234 }
235
236 pub fn from_slice(slice: &[u8]) -> Result<Self> {
238 <&SignatureBytes<C>>::try_from(slice)
239 .map_err(|_| Error::new())
240 .and_then(Self::from_bytes)
241 }
242
243 #[cfg(feature = "der")]
245 pub fn from_der(bytes: &[u8]) -> Result<Self>
246 where
247 der::MaxSize<C>: ArraySize,
248 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
249 {
250 der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
251 }
252
253 pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
262 let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?;
263 let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?;
264
265 if r.is_zero().into() || s.is_zero().into() {
266 return Err(Error::new());
267 }
268
269 Ok(Self { r, s })
270 }
271
272 pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
274 (self.r.to_bytes(), self.s.to_bytes())
275 }
276
277 pub fn to_bytes(&self) -> SignatureBytes<C> {
279 let mut bytes = SignatureBytes::<C>::default();
280 let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
281 r_bytes.copy_from_slice(&self.r.to_bytes());
282 s_bytes.copy_from_slice(&self.s.to_bytes());
283 bytes
284 }
285
286 #[cfg(feature = "der")]
288 pub fn to_der(&self) -> der::Signature<C>
289 where
290 der::MaxSize<C>: ArraySize,
291 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
292 {
293 let (r, s) = self.split_bytes();
294 der::Signature::from_components(&r, &s).expect("DER encoding error")
295 }
296
297 #[cfg(feature = "alloc")]
299 pub fn to_vec(&self) -> Vec<u8> {
300 self.to_bytes().to_vec()
301 }
302}
303
304#[cfg(feature = "algorithm")]
305impl<C> Signature<C>
306where
307 C: EcdsaCurve + CurveArithmetic,
308 SignatureSize<C>: ArraySize,
309{
310 pub fn r(&self) -> NonZeroScalar<C> {
312 NonZeroScalar::new(self.r.into()).unwrap()
313 }
314
315 pub fn s(&self) -> NonZeroScalar<C> {
317 NonZeroScalar::new(self.s.into()).unwrap()
318 }
319
320 pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
322 (self.r(), self.s())
323 }
324
325 pub fn normalize_s(&self) -> Self {
330 let mut result = self.clone();
331 let s_inv = ScalarValue::from(-self.s());
332 result.s.conditional_assign(&s_inv, self.s.is_high());
333 result
334 }
335}
336
337impl<C> Copy for Signature<C>
338where
339 C: EcdsaCurve,
340 SignatureSize<C>: ArraySize,
341 <SignatureSize<C> as ArraySize>::ArrayType<u8>: Copy,
342{
343}
344
345impl<C> From<Signature<C>> for SignatureBytes<C>
346where
347 C: EcdsaCurve,
348 SignatureSize<C>: ArraySize,
349{
350 fn from(signature: Signature<C>) -> SignatureBytes<C> {
351 signature.to_bytes()
352 }
353}
354
355impl<C> SignatureEncoding for Signature<C>
356where
357 C: EcdsaCurve,
358 SignatureSize<C>: ArraySize,
359{
360 type Repr = SignatureBytes<C>;
361}
362
363impl<C> TryFrom<&[u8]> for Signature<C>
364where
365 C: EcdsaCurve,
366 SignatureSize<C>: ArraySize,
367{
368 type Error = Error;
369
370 fn try_from(slice: &[u8]) -> Result<Self> {
371 Self::from_slice(slice)
372 }
373}
374
375impl<C> fmt::Debug for Signature<C>
376where
377 C: EcdsaCurve,
378 SignatureSize<C>: ArraySize,
379{
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
382
383 for byte in self.to_bytes() {
384 write!(f, "{byte:02X}")?;
385 }
386
387 write!(f, ")")
388 }
389}
390
391impl<C> fmt::Display for Signature<C>
392where
393 C: EcdsaCurve,
394 SignatureSize<C>: ArraySize,
395{
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 write!(f, "{self:X}")
398 }
399}
400
401impl<C> core::hash::Hash for Signature<C>
402where
403 C: EcdsaCurve,
404 SignatureSize<C>: ArraySize,
405{
406 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
407 self.to_bytes().hash(state);
408 }
409}
410
411impl<C> fmt::LowerHex for Signature<C>
412where
413 C: EcdsaCurve,
414 SignatureSize<C>: ArraySize,
415{
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 for byte in self.to_bytes() {
418 write!(f, "{byte:02x}")?;
419 }
420 Ok(())
421 }
422}
423
424impl<C> fmt::UpperHex for Signature<C>
425where
426 C: EcdsaCurve,
427 SignatureSize<C>: ArraySize,
428{
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 for byte in self.to_bytes() {
431 write!(f, "{byte:02X}")?;
432 }
433 Ok(())
434 }
435}
436
437#[cfg(feature = "algorithm")]
438impl<C> str::FromStr for Signature<C>
439where
440 C: EcdsaCurve + CurveArithmetic,
441 SignatureSize<C>: ArraySize,
442{
443 type Err = Error;
444
445 fn from_str(hex: &str) -> Result<Self> {
446 if hex.len() != C::FieldBytesSize::USIZE * 4 {
447 return Err(Error::new());
448 }
449
450 let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
451
452 let r = r_hex
453 .parse::<NonZeroScalar<C>>()
454 .map_err(|_| Error::new())?;
455
456 let s = s_hex
457 .parse::<NonZeroScalar<C>>()
458 .map_err(|_| Error::new())?;
459
460 Self::from_scalars(r, s)
461 }
462}
463
464#[cfg(all(feature = "digest", feature = "hazmat"))]
470impl<C> AssociatedOid for Signature<C>
471where
472 C: hazmat::DigestAlgorithm,
473 C::Digest: AssociatedOid,
474{
475 const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) {
476 Some(oid) => oid,
477 None => panic!("no RFC5758 ECDSA OID defined for DigestAlgorithm::Digest"),
478 };
479}
480
481#[cfg(feature = "pkcs8")]
484impl<C> AssociatedAlgorithmIdentifier for Signature<C>
485where
486 C: EcdsaCurve,
487 Self: AssociatedOid,
488{
489 type Params = AnyRef<'static>;
490
491 const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
492 oid: Self::OID,
493 parameters: None,
494 };
495}
496
497#[cfg(feature = "serde")]
498impl<C> Serialize for Signature<C>
499where
500 C: EcdsaCurve,
501 SignatureSize<C>: ArraySize,
502{
503 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
504 where
505 S: ser::Serializer,
506 {
507 serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
508 }
509}
510
511#[cfg(feature = "serde")]
512impl<'de, C> Deserialize<'de> for Signature<C>
513where
514 C: EcdsaCurve,
515 SignatureSize<C>: ArraySize,
516{
517 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
518 where
519 D: de::Deserializer<'de>,
520 {
521 let mut bytes = SignatureBytes::<C>::default();
522 serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
523 Self::try_from(bytes.as_slice()).map_err(de::Error::custom)
524 }
525}
526
527impl<C: EcdsaCurve> Zeroize for Signature<C> {
528 fn zeroize(&mut self) {
529 self.r = ScalarValue::ONE;
530 self.s = ScalarValue::ONE;
531 }
532}
533
534#[cfg(feature = "digest")]
547#[derive(Clone, Eq, PartialEq)]
548pub struct SignatureWithOid<C: EcdsaCurve> {
549 signature: Signature<C>,
551
552 oid: ObjectIdentifier,
558}
559
560#[cfg(feature = "digest")]
561impl<C> SignatureWithOid<C>
562where
563 C: EcdsaCurve,
564{
565 pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
572 for (arc1, arc2) in ObjectIdentifier::new_unwrap("1.2.840.10045.4.3")
574 .arcs()
575 .zip(oid.arcs())
576 {
577 if arc1 != arc2 {
578 return Err(Error::new());
579 }
580 }
581
582 Ok(Self { signature, oid })
583 }
584
585 pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
592 where
593 D: AssociatedOid + Digest,
594 {
595 let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
596 Ok(Self { signature, oid })
597 }
598
599 pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
601 where
602 D: AssociatedOid + Digest,
603 SignatureSize<C>: ArraySize,
604 {
605 Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
606 }
607
608 pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
610 where
611 D: AssociatedOid + Digest,
612 SignatureSize<C>: ArraySize,
613 {
614 Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
615 }
616
617 #[cfg(feature = "der")]
619 pub fn from_der_with_digest<D>(der_bytes: &[u8]) -> Result<Self>
620 where
621 D: AssociatedOid + Digest,
622 der::MaxSize<C>: ArraySize,
623 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
624 {
625 Self::new_with_digest::<D>(Signature::<C>::from_der(der_bytes)?)
626 }
627
628 #[cfg(feature = "der")]
630 pub fn from_der_with_oid(der_bytes: &[u8], oid: ObjectIdentifier) -> Result<Self>
631 where
632 der::MaxSize<C>: ArraySize,
633 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
634 {
635 Self::new(Signature::<C>::from_der(der_bytes)?, oid)
636 }
637
638 pub fn signature(&self) -> &Signature<C> {
640 &self.signature
641 }
642
643 pub fn oid(&self) -> ObjectIdentifier {
645 self.oid
646 }
647
648 pub fn to_bytes(&self) -> SignatureBytes<C>
650 where
651 SignatureSize<C>: ArraySize,
652 {
653 self.signature.to_bytes()
654 }
655
656 #[cfg(feature = "der")]
662 pub fn to_der(&self) -> der::Signature<C>
663 where
664 der::MaxSize<C>: ArraySize,
665 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
666 {
667 self.signature.clone().into()
668 }
669}
670
671#[cfg(feature = "digest")]
672impl<C> Copy for SignatureWithOid<C>
673where
674 C: EcdsaCurve,
675 SignatureSize<C>: ArraySize,
676 <SignatureSize<C> as ArraySize>::ArrayType<u8>: Copy,
677{
678}
679
680#[cfg(feature = "digest")]
681impl<C> core::hash::Hash for SignatureWithOid<C>
682where
683 C: EcdsaCurve,
684 SignatureSize<C>: ArraySize,
685{
686 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
687 self.signature.hash(state);
688 self.oid.hash(state);
689 }
690}
691
692#[cfg(feature = "digest")]
693impl<C> From<SignatureWithOid<C>> for Signature<C>
694where
695 C: EcdsaCurve,
696{
697 fn from(sig: SignatureWithOid<C>) -> Signature<C> {
698 sig.signature
699 }
700}
701
702#[cfg(feature = "digest")]
703impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
704where
705 C: EcdsaCurve,
706 SignatureSize<C>: ArraySize,
707{
708 fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
709 signature.to_bytes()
710 }
711}
712
713#[cfg(all(feature = "der", feature = "digest"))]
714impl<C> From<SignatureWithOid<C>> for der::Signature<C>
715where
716 C: EcdsaCurve,
717 der::MaxSize<C>: ArraySize,
718 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
719{
720 fn from(sig: SignatureWithOid<C>) -> der::Signature<C> {
721 sig.to_der()
722 }
723}
724
725#[cfg(all(feature = "der", feature = "digest"))]
726impl<C> From<&SignatureWithOid<C>> for der::Signature<C>
727where
728 C: EcdsaCurve,
729 der::MaxSize<C>: ArraySize,
730 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
731{
732 fn from(sig: &SignatureWithOid<C>) -> der::Signature<C> {
733 sig.to_der()
734 }
735}
736
737#[cfg(all(feature = "digest", feature = "hazmat"))]
743impl<C> SignatureEncoding for SignatureWithOid<C>
744where
745 C: hazmat::DigestAlgorithm,
746 C::Digest: AssociatedOid,
747 SignatureSize<C>: ArraySize,
748{
749 type Repr = SignatureBytes<C>;
750}
751
752#[cfg(all(feature = "digest", feature = "hazmat"))]
758impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
759where
760 C: hazmat::DigestAlgorithm,
761 C::Digest: AssociatedOid,
762 SignatureSize<C>: ArraySize,
763{
764 type Error = Error;
765
766 fn try_from(slice: &[u8]) -> Result<Self> {
767 Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
768 }
769}
770
771#[cfg(all(feature = "alloc", feature = "pkcs8"))]
772impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
773where
774 C: EcdsaCurve,
775{
776 fn algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
777 Ok(AlgorithmIdentifierOwned {
778 oid: self.oid,
779 parameters: None,
780 })
781 }
782}
783
784#[cfg(feature = "digest")]
786const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
787 match digest_oid {
788 SHA224_OID => Some(ECDSA_SHA224_OID),
789 SHA256_OID => Some(ECDSA_SHA256_OID),
790 SHA384_OID => Some(ECDSA_SHA384_OID),
791 SHA512_OID => Some(ECDSA_SHA512_OID),
792 _ => None,
793 }
794}