1#![no_std]
2#![cfg_attr(docsrs, feature(doc_auto_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)]
25
26#[cfg(feature = "alloc")]
58extern crate alloc;
59
60mod normalized;
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 = "signing")]
70mod signing;
71#[cfg(feature = "verifying")]
72mod verifying;
73
74pub use crate::{normalized::NormalizedSignature, recovery::RecoveryId};
75
76pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve};
78
79pub use signature::{self, Error, Result, SignatureEncoding};
81
82#[cfg(feature = "signing")]
83pub use crate::signing::SigningKey;
84#[cfg(feature = "verifying")]
85pub use crate::verifying::VerifyingKey;
86
87use core::{fmt, ops::Add};
88use elliptic_curve::{
89 generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
90 FieldBytes, FieldBytesSize, ScalarPrimitive,
91};
92
93#[cfg(feature = "alloc")]
94use alloc::vec::Vec;
95
96#[cfg(feature = "arithmetic")]
97use {
98 core::str,
99 elliptic_curve::{scalar::IsHigh, CurveArithmetic, NonZeroScalar},
100};
101
102#[cfg(feature = "digest")]
103use digest::{
104 const_oid::{AssociatedOid, ObjectIdentifier},
105 Digest,
106};
107
108#[cfg(feature = "pkcs8")]
109use elliptic_curve::pkcs8::spki::{
110 der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
111};
112
113#[cfg(feature = "serde")]
114use serdect::serde::{de, ser, Deserialize, Serialize};
115
116#[cfg(all(feature = "alloc", feature = "pkcs8"))]
117use elliptic_curve::pkcs8::spki::{
118 self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier,
119};
120
121#[cfg(feature = "digest")]
129pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1");
130
131#[cfg(feature = "digest")]
138pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
139
140#[cfg(feature = "digest")]
147pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3");
148
149#[cfg(feature = "digest")]
156pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
157
158#[cfg(feature = "digest")]
159const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
160#[cfg(feature = "digest")]
161const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
162#[cfg(feature = "digest")]
163const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
164#[cfg(feature = "digest")]
165const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
166
167pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
169
170pub type SignatureBytes<C> = GenericArray<u8, SignatureSize<C>>;
172
173#[derive(Clone, Eq, PartialEq)]
198pub struct Signature<C: PrimeCurve> {
199 r: ScalarPrimitive<C>,
200 s: ScalarPrimitive<C>,
201}
202
203impl<C> Signature<C>
204where
205 C: PrimeCurve,
206 SignatureSize<C>: ArrayLength<u8>,
207{
208 pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
217 let (r_bytes, s_bytes) = bytes.split_at(C::FieldBytesSize::USIZE);
218 let r = FieldBytes::<C>::clone_from_slice(r_bytes);
219 let s = FieldBytes::<C>::clone_from_slice(s_bytes);
220 Self::from_scalars(r, s)
221 }
222
223 pub fn from_slice(slice: &[u8]) -> Result<Self> {
225 if slice.len() == SignatureSize::<C>::USIZE {
226 Self::from_bytes(SignatureBytes::<C>::from_slice(slice))
227 } else {
228 Err(Error::new())
229 }
230 }
231
232 #[cfg(feature = "der")]
234 pub fn from_der(bytes: &[u8]) -> Result<Self>
235 where
236 der::MaxSize<C>: ArrayLength<u8>,
237 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
238 {
239 der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
240 }
241
242 pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
251 let r = ScalarPrimitive::from_slice(&r.into()).map_err(|_| Error::new())?;
252 let s = ScalarPrimitive::from_slice(&s.into()).map_err(|_| Error::new())?;
253
254 if r.is_zero().into() || s.is_zero().into() {
255 return Err(Error::new());
256 }
257
258 Ok(Self { r, s })
259 }
260
261 pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
263 (self.r.to_bytes(), self.s.to_bytes())
264 }
265
266 pub fn to_bytes(&self) -> SignatureBytes<C> {
268 let mut bytes = SignatureBytes::<C>::default();
269 let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
270 r_bytes.copy_from_slice(&self.r.to_bytes());
271 s_bytes.copy_from_slice(&self.s.to_bytes());
272 bytes
273 }
274
275 #[cfg(feature = "der")]
277 pub fn to_der(&self) -> der::Signature<C>
278 where
279 der::MaxSize<C>: ArrayLength<u8>,
280 <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
281 {
282 let (r, s) = self.split_bytes();
283 der::Signature::from_components(&r, &s).expect("DER encoding error")
284 }
285
286 #[cfg(feature = "alloc")]
288 pub fn to_vec(&self) -> Vec<u8> {
289 self.to_bytes().to_vec()
290 }
291}
292
293#[cfg(feature = "arithmetic")]
294impl<C> Signature<C>
295where
296 C: PrimeCurve + CurveArithmetic,
297 SignatureSize<C>: ArrayLength<u8>,
298{
299 pub fn r(&self) -> NonZeroScalar<C> {
301 NonZeroScalar::new(self.r.into()).unwrap()
302 }
303
304 pub fn s(&self) -> NonZeroScalar<C> {
306 NonZeroScalar::new(self.s.into()).unwrap()
307 }
308
309 pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
311 (self.r(), self.s())
312 }
313
314 pub fn normalize_s(&self) -> Option<Self> {
319 let s = self.s();
320
321 if s.is_high().into() {
322 let mut result = self.clone();
323 result.s = ScalarPrimitive::from(-s);
324 Some(result)
325 } else {
326 None
327 }
328 }
329}
330
331impl<C> Copy for Signature<C>
332where
333 C: PrimeCurve,
334 SignatureSize<C>: ArrayLength<u8>,
335 <SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
336{
337}
338
339impl<C> From<Signature<C>> for SignatureBytes<C>
340where
341 C: PrimeCurve,
342 SignatureSize<C>: ArrayLength<u8>,
343{
344 fn from(signature: Signature<C>) -> SignatureBytes<C> {
345 signature.to_bytes()
346 }
347}
348
349impl<C> SignatureEncoding for Signature<C>
350where
351 C: PrimeCurve,
352 SignatureSize<C>: ArrayLength<u8>,
353{
354 type Repr = SignatureBytes<C>;
355}
356
357impl<C> TryFrom<&[u8]> for Signature<C>
358where
359 C: PrimeCurve,
360 SignatureSize<C>: ArrayLength<u8>,
361{
362 type Error = Error;
363
364 fn try_from(slice: &[u8]) -> Result<Self> {
365 Self::from_slice(slice)
366 }
367}
368
369impl<C> fmt::Debug for Signature<C>
370where
371 C: PrimeCurve,
372 SignatureSize<C>: ArrayLength<u8>,
373{
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
376
377 for byte in self.to_bytes() {
378 write!(f, "{:02X}", byte)?;
379 }
380
381 write!(f, ")")
382 }
383}
384
385impl<C> fmt::Display for Signature<C>
386where
387 C: PrimeCurve,
388 SignatureSize<C>: ArrayLength<u8>,
389{
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 write!(f, "{:X}", self)
392 }
393}
394
395impl<C> fmt::LowerHex for Signature<C>
396where
397 C: PrimeCurve,
398 SignatureSize<C>: ArrayLength<u8>,
399{
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 for byte in self.to_bytes() {
402 write!(f, "{:02x}", byte)?;
403 }
404 Ok(())
405 }
406}
407
408impl<C> fmt::UpperHex for Signature<C>
409where
410 C: PrimeCurve,
411 SignatureSize<C>: ArrayLength<u8>,
412{
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 for byte in self.to_bytes() {
415 write!(f, "{:02X}", byte)?;
416 }
417 Ok(())
418 }
419}
420
421#[cfg(feature = "arithmetic")]
422impl<C> str::FromStr for Signature<C>
423where
424 C: PrimeCurve + CurveArithmetic,
425 SignatureSize<C>: ArrayLength<u8>,
426{
427 type Err = Error;
428
429 fn from_str(hex: &str) -> Result<Self> {
430 if hex.as_bytes().len() != C::FieldBytesSize::USIZE * 4 {
431 return Err(Error::new());
432 }
433
434 if !hex
436 .as_bytes()
437 .iter()
438 .all(|&byte| matches!(byte, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
439 {
440 return Err(Error::new());
441 }
442
443 let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
444
445 let r = r_hex
446 .parse::<NonZeroScalar<C>>()
447 .map_err(|_| Error::new())?;
448
449 let s = s_hex
450 .parse::<NonZeroScalar<C>>()
451 .map_err(|_| Error::new())?;
452
453 Self::from_scalars(r, s)
454 }
455}
456
457#[cfg(all(feature = "digest", feature = "hazmat"))]
463impl<C> AssociatedOid for Signature<C>
464where
465 C: hazmat::DigestPrimitive,
466 C::Digest: AssociatedOid,
467{
468 const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) {
469 Some(oid) => oid,
470 None => panic!("no RFC5758 ECDSA OID defined for DigestPrimitive::Digest"),
471 };
472}
473
474#[cfg(feature = "pkcs8")]
477impl<C> AssociatedAlgorithmIdentifier for Signature<C>
478where
479 C: PrimeCurve,
480 Self: AssociatedOid,
481{
482 type Params = AnyRef<'static>;
483
484 const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
485 oid: Self::OID,
486 parameters: None,
487 };
488}
489
490#[cfg(feature = "serde")]
491impl<C> Serialize for Signature<C>
492where
493 C: PrimeCurve,
494 SignatureSize<C>: ArrayLength<u8>,
495{
496 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
497 where
498 S: ser::Serializer,
499 {
500 serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
501 }
502}
503
504#[cfg(feature = "serde")]
505impl<'de, C> Deserialize<'de> for Signature<C>
506where
507 C: PrimeCurve,
508 SignatureSize<C>: ArrayLength<u8>,
509{
510 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
511 where
512 D: de::Deserializer<'de>,
513 {
514 let mut bytes = SignatureBytes::<C>::default();
515 serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
516 Self::try_from(bytes.as_slice()).map_err(de::Error::custom)
517 }
518}
519
520#[cfg(feature = "digest")]
533#[derive(Clone, Eq, PartialEq)]
534pub struct SignatureWithOid<C: PrimeCurve> {
535 signature: Signature<C>,
537
538 oid: ObjectIdentifier,
544}
545
546#[cfg(feature = "digest")]
547impl<C> SignatureWithOid<C>
548where
549 C: PrimeCurve,
550{
551 pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
558 for (arc1, arc2) in ObjectIdentifier::new_unwrap("1.2.840.10045.4.3")
560 .arcs()
561 .zip(oid.arcs())
562 {
563 if arc1 != arc2 {
564 return Err(Error::new());
565 }
566 }
567
568 Ok(Self { signature, oid })
569 }
570
571 pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
578 where
579 D: AssociatedOid + Digest,
580 {
581 let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
582 Ok(Self { signature, oid })
583 }
584
585 pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
587 where
588 D: AssociatedOid + Digest,
589 SignatureSize<C>: ArrayLength<u8>,
590 {
591 Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
592 }
593
594 pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
596 where
597 D: AssociatedOid + Digest,
598 SignatureSize<C>: ArrayLength<u8>,
599 {
600 Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
601 }
602
603 pub fn signature(&self) -> &Signature<C> {
605 &self.signature
606 }
607
608 pub fn oid(&self) -> ObjectIdentifier {
610 self.oid
611 }
612
613 pub fn to_bytes(&self) -> SignatureBytes<C>
615 where
616 SignatureSize<C>: ArrayLength<u8>,
617 {
618 self.signature.to_bytes()
619 }
620}
621
622#[cfg(feature = "digest")]
623impl<C> Copy for SignatureWithOid<C>
624where
625 C: PrimeCurve,
626 SignatureSize<C>: ArrayLength<u8>,
627 <SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
628{
629}
630
631#[cfg(feature = "digest")]
632impl<C> From<SignatureWithOid<C>> for Signature<C>
633where
634 C: PrimeCurve,
635{
636 fn from(sig: SignatureWithOid<C>) -> Signature<C> {
637 sig.signature
638 }
639}
640
641#[cfg(feature = "digest")]
642impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
643where
644 C: PrimeCurve,
645 SignatureSize<C>: ArrayLength<u8>,
646{
647 fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
648 signature.to_bytes()
649 }
650}
651
652#[cfg(all(feature = "digest", feature = "hazmat"))]
658impl<C> SignatureEncoding for SignatureWithOid<C>
659where
660 C: hazmat::DigestPrimitive,
661 C::Digest: AssociatedOid,
662 SignatureSize<C>: ArrayLength<u8>,
663{
664 type Repr = SignatureBytes<C>;
665}
666
667#[cfg(all(feature = "digest", feature = "hazmat"))]
673impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
674where
675 C: hazmat::DigestPrimitive,
676 C::Digest: AssociatedOid,
677 SignatureSize<C>: ArrayLength<u8>,
678{
679 type Error = Error;
680
681 fn try_from(slice: &[u8]) -> Result<Self> {
682 Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
683 }
684}
685
686#[cfg(all(feature = "alloc", feature = "pkcs8"))]
687impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
688where
689 C: PrimeCurve,
690{
691 fn algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
692 Ok(AlgorithmIdentifierOwned {
693 oid: self.oid,
694 parameters: None,
695 })
696 }
697}
698
699#[cfg(feature = "digest")]
701const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
702 match digest_oid {
703 SHA224_OID => Some(ECDSA_SHA224_OID),
704 SHA256_OID => Some(ECDSA_SHA256_OID),
705 SHA384_OID => Some(ECDSA_SHA384_OID),
706 SHA512_OID => Some(ECDSA_SHA512_OID),
707 _ => None,
708 }
709}