/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ecdsa-0.17.0/src/lib.rs
Line | Count | Source |
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 | | //! ## `serde` support |
10 | | //! |
11 | | //! When the `serde` feature of this crate is enabled, `Serialize` and `Deserialize` impls are |
12 | | //! provided for the [`Signature`] and [`VerifyingKey`] types. |
13 | | //! |
14 | | //! Please see type-specific documentation for more information. |
15 | | //! |
16 | | //! ## Interop |
17 | | //! |
18 | | //! Any crates which provide an implementation of ECDSA for a particular elliptic curve can leverage |
19 | | //! the types from this crate, along with the [`k256`], [`p256`], and/or [`p384`] crates to expose |
20 | | //! ECDSA functionality in a generic, interoperable way by leveraging the [`Signature`] type with in |
21 | | //! conjunction with the [`signature::Signer`] and [`signature::Verifier`] traits. |
22 | | //! |
23 | | //! For example, the [`ring-compat`] crate implements the [`signature::Signer`] and |
24 | | //! [`signature::Verifier`] traits in conjunction with the [`p256::ecdsa::Signature`] and |
25 | | //! [`p384::ecdsa::Signature`] types to wrap the ECDSA implementations from [*ring*] in a generic, |
26 | | //! interoperable API. |
27 | | //! |
28 | | //! [`k256`]: https://docs.rs/k256 |
29 | | //! [`p256`]: https://docs.rs/p256 |
30 | | //! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html |
31 | | //! [`p384`]: https://docs.rs/p384 |
32 | | //! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html |
33 | | //! [`ring-compat`]: https://docs.rs/ring-compat |
34 | | //! [*ring*]: https://docs.rs/ring |
35 | | |
36 | | #[cfg(feature = "alloc")] |
37 | | extern crate alloc; |
38 | | |
39 | | mod recovery; |
40 | | |
41 | | #[cfg(feature = "der")] |
42 | | pub mod der; |
43 | | #[cfg(feature = "dev")] |
44 | | pub mod dev; |
45 | | #[cfg(feature = "algorithm")] |
46 | | pub mod hazmat; |
47 | | #[cfg(feature = "algorithm")] |
48 | | mod signing; |
49 | | #[cfg(feature = "algorithm")] |
50 | | mod verifying; |
51 | | |
52 | | pub use crate::recovery::RecoveryId; |
53 | | |
54 | | // Re-export the `elliptic-curve` crate (and select types) |
55 | | pub use elliptic_curve::{self, PrimeCurve, sec1::Sec1Point}; |
56 | | |
57 | | // Re-export the `signature` crate (and select types) |
58 | | pub use signature::{self, Error, Result, SignatureEncoding}; |
59 | | use zeroize::Zeroize; |
60 | | |
61 | | #[cfg(feature = "algorithm")] |
62 | | pub use crate::signing::SigningKey; |
63 | | #[cfg(feature = "algorithm")] |
64 | | pub use crate::verifying::VerifyingKey; |
65 | | |
66 | | use core::{fmt, ops::Add}; |
67 | | use elliptic_curve::{ |
68 | | Curve, FieldBytes, FieldBytesSize, ScalarValue, |
69 | | array::{Array, ArraySize, typenum::Unsigned}, |
70 | | }; |
71 | | |
72 | | #[cfg(feature = "alloc")] |
73 | | use alloc::vec::Vec; |
74 | | #[cfg(feature = "digest")] |
75 | | use digest::{ |
76 | | Digest, FixedOutput, |
77 | | common::BlockSizeUser, |
78 | | const_oid::{AssociatedOid, ObjectIdentifier}, |
79 | | }; |
80 | | #[cfg(all(feature = "alloc", feature = "pkcs8"))] |
81 | | use elliptic_curve::pkcs8::spki::{ |
82 | | self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier, |
83 | | }; |
84 | | #[cfg(feature = "pkcs8")] |
85 | | use elliptic_curve::pkcs8::spki::{ |
86 | | AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, der::AnyRef, |
87 | | }; |
88 | | #[cfg(feature = "serde")] |
89 | | use serdect::serde::{Deserialize, Serialize, de, ser}; |
90 | | #[cfg(feature = "algorithm")] |
91 | | use { |
92 | | core::str, |
93 | | elliptic_curve::{ |
94 | | CurveArithmetic, NonZeroScalar, scalar::IsHigh, subtle::ConditionallySelectable, |
95 | | }, |
96 | | }; |
97 | | |
98 | | /// OID for ECDSA with SHA-224 digests. |
99 | | /// |
100 | | /// ```text |
101 | | /// ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) |
102 | | /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 1 } |
103 | | /// ``` |
104 | | // TODO(tarcieri): use `ObjectIdentifier::push_arc` when const unwrap is stable |
105 | | #[cfg(feature = "digest")] |
106 | | pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1"); |
107 | | |
108 | | /// OID for ECDSA with SHA-256 digests. |
109 | | /// |
110 | | /// ```text |
111 | | /// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) |
112 | | /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } |
113 | | /// ``` |
114 | | #[cfg(feature = "digest")] |
115 | | pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2"); |
116 | | |
117 | | /// OID for ECDSA with SHA-384 digests. |
118 | | /// |
119 | | /// ```text |
120 | | /// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) |
121 | | /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } |
122 | | /// ``` |
123 | | #[cfg(feature = "digest")] |
124 | | pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3"); |
125 | | |
126 | | /// OID for ECDSA with SHA-512 digests. |
127 | | /// |
128 | | /// ```text |
129 | | /// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) |
130 | | /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } |
131 | | /// ``` |
132 | | #[cfg(feature = "digest")] |
133 | | pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4"); |
134 | | |
135 | | #[cfg(feature = "digest")] |
136 | | const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4"); |
137 | | #[cfg(feature = "digest")] |
138 | | const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"); |
139 | | #[cfg(feature = "digest")] |
140 | | const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2"); |
141 | | #[cfg(feature = "digest")] |
142 | | const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3"); |
143 | | |
144 | | /// Marker trait for elliptic curves intended for use with ECDSA. |
145 | | pub trait EcdsaCurve: |
146 | | Curve<FieldBytesSize: Add<Output: ArraySize<ArrayType<u8>: Copy>>> + PrimeCurve |
147 | | { |
148 | | /// Does this curve use low-S normalized signatures? |
149 | | /// |
150 | | /// This is typically `false`. See [`Signature::normalize_s`] for more information. |
151 | | const NORMALIZE_S: bool; |
152 | | } |
153 | | |
154 | | /// Size of a fixed sized signature for the given elliptic curve. |
155 | | pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output; |
156 | | |
157 | | /// Fixed-size byte array containing an ECDSA signature |
158 | | pub type SignatureBytes<C> = Array<u8, SignatureSize<C>>; |
159 | | |
160 | | /// ECDSA signature (fixed-size, a.k.a. [IEEE P1363]). Generic over elliptic curve types. |
161 | | /// |
162 | | /// Serialized as fixed-sized big endian scalar values with no added framing: |
163 | | /// |
164 | | /// - `r`: field element size for the given curve, big-endian |
165 | | /// - `s`: field element size for the given curve, big-endian |
166 | | /// |
167 | | /// Both `r` and `s` MUST be non-zero. |
168 | | /// |
169 | | /// For example, in a curve with a 256-bit modulus like NIST P-256 or secp256k1, `r` and `s` are |
170 | | /// both 32-bytes and serialized as big endian, resulting in a signature with a total of 64-bytes. |
171 | | /// |
172 | | /// ASN.1 DER-encoded signatures also supported via the [`Signature::from_der`] and |
173 | | /// [`Signature::to_der`] methods. |
174 | | /// |
175 | | /// # `serde` support |
176 | | /// |
177 | | /// When the `serde` feature of this crate is enabled, it provides support for serializing and |
178 | | /// deserializing ECDSA signatures using the `Serialize` and `Deserialize` traits. |
179 | | /// |
180 | | /// The serialization uses a hexadecimal encoding when used with "human readable" text formats, and |
181 | | /// a binary encoding otherwise. |
182 | | /// |
183 | | /// [IEEE P1363]: https://en.wikipedia.org/wiki/IEEE_P1363 |
184 | | #[derive(Clone, Copy, Eq, PartialEq)] |
185 | | pub struct Signature<C: EcdsaCurve> { |
186 | | r: ScalarValue<C>, |
187 | | s: ScalarValue<C>, |
188 | | } |
189 | | |
190 | | impl<C> Signature<C> |
191 | | where |
192 | | C: EcdsaCurve, |
193 | | { |
194 | | /// Parse a signature from fixed-width bytes, i.e. 2 * the size of [`FieldBytes`] for a |
195 | | /// particular curve. |
196 | | /// |
197 | | /// # Errors |
198 | | /// If the `r` and/or `s` component of the signature is out-of-range when interpreted as a big |
199 | | /// endian integer. |
200 | 0 | pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> { |
201 | 0 | let chunks = FieldBytes::<C>::slice_as_chunks(bytes).0; |
202 | 0 | let r = chunks[0]; |
203 | 0 | let s = chunks[1]; |
204 | 0 | Self::from_scalars(r, s) |
205 | 0 | } |
206 | | |
207 | | /// Parse a signature from a byte slice. |
208 | | /// |
209 | | /// # Errors |
210 | | /// Returns [`Error`] in the event the signature is not the expected size, i.e. 2 * the size of |
211 | | /// [`FieldBytes`] for a particular curve. |
212 | 0 | pub fn from_slice(slice: &[u8]) -> Result<Self> { |
213 | 0 | <&SignatureBytes<C>>::try_from(slice) |
214 | 0 | .map_err(|_| Error::new()) |
215 | 0 | .and_then(Self::from_bytes) |
216 | 0 | } |
217 | | |
218 | | /// Parse a signature from ASN.1 DER. |
219 | | /// |
220 | | /// # Errors |
221 | | /// Returns [`Error`] if `input` failed to parse as an ASN.1 DER-encoded ECDSA signature. |
222 | | #[cfg(feature = "der")] |
223 | 0 | pub fn from_der(bytes: &[u8]) -> Result<Self> |
224 | 0 | where |
225 | 0 | der::MaxSize<C>: ArraySize, |
226 | 0 | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
227 | | { |
228 | 0 | der::Signature::<C>::try_from(bytes).and_then(Self::try_from) |
229 | 0 | } |
230 | | |
231 | | /// Create a [`Signature`] from the serialized `r` and `s` scalar values |
232 | | /// which comprise the signature. |
233 | | /// |
234 | | /// # Errors |
235 | | /// If the `r` and/or `s` component of the signature is out-of-range when interpreted as a big |
236 | | /// endian integer. |
237 | 2.16k | pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> { |
238 | 2.16k | let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?; Unexecuted instantiation: <ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar>::{closure#0}Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _>::{closure#0} |
239 | 2.16k | let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?; Unexecuted instantiation: <ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar>::{closure#1}Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _>::{closure#1} |
240 | | |
241 | 2.16k | if r.is_zero().into() || s.is_zero().into() { |
242 | 0 | return Err(Error::new()); |
243 | 2.16k | } |
244 | | |
245 | 2.16k | Ok(Self { r, s }) |
246 | 2.16k | } <ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar> Line | Count | Source | 237 | 2.16k | pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> { | 238 | 2.16k | let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?; | 239 | 2.16k | let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?; | 240 | | | 241 | 2.16k | if r.is_zero().into() || s.is_zero().into() { | 242 | 0 | return Err(Error::new()); | 243 | 2.16k | } | 244 | | | 245 | 2.16k | Ok(Self { r, s }) | 246 | 2.16k | } |
Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _> |
247 | | |
248 | | /// Split the signature into its `r` and `s` components, represented as bytes. |
249 | 2.16k | pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) { |
250 | 2.16k | (self.r.to_bytes(), self.s.to_bytes()) |
251 | 2.16k | } <ecdsa::Signature<p256::NistP256>>::split_bytes Line | Count | Source | 249 | 2.16k | pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) { | 250 | 2.16k | (self.r.to_bytes(), self.s.to_bytes()) | 251 | 2.16k | } |
Unexecuted instantiation: <ecdsa::Signature<_>>::split_bytes |
252 | | |
253 | | /// Serialize this signature as bytes. |
254 | 0 | pub fn to_bytes(&self) -> SignatureBytes<C> { |
255 | 0 | let mut bytes = SignatureBytes::<C>::default(); |
256 | 0 | let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE); |
257 | 0 | r_bytes.copy_from_slice(&self.r.to_bytes()); |
258 | 0 | s_bytes.copy_from_slice(&self.s.to_bytes()); |
259 | 0 | bytes |
260 | 0 | } |
261 | | |
262 | | /// Serialize this signature as ASN.1 DER. |
263 | | #[cfg(feature = "der")] |
264 | | #[allow(clippy::missing_panics_doc, reason = "should not panic in practice")] |
265 | 2.16k | pub fn to_der(&self) -> der::Signature<C> |
266 | 2.16k | where |
267 | 2.16k | der::MaxSize<C>: ArraySize, |
268 | 2.16k | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
269 | | { |
270 | 2.16k | let (r, s) = self.split_bytes(); |
271 | 2.16k | der::Signature::from_components(&r, &s).expect("DER encoding error") |
272 | 2.16k | } <ecdsa::Signature<p256::NistP256>>::to_der Line | Count | Source | 265 | 2.16k | pub fn to_der(&self) -> der::Signature<C> | 266 | 2.16k | where | 267 | 2.16k | der::MaxSize<C>: ArraySize, | 268 | 2.16k | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, | 269 | | { | 270 | 2.16k | let (r, s) = self.split_bytes(); | 271 | 2.16k | der::Signature::from_components(&r, &s).expect("DER encoding error") | 272 | 2.16k | } |
Unexecuted instantiation: <ecdsa::Signature<_>>::to_der |
273 | | |
274 | | /// Convert this signature into a byte vector. |
275 | | #[cfg(feature = "alloc")] |
276 | 0 | pub fn to_vec(&self) -> Vec<u8> { |
277 | 0 | self.to_bytes().to_vec() |
278 | 0 | } |
279 | | } |
280 | | |
281 | | #[cfg(feature = "algorithm")] |
282 | | impl<C> Signature<C> |
283 | | where |
284 | | C: EcdsaCurve + CurveArithmetic, |
285 | | { |
286 | | /// Get the `r` component of this signature |
287 | 0 | pub fn r(&self) -> NonZeroScalar<C> { |
288 | 0 | NonZeroScalar::new(self.r.into()).unwrap() |
289 | 0 | } |
290 | | |
291 | | /// Get the `s` component of this signature |
292 | 0 | pub fn s(&self) -> NonZeroScalar<C> { |
293 | 0 | NonZeroScalar::new(self.s.into()).unwrap() |
294 | 0 | } |
295 | | |
296 | | /// Split the signature into its `r` and `s` scalars. |
297 | 0 | pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) { |
298 | 0 | (self.r(), self.s()) |
299 | 0 | } |
300 | | |
301 | | /// Normalize signature into "low S" form described in [BIP 0062: Dealing with Malleability][1]. |
302 | | /// |
303 | | /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki |
304 | | #[must_use] |
305 | 0 | pub fn normalize_s(&self) -> Self { |
306 | 0 | let mut result = *self; |
307 | 0 | let s_inv = ScalarValue::from(-self.s()); |
308 | 0 | result.s.conditional_assign(&s_inv, self.s.is_high()); |
309 | 0 | result |
310 | 0 | } |
311 | | } |
312 | | |
313 | | impl<C> From<Signature<C>> for SignatureBytes<C> |
314 | | where |
315 | | C: EcdsaCurve, |
316 | | { |
317 | 0 | fn from(signature: Signature<C>) -> SignatureBytes<C> { |
318 | 0 | signature.to_bytes() |
319 | 0 | } |
320 | | } |
321 | | |
322 | | impl<C> SignatureEncoding for Signature<C> |
323 | | where |
324 | | C: EcdsaCurve, |
325 | | { |
326 | | type Repr = SignatureBytes<C>; |
327 | | } |
328 | | |
329 | | impl<C> TryFrom<&[u8]> for Signature<C> |
330 | | where |
331 | | C: EcdsaCurve, |
332 | | { |
333 | | type Error = Error; |
334 | | |
335 | 0 | fn try_from(slice: &[u8]) -> Result<Self> { |
336 | 0 | Self::from_slice(slice) |
337 | 0 | } |
338 | | } |
339 | | |
340 | | impl<C> fmt::Debug for Signature<C> |
341 | | where |
342 | | C: EcdsaCurve, |
343 | | { |
344 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
345 | 0 | write!(f, "ecdsa::Signature<{:?}>(", C::default())?; |
346 | | |
347 | 0 | for byte in self.to_bytes() { |
348 | 0 | write!(f, "{byte:02X}")?; |
349 | | } |
350 | | |
351 | 0 | write!(f, ")") |
352 | 0 | } |
353 | | } |
354 | | |
355 | | impl<C> fmt::Display for Signature<C> |
356 | | where |
357 | | C: EcdsaCurve, |
358 | | { |
359 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
360 | 0 | write!(f, "{self:X}") |
361 | 0 | } |
362 | | } |
363 | | |
364 | | impl<C> core::hash::Hash for Signature<C> |
365 | | where |
366 | | C: EcdsaCurve, |
367 | | { |
368 | 0 | fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
369 | 0 | self.to_bytes().hash(state); |
370 | 0 | } |
371 | | } |
372 | | |
373 | | impl<C> fmt::LowerHex for Signature<C> |
374 | | where |
375 | | C: EcdsaCurve, |
376 | | { |
377 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
378 | 0 | for byte in self.to_bytes() { |
379 | 0 | write!(f, "{byte:02x}")?; |
380 | | } |
381 | 0 | Ok(()) |
382 | 0 | } |
383 | | } |
384 | | |
385 | | impl<C> fmt::UpperHex for Signature<C> |
386 | | where |
387 | | C: EcdsaCurve, |
388 | | { |
389 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
390 | 0 | for byte in self.to_bytes() { |
391 | 0 | write!(f, "{byte:02X}")?; |
392 | | } |
393 | 0 | Ok(()) |
394 | 0 | } |
395 | | } |
396 | | |
397 | | #[cfg(feature = "algorithm")] |
398 | | impl<C> str::FromStr for Signature<C> |
399 | | where |
400 | | C: EcdsaCurve + CurveArithmetic, |
401 | | { |
402 | | type Err = Error; |
403 | | |
404 | 0 | fn from_str(hex: &str) -> Result<Self> { |
405 | 0 | if hex.len() != C::FieldBytesSize::USIZE * 4 { |
406 | 0 | return Err(Error::new()); |
407 | 0 | } |
408 | | |
409 | 0 | let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2); |
410 | | |
411 | 0 | let r = r_hex |
412 | 0 | .parse::<NonZeroScalar<C>>() |
413 | 0 | .map_err(|_| Error::new())?; |
414 | | |
415 | 0 | let s = s_hex |
416 | 0 | .parse::<NonZeroScalar<C>>() |
417 | 0 | .map_err(|_| Error::new())?; |
418 | | |
419 | 0 | Self::from_scalars(r, s) |
420 | 0 | } |
421 | | } |
422 | | |
423 | | /// ECDSA [`ObjectIdentifier`] which identifies the digest used by default with the `Signer` and |
424 | | /// `Verifier` traits. |
425 | | /// |
426 | | /// To support non-default digest algorithms, use the [`SignatureWithOid`] type instead. |
427 | | #[cfg(feature = "digest")] |
428 | | impl<C> AssociatedOid for Signature<C> |
429 | | where |
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 | | /// ECDSA `AlgorithmIdentifier` which identifies the digest used by default |
440 | | /// with the `Signer` and `Verifier` traits. |
441 | | #[cfg(feature = "pkcs8")] |
442 | | impl<C> AssociatedAlgorithmIdentifier for Signature<C> |
443 | | where |
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")] |
456 | | impl<C> Serialize for Signature<C> |
457 | | where |
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")] |
469 | | impl<'de, C> Deserialize<'de> for Signature<C> |
470 | | where |
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 | | |
483 | | impl<C: EcdsaCurve> Zeroize for Signature<C> { |
484 | 0 | fn zeroize(&mut self) { |
485 | 0 | self.r = ScalarValue::ONE; |
486 | 0 | self.s = ScalarValue::ONE; |
487 | 0 | } |
488 | | } |
489 | | |
490 | | /// An extended [`Signature`] type which is parameterized by an `ObjectIdentifier` which identifies |
491 | | /// the ECDSA variant used by a particular signature. |
492 | | /// |
493 | | /// Valid `ObjectIdentifiers` are defined in [RFC5758 § 3.2]: |
494 | | /// |
495 | | /// - SHA-224: [`ECDSA_SHA224_OID`] (1.2.840.10045.4.3.1) |
496 | | /// - SHA-256: [`ECDSA_SHA256_OID`] (1.2.840.10045.4.3.2) |
497 | | /// - SHA-384: [`ECDSA_SHA384_OID`] (1.2.840.10045.4.3.3) |
498 | | /// - SHA-512: [`ECDSA_SHA512_OID`] (1.2.840.10045.4.3.4) |
499 | | /// |
500 | | /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 |
501 | | #[cfg(feature = "digest")] |
502 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
503 | | pub struct SignatureWithOid<C: EcdsaCurve> { |
504 | | /// Inner signature type. |
505 | | signature: Signature<C>, |
506 | | |
507 | | /// OID which identifies the ECDSA variant used. |
508 | | /// |
509 | | /// MUST be one of the ECDSA algorithm variants as defined in RFC5758. |
510 | | /// |
511 | | /// These OIDs begin with `1.2.840.10045.4`. |
512 | | oid: ObjectIdentifier, |
513 | | } |
514 | | |
515 | | #[cfg(feature = "digest")] |
516 | | impl<C> SignatureWithOid<C> |
517 | | where |
518 | | C: EcdsaCurve, |
519 | | { |
520 | | /// Create a new signature with an explicitly provided OID. |
521 | | /// |
522 | | /// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for ECDSA variants. |
523 | | /// |
524 | | /// [RFC5758]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 |
525 | | /// |
526 | | /// # Errors |
527 | | /// Returns [`Error`] if `oid` does not start with `1.2.840.10045.4`. |
528 | 0 | pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> { |
529 | 0 | if !oid.starts_with(ObjectIdentifier::new_unwrap("1.2.840.10045.4")) { |
530 | 0 | return Err(Error::new()); |
531 | 0 | } |
532 | | |
533 | 0 | Ok(Self { signature, oid }) |
534 | 0 | } |
535 | | |
536 | | /// Create a new signature, determining the OID from the given digest. |
537 | | /// |
538 | | /// Supports SHA-2 family digests as enumerated in [RFC5758 § 3.2], i.e. SHA-224, SHA-256, |
539 | | /// SHA-384, or SHA-512. |
540 | | /// |
541 | | /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 |
542 | | /// |
543 | | /// # Errors |
544 | | /// Returns [`Error`] if the [`AssociatedOid`] for `D` is not one from the SHA2 family. |
545 | 0 | pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self> |
546 | 0 | where |
547 | 0 | D: AssociatedOid + Digest, |
548 | | { |
549 | 0 | let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?; |
550 | 0 | Ok(Self { signature, oid }) |
551 | 0 | } |
552 | | |
553 | | /// Parse a signature from fixed-with bytes. |
554 | | /// |
555 | | /// # Errors |
556 | | /// Returns [`Error`] if [`Signature`] fails to parse, or if `D` is not a valid digest. |
557 | | /// See [`SignatureWithOid::new_with_digest`] documentation. |
558 | 0 | pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self> |
559 | 0 | where |
560 | 0 | D: AssociatedOid + Digest, |
561 | | { |
562 | 0 | Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?) |
563 | 0 | } |
564 | | |
565 | | /// Parse a signature from a byte slice. |
566 | | /// |
567 | | /// # Errors |
568 | | /// Returns [`Error`] if [`Signature`] fails to parse, or if `D` is not a valid digest. |
569 | | /// See [`SignatureWithOid::new_with_digest`] documentation. |
570 | 0 | pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self> |
571 | 0 | where |
572 | 0 | D: AssociatedOid + Digest, |
573 | | { |
574 | 0 | Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?) |
575 | 0 | } |
576 | | |
577 | | /// Parse a signature from ASN.1 DER and associate the given digest's OID with it. |
578 | | /// |
579 | | /// # Errors |
580 | | /// Returns [`Error`] if `input` failed to parse as an ASN.1 DER-encoded ECDSA signature, |
581 | | /// or if `D` is not a valid digest. |
582 | | #[cfg(feature = "der")] |
583 | 0 | pub fn from_der_with_digest<D>(der_bytes: &[u8]) -> Result<Self> |
584 | 0 | where |
585 | 0 | D: AssociatedOid + Digest, |
586 | 0 | der::MaxSize<C>: ArraySize, |
587 | 0 | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
588 | | { |
589 | 0 | Self::new_with_digest::<D>(Signature::<C>::from_der(der_bytes)?) |
590 | 0 | } |
591 | | |
592 | | /// Parse a signature from ASN.1 DER and associate the given OID with it. |
593 | | /// |
594 | | /// # Errors |
595 | | /// Returns [`Error`] if `input` failed to parse as an ASN.1 DER-encoded ECDSA signature, |
596 | | /// or if `D` is not a valid digest. |
597 | | #[cfg(feature = "der")] |
598 | 0 | pub fn from_der_with_oid(der_bytes: &[u8], oid: ObjectIdentifier) -> Result<Self> |
599 | 0 | where |
600 | 0 | der::MaxSize<C>: ArraySize, |
601 | 0 | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
602 | | { |
603 | 0 | Self::new(Signature::<C>::from_der(der_bytes)?, oid) |
604 | 0 | } |
605 | | |
606 | | /// Get the fixed-width ECDSA signature. |
607 | 0 | pub fn signature(&self) -> &Signature<C> { |
608 | 0 | &self.signature |
609 | 0 | } |
610 | | |
611 | | /// Get the ECDSA OID for this signature. |
612 | 0 | pub fn oid(&self) -> ObjectIdentifier { |
613 | 0 | self.oid |
614 | 0 | } |
615 | | |
616 | | /// Serialize this signature as fixed-width bytes. |
617 | 0 | pub fn to_bytes(&self) -> SignatureBytes<C> |
618 | 0 | where { |
619 | 0 | self.signature.to_bytes() |
620 | 0 | } |
621 | | |
622 | | /// Serialize this signature as ASN.1 DER. |
623 | | /// |
624 | | /// Note that this includes only the `r` and `s` signature components, and not the OID. |
625 | | /// |
626 | | /// See [`der::Signature`] documentation for more information. |
627 | | #[cfg(feature = "der")] |
628 | 0 | pub fn to_der(&self) -> der::Signature<C> |
629 | 0 | where |
630 | 0 | der::MaxSize<C>: ArraySize, |
631 | 0 | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
632 | | { |
633 | 0 | self.signature.into() |
634 | 0 | } |
635 | | } |
636 | | |
637 | | /// Bind a preferred [`Digest`] algorithm to an elliptic curve type. |
638 | | /// |
639 | | /// Generally there is a preferred variety of the SHA-2 family used with ECDSA |
640 | | /// for a particular elliptic curve. |
641 | | #[cfg(feature = "digest")] |
642 | | pub trait DigestAlgorithm: EcdsaCurve { |
643 | | /// Preferred digest to use when computing ECDSA signatures for this |
644 | | /// elliptic curve. This is typically a member of the SHA-2 family. |
645 | | type Digest: BlockSizeUser + Digest + FixedOutput; |
646 | | } |
647 | | |
648 | | #[cfg(feature = "digest")] |
649 | | impl<C> core::hash::Hash for SignatureWithOid<C> |
650 | | where |
651 | | C: EcdsaCurve, |
652 | | { |
653 | 0 | fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
654 | 0 | self.signature.hash(state); |
655 | 0 | self.oid.hash(state); |
656 | 0 | } |
657 | | } |
658 | | |
659 | | #[cfg(feature = "digest")] |
660 | | impl<C> From<SignatureWithOid<C>> for Signature<C> |
661 | | where |
662 | | C: EcdsaCurve, |
663 | | { |
664 | 0 | fn from(sig: SignatureWithOid<C>) -> Signature<C> { |
665 | 0 | sig.signature |
666 | 0 | } |
667 | | } |
668 | | |
669 | | #[cfg(feature = "digest")] |
670 | | impl<C> From<SignatureWithOid<C>> for SignatureBytes<C> |
671 | | where |
672 | | C: EcdsaCurve, |
673 | | { |
674 | 0 | fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> { |
675 | 0 | signature.to_bytes() |
676 | 0 | } |
677 | | } |
678 | | |
679 | | #[cfg(all(feature = "der", feature = "digest"))] |
680 | | impl<C> From<SignatureWithOid<C>> for der::Signature<C> |
681 | | where |
682 | | C: EcdsaCurve, |
683 | | der::MaxSize<C>: ArraySize, |
684 | | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
685 | | { |
686 | 0 | fn from(sig: SignatureWithOid<C>) -> der::Signature<C> { |
687 | 0 | sig.to_der() |
688 | 0 | } |
689 | | } |
690 | | |
691 | | #[cfg(all(feature = "der", feature = "digest"))] |
692 | | impl<C> From<&SignatureWithOid<C>> for der::Signature<C> |
693 | | where |
694 | | C: EcdsaCurve, |
695 | | der::MaxSize<C>: ArraySize, |
696 | | <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize, |
697 | | { |
698 | 0 | fn from(sig: &SignatureWithOid<C>) -> der::Signature<C> { |
699 | 0 | sig.to_der() |
700 | 0 | } |
701 | | } |
702 | | |
703 | | /// NOTE: this implementation assumes the default digest for the given elliptic |
704 | | /// curve as defined by [`DigestAlgorithm`]. |
705 | | /// |
706 | | /// When working with alternative digests, you will need to use e.g. |
707 | | /// [`SignatureWithOid::new_with_digest`]. |
708 | | #[cfg(feature = "digest")] |
709 | | impl<C> SignatureEncoding for SignatureWithOid<C> |
710 | | where |
711 | | C: DigestAlgorithm, |
712 | | C::Digest: AssociatedOid, |
713 | | { |
714 | | type Repr = SignatureBytes<C>; |
715 | | } |
716 | | |
717 | | /// NOTE: this implementation assumes the default digest for the given elliptic |
718 | | /// curve as defined by [`DigestAlgorithm`]. |
719 | | /// |
720 | | /// When working with alternative digests, you will need to use e.g. |
721 | | /// [`SignatureWithOid::new_with_digest`]. |
722 | | #[cfg(feature = "digest")] |
723 | | impl<C> TryFrom<&[u8]> for SignatureWithOid<C> |
724 | | where |
725 | | C: DigestAlgorithm, |
726 | | C::Digest: AssociatedOid, |
727 | | { |
728 | | type Error = Error; |
729 | | |
730 | 0 | fn try_from(slice: &[u8]) -> Result<Self> { |
731 | 0 | Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID) |
732 | 0 | } |
733 | | } |
734 | | |
735 | | #[cfg(all(feature = "alloc", feature = "pkcs8"))] |
736 | | impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C> |
737 | | where |
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 | | /// Get the ECDSA OID for a given digest OID. |
749 | | #[cfg(feature = "digest")] |
750 | 0 | const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> { |
751 | 0 | match digest_oid { |
752 | 0 | SHA224_OID => Some(ECDSA_SHA224_OID), |
753 | 0 | SHA256_OID => Some(ECDSA_SHA256_OID), |
754 | 0 | SHA384_OID => Some(ECDSA_SHA384_OID), |
755 | 0 | SHA512_OID => Some(ECDSA_SHA512_OID), |
756 | 0 | _ => None, |
757 | | } |
758 | 0 | } |