ecdsa_flow/
lib.rs

1//! Elliptic Curve Digital Signature Algorithm (ECDSA) as specified in
2//! [FIPS 186-4] (Digital Signature Standard)
3//!
4//! ## About
5//!
6//! This crate provides generic ECDSA support which can be used in the
7//! following ways:
8//!
9//! - Generic implementation of ECDSA usable with the following crates:
10//!   - [`k256`] (secp256k1)
11//!   - [`p256`] (NIST P-256)
12//! - ECDSA signature types alone which can be used to provide interoperability
13//!   between other crates that provide an ECDSA implementation:
14//!   - [`p384`] (NIST P-384)
15//!
16//! Any crates which provide an implementation of ECDSA for a particular
17//! elliptic curve can leverage the types from this crate, along with the
18//! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in
19//! a generic, interoperable way by leveraging the [`Signature`] type with in
20//! conjunction with the [`signature_flow::Signer`] and [`signature_flow::Verifier`]
21//! traits.
22//!
23//! For example, the [`ring-compat`] crate implements the [`signature_flow::Signer`]
24//! and [`signature_flow::Verifier`] traits in conjunction with the
25//! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to
26//! wrap the ECDSA implementations from [*ring*] in a generic, interoperable
27//! API.
28//!
29//! ## Minimum Supported Rust Version
30//!
31//! Rust **1.52** or higher.
32//!
33//! Minimum supported Rust version may be changed in the future, but it will be
34//! accompanied with a minor version bump.
35//!
36//! [FIPS 186-4]: https://csrc.nist.gov/publications/detail/fips/186/4/final
37//! [`k256`]: https://docs.rs/k256
38//! [`p256`]: https://docs.rs/p256
39//! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html
40//! [`p384`]: https://docs.rs/p384
41//! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html
42//! [`ring-compat`]: https://docs.rs/ring-compat
43//! [*ring*]: https://docs.rs/ring
44
45#![no_std]
46#![cfg_attr(docsrs, feature(doc_cfg))]
47#![forbid(unsafe_code, clippy::unwrap_used)]
48#![warn(missing_docs, rust_2018_idioms)]
49#![doc(
50    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
51    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
52    html_root_url = "https://docs.rs/ecdsa/0.13.0-pre"
53)]
54
55#[cfg(feature = "alloc")]
56extern crate alloc;
57
58#[cfg(feature = "der")]
59#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
60pub mod der;
61
62#[cfg(feature = "dev")]
63#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
64pub mod dev;
65
66#[cfg(feature = "hazmat")]
67#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
68pub mod hazmat;
69
70#[cfg(feature = "sign")]
71#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
72pub mod rfc6979;
73
74#[cfg(feature = "sign")]
75mod sign;
76
77#[cfg(feature = "verify")]
78mod verify;
79
80// Re-export the `elliptic-curve` crate (and select types)
81pub use elliptic_curve_flow::{self, sec1::EncodedPoint, PrimeCurve};
82
83// Re-export the `signature` crate (and select types)
84pub use signature_flow::{self, Error, Result};
85
86#[cfg(feature = "sign")]
87#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
88pub use sign::SigningKey;
89
90#[cfg(feature = "verify")]
91#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
92pub use verify::VerifyingKey;
93
94use core::{
95    convert::TryFrom,
96    fmt::{self, Debug},
97    ops::Add,
98};
99use elliptic_curve_flow::{
100    bigint::Encoding as _,
101    generic_array::{sequence::Concat, ArrayLength, GenericArray},
102    FieldBytes, FieldSize, ScalarCore,
103};
104
105#[cfg(feature = "arithmetic")]
106use elliptic_curve_flow::{group::ff::PrimeField, NonZeroScalar, ProjectiveArithmetic, Scalar};
107
108/// Size of a fixed sized signature for the given elliptic curve.
109pub type SignatureSize<C> = <FieldSize<C> as Add>::Output;
110
111/// Fixed-size byte array containing an ECDSA signature
112pub type SignatureBytes<C> = GenericArray<u8, SignatureSize<C>>;
113
114/// ECDSA signature (fixed-size). Generic over elliptic curve types.
115///
116/// Serialized as fixed-sized big endian scalar values with no added framing:
117///
118/// - `r`: field element size for the given curve, big-endian
119/// - `s`: field element size for the given curve, big-endian
120///
121/// For example, in a curve with a 256-bit modulus like NIST P-256 or
122/// secp256k1, `r` and `s` will both be 32-bytes, resulting in a signature
123/// with a total of 64-bytes.
124///
125/// ASN.1 DER-encoded signatures also supported via the
126/// [`Signature::from_der`] and [`Signature::to_der`] methods.
127#[derive(Clone, Eq, PartialEq)]
128pub struct Signature<C: PrimeCurve>
129where
130    SignatureSize<C>: ArrayLength<u8>,
131{
132    bytes: SignatureBytes<C>,
133}
134
135impl<C> Signature<C>
136where
137    C: PrimeCurve,
138    SignatureSize<C>: ArrayLength<u8>,
139{
140    /// Parse a signature from ASN.1 DER
141    #[cfg(feature = "der")]
142    #[cfg_attr(docsrs, doc(cfg(feature = "der")))]
143    pub fn from_der(bytes: &[u8]) -> Result<Self>
144    where
145        der::MaxSize<C>: ArrayLength<u8>,
146        <FieldSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
147    {
148        der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
149    }
150
151    /// Create a [`Signature`] from the serialized `r` and `s` scalar values
152    /// which comprise the signature.
153    pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
154        Self::try_from(r.into().concat(s.into()).as_slice())
155    }
156
157    /// Split the signature into its `r` and `s` components, represented as bytes.
158    pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
159        let (r_bytes, s_bytes) = self.bytes.split_at(C::UInt::BYTE_SIZE);
160
161        (
162            GenericArray::clone_from_slice(r_bytes),
163            GenericArray::clone_from_slice(s_bytes),
164        )
165    }
166
167    /// Serialize this signature as ASN.1 DER
168    #[cfg(feature = "der")]
169    #[cfg_attr(docsrs, doc(cfg(feature = "der")))]
170    pub fn to_der(&self) -> der::Signature<C>
171    where
172        der::MaxSize<C>: ArrayLength<u8>,
173        <FieldSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
174    {
175        let (r, s) = self.bytes.split_at(C::UInt::BYTE_SIZE);
176        der::Signature::from_scalar_bytes(r, s).expect("DER encoding error")
177    }
178}
179
180#[cfg(feature = "arithmetic")]
181#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
182impl<C> Signature<C>
183where
184    C: PrimeCurve + ProjectiveArithmetic,
185    SignatureSize<C>: ArrayLength<u8>,
186{
187    /// Get the `r` component of this signature
188    pub fn r(&self) -> NonZeroScalar<C> {
189        NonZeroScalar::try_from(self.split_bytes().0.as_slice())
190            .expect("r-component ensured valid in constructor")
191    }
192
193    /// Get the `s` component of this signature
194    pub fn s(&self) -> NonZeroScalar<C> {
195        NonZeroScalar::try_from(self.split_bytes().1.as_slice())
196            .expect("s-component ensured valid in constructor")
197    }
198
199    /// Split the signature into its `r` and `s` scalars.
200    pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
201        (self.r(), self.s())
202    }
203
204    /// Normalize signature into "low S" form as described in
205    /// [BIP 0062: Dealing with Malleability][1].
206    ///
207    /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
208    pub fn normalize_s(&self) -> Option<Self>
209    where
210        Scalar<C>: NormalizeLow,
211    {
212        self.s().normalize_low().map(|s_low| {
213            let mut result = self.clone();
214            result.bytes[C::UInt::BYTE_SIZE..].copy_from_slice(&s_low.to_repr());
215            result
216        })
217    }
218}
219
220impl<C> signature_flow::Signature for Signature<C>
221where
222    C: PrimeCurve,
223    SignatureSize<C>: ArrayLength<u8>,
224{
225    fn from_bytes(bytes: &[u8]) -> Result<Self> {
226        Self::try_from(bytes)
227    }
228}
229
230impl<C> AsRef<[u8]> for Signature<C>
231where
232    C: PrimeCurve,
233    SignatureSize<C>: ArrayLength<u8>,
234{
235    fn as_ref(&self) -> &[u8] {
236        self.bytes.as_slice()
237    }
238}
239
240impl<C> Copy for Signature<C>
241where
242    C: PrimeCurve,
243    SignatureSize<C>: ArrayLength<u8>,
244    <SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
245{
246}
247
248impl<C> Debug for Signature<C>
249where
250    C: PrimeCurve,
251    SignatureSize<C>: ArrayLength<u8>,
252{
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        write!(
255            f,
256            "ecdsa::Signature<{:?}>({:?})",
257            C::default(),
258            self.as_ref()
259        )
260    }
261}
262
263impl<C> TryFrom<&[u8]> for Signature<C>
264where
265    C: PrimeCurve,
266    SignatureSize<C>: ArrayLength<u8>,
267{
268    type Error = Error;
269
270    fn try_from(bytes: &[u8]) -> Result<Self> {
271        if bytes.len() != C::UInt::BYTE_SIZE * 2 {
272            return Err(Error::new());
273        }
274
275        for scalar_bytes in bytes.chunks_exact(C::UInt::BYTE_SIZE) {
276            let scalar = ScalarCore::<C>::from_be_slice(scalar_bytes).map_err(|_| Error::new())?;
277
278            if scalar.is_zero().into() {
279                return Err(Error::new());
280            }
281        }
282
283        Ok(Self {
284            bytes: GenericArray::clone_from_slice(bytes),
285        })
286    }
287}
288
289/// Normalize a scalar (i.e. ECDSA S) to the lower half the field, as described
290/// in [BIP 0062: Dealing with Malleability][1].
291///
292/// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
293pub trait NormalizeLow: Sized {
294    /// Normalize scalar to the lower half of the field (i.e. negate it if it's
295    /// larger than half the curve's order).
296    ///
297    /// Returns an `Option` with a new scalar if the original wasn't already
298    /// low-normalized.
299    ///
300    /// May be implemented to work in variable time.
301    fn normalize_low(&self) -> Option<Self>;
302}