blob: 29dfbe5bf52208e10f65c9f60b3f3df97f2284af [file] [log] [blame]
#![doc = include_str!("../README.md")]
//! ## `serde` support
//!
//! When the `serde` feature of this crate is enabled, `Serialize` and
//! `Deserialize` impls are provided for the [`Signature`] and [`VerifyingKey`]
//! types.
//!
//! Please see type-specific documentation for more information.
//!
//! ## Interop
//!
//! Any crates which provide an implementation of ECDSA for a particular
//! elliptic curve can leverage the types from this crate, along with the
//! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in
//! a generic, interoperable way by leveraging the [`Signature`] type with in
//! conjunction with the [`signature::Signer`] and [`signature::Verifier`]
//! traits.
//!
//! For example, the [`ring-compat`] crate implements the [`signature::Signer`]
//! and [`signature::Verifier`] traits in conjunction with the
//! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to
//! wrap the ECDSA implementations from [*ring*] in a generic, interoperable
//! API.
//!
//! [`k256`]: https://docs.rs/k256
//! [`p256`]: https://docs.rs/p256
//! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html
//! [`p384`]: https://docs.rs/p384
//! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html
//! [`ring-compat`]: https://docs.rs/ring-compat
//! [*ring*]: https://docs.rs/ring
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code, clippy::unwrap_used)]
#![warn(missing_docs, rust_2018_idioms)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_root_url = "https://docs.rs/ecdsa/0.13.4"
)]
#[cfg(feature = "alloc")]
extern crate alloc;
mod recovery;
#[cfg(feature = "der")]
#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
pub mod der;
#[cfg(feature = "dev")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
pub mod dev;
#[cfg(feature = "hazmat")]
#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
pub mod hazmat;
#[cfg(feature = "sign")]
mod sign;
#[cfg(feature = "verify")]
mod verify;
pub use crate::recovery::RecoveryId;
// Re-export the `elliptic-curve` crate (and select types)
pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve};
// Re-export the `signature` crate (and select types)
pub use signature::{self, Error, Result};
#[cfg(feature = "sign")]
#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
pub use crate::sign::SigningKey;
#[cfg(feature = "verify")]
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
pub use crate::verify::VerifyingKey;
use core::{
fmt::{self, Debug},
ops::Add,
};
use elliptic_curve::{
bigint::Encoding as _,
generic_array::{sequence::Concat, ArrayLength, GenericArray},
FieldBytes, FieldSize, ScalarCore,
};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "arithmetic")]
use {
core::str,
elliptic_curve::{ff::PrimeField, IsHigh, NonZeroScalar, ScalarArithmetic},
};
#[cfg(feature = "serde")]
use elliptic_curve::serde::{ser, Serialize};
// TODO(tarcieri): support deserialization with the `arithmetic` feature disabled
#[cfg(all(feature = "arithmetic", feature = "serde"))]
use elliptic_curve::serde::{de, Deserialize};
/// Size of a fixed sized signature for the given elliptic curve.
pub type SignatureSize<C> = <FieldSize<C> as Add>::Output;
/// Fixed-size byte array containing an ECDSA signature
pub type SignatureBytes<C> = GenericArray<u8, SignatureSize<C>>;
/// ECDSA signature (fixed-size). Generic over elliptic curve types.
///
/// Serialized as fixed-sized big endian scalar values with no added framing:
///
/// - `r`: field element size for the given curve, big-endian
/// - `s`: field element size for the given curve, big-endian
///
/// For example, in a curve with a 256-bit modulus like NIST P-256 or
/// secp256k1, `r` and `s` will both be 32-bytes, resulting in a signature
/// with a total of 64-bytes.
///
/// ASN.1 DER-encoded signatures also supported via the
/// [`Signature::from_der`] and [`Signature::to_der`] methods.
///
/// # `serde` support
///
/// When the `serde` feature of this crate is enabled, it provides support for
/// serializing and deserializing ECDSA signatures using the `Serialize` and
/// `Deserialize` traits.
///
/// The serialization uses a 64-byte fixed encoding when used with binary
/// formats, and a hexadecimal encoding when used with text formats.
#[derive(Clone, Eq, PartialEq)]
pub struct Signature<C: PrimeCurve>
where
SignatureSize<C>: ArrayLength<u8>,
{
bytes: SignatureBytes<C>,
}
impl<C> Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
/// Parse a signature from ASN.1 DER
#[cfg(feature = "der")]
#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
pub fn from_der(bytes: &[u8]) -> Result<Self>
where
der::MaxSize<C>: ArrayLength<u8>,
<FieldSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
}
/// Create a [`Signature`] from the serialized `r` and `s` scalar values
/// which comprise the signature.
pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
Self::try_from(r.into().concat(s.into()).as_slice())
}
/// Split the signature into its `r` and `s` components, represented as bytes.
pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
let (r_bytes, s_bytes) = self.bytes.split_at(C::UInt::BYTE_SIZE);
(
GenericArray::clone_from_slice(r_bytes),
GenericArray::clone_from_slice(s_bytes),
)
}
/// Serialize this signature as ASN.1 DER
#[cfg(feature = "der")]
#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
pub fn to_der(&self) -> der::Signature<C>
where
der::MaxSize<C>: ArrayLength<u8>,
<FieldSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
let (r, s) = self.bytes.split_at(C::UInt::BYTE_SIZE);
der::Signature::from_scalar_bytes(r, s).expect("DER encoding error")
}
/// Convert this signature into a byte vector.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn to_vec(&self) -> Vec<u8> {
self.bytes.to_vec()
}
}
#[cfg(feature = "arithmetic")]
#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
impl<C> Signature<C>
where
C: PrimeCurve + ScalarArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
/// Get the `r` component of this signature
pub fn r(&self) -> NonZeroScalar<C> {
NonZeroScalar::try_from(self.split_bytes().0.as_slice())
.expect("r-component ensured valid in constructor")
}
/// Get the `s` component of this signature
pub fn s(&self) -> NonZeroScalar<C> {
NonZeroScalar::try_from(self.split_bytes().1.as_slice())
.expect("s-component ensured valid in constructor")
}
/// Split the signature into its `r` and `s` scalars.
pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
(self.r(), self.s())
}
/// Normalize signature into "low S" form as described in
/// [BIP 0062: Dealing with Malleability][1].
///
/// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
pub fn normalize_s(&self) -> Option<Self> {
let s = self.s();
if s.is_high().into() {
let neg_s = -s;
let mut result = self.clone();
result.bytes[C::UInt::BYTE_SIZE..].copy_from_slice(&neg_s.to_repr());
Some(result)
} else {
None
}
}
}
impl<C> signature::Signature for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Self::try_from(bytes)
}
}
impl<C> AsRef<[u8]> for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn as_ref(&self) -> &[u8] {
self.bytes.as_slice()
}
}
impl<C> Copy for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
<SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
{
}
impl<C> Debug for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ecdsa::Signature<{:?}>({:?})",
C::default(),
self.as_ref()
)
}
}
impl<C> TryFrom<&[u8]> for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
if bytes.len() != C::UInt::BYTE_SIZE * 2 {
return Err(Error::new());
}
for scalar_bytes in bytes.chunks_exact(C::UInt::BYTE_SIZE) {
let scalar = ScalarCore::<C>::from_be_slice(scalar_bytes).map_err(|_| Error::new())?;
if scalar.is_zero().into() {
return Err(Error::new());
}
}
Ok(Self {
bytes: GenericArray::clone_from_slice(bytes),
})
}
}
impl<C> fmt::Display for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}", self)
}
}
impl<C> fmt::LowerHex for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in &self.bytes {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl<C> fmt::UpperHex for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in &self.bytes {
write!(f, "{:02X}", byte)?;
}
Ok(())
}
}
#[cfg(feature = "arithmetic")]
#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
impl<C> str::FromStr for Signature<C>
where
C: PrimeCurve + ScalarArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
type Err = Error;
fn from_str(hex: &str) -> Result<Self> {
if hex.as_bytes().len() != C::UInt::BYTE_SIZE * 4 {
return Err(Error::new());
}
if !hex
.as_bytes()
.iter()
.all(|&byte| matches!(byte, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
{
return Err(Error::new());
}
let (r_hex, s_hex) = hex.split_at(C::UInt::BYTE_SIZE * 2);
let r = r_hex
.parse::<NonZeroScalar<C>>()
.map_err(|_| Error::new())?;
let s = s_hex
.parse::<NonZeroScalar<C>>()
.map_err(|_| Error::new())?;
Self::from_scalars(r, s)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<C> Serialize for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
#[cfg(not(feature = "alloc"))]
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
self.as_ref().serialize(serializer)
}
#[cfg(feature = "alloc")]
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
use alloc::string::ToString;
if serializer.is_human_readable() {
self.to_string().serialize(serializer)
} else {
self.as_ref().serialize(serializer)
}
}
}
// TODO(tarcieri): support deserialization with the `arithmetic` feature disabled
#[cfg(all(feature = "arithmetic", feature = "serde"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "arithmetic", feature = "serde"))))]
impl<'de, C> Deserialize<'de> for Signature<C>
where
C: PrimeCurve + ScalarArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
#[cfg(not(feature = "alloc"))]
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
use de::Error;
<&[u8]>::deserialize(deserializer)
.and_then(|bytes| Self::try_from(bytes).map_err(D::Error::custom))
}
#[cfg(feature = "alloc")]
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
use de::Error;
if deserializer.is_human_readable() {
<&str>::deserialize(deserializer)?
.parse()
.map_err(D::Error::custom)
} else {
<&[u8]>::deserialize(deserializer)
.and_then(|bytes| Self::try_from(bytes).map_err(D::Error::custom))
}
}
}