#![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.0-pre"
)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[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")]
#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
pub mod rfc6979;
#[cfg(feature = "sign")]
mod sign;
#[cfg(feature = "verify")]
mod verify;
pub use elliptic_curve_flow::{self, sec1::EncodedPoint, PrimeCurve};
pub use signature_flow::{self, Error, Result};
#[cfg(feature = "sign")]
#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
pub use sign::SigningKey;
#[cfg(feature = "verify")]
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
pub use verify::VerifyingKey;
use core::{
convert::TryFrom,
fmt::{self, Debug},
ops::Add,
};
use elliptic_curve_flow::{
bigint::Encoding as _,
generic_array::{sequence::Concat, ArrayLength, GenericArray},
FieldBytes, FieldSize, ScalarCore,
};
#[cfg(feature = "arithmetic")]
use elliptic_curve_flow::{group::ff::PrimeField, NonZeroScalar, ProjectiveArithmetic, Scalar};
pub type SignatureSize<C> = <FieldSize<C> as Add>::Output;
pub type SignatureBytes<C> = GenericArray<u8, SignatureSize<C>>;
#[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>,
{
#[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)
}
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())
}
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),
)
}
#[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")
}
}
#[cfg(feature = "arithmetic")]
#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
impl<C> Signature<C>
where
C: PrimeCurve + ProjectiveArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
pub fn r(&self) -> NonZeroScalar<C> {
NonZeroScalar::try_from(self.split_bytes().0.as_slice())
.expect("r-component ensured valid in constructor")
}
pub fn s(&self) -> NonZeroScalar<C> {
NonZeroScalar::try_from(self.split_bytes().1.as_slice())
.expect("s-component ensured valid in constructor")
}
pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
(self.r(), self.s())
}
pub fn normalize_s(&self) -> Option<Self>
where
Scalar<C>: NormalizeLow,
{
self.s().normalize_low().map(|s_low| {
let mut result = self.clone();
result.bytes[C::UInt::BYTE_SIZE..].copy_from_slice(&s_low.to_repr());
result
})
}
}
impl<C> signature_flow::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),
})
}
}
pub trait NormalizeLow: Sized {
fn normalize_low(&self) -> Option<Self>;
}