mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
ecdsa: Add Asn1Signature type
Support for ASN.1 DER-encoded signatures. Uses a GenericArray to store the variable-width signature, calculating maximum DER framing overhead using typenum's type-level arithmetic.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
//! ASN.1 DER-encoded ECDSA signatures
|
||||
|
||||
use crate::curve::Curve;
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::{self, Debug},
|
||||
ops::Add,
|
||||
};
|
||||
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
|
||||
use signature::Error;
|
||||
|
||||
/// Maximum overhead of an ASN.1 DER-encoded ECDSA signature for a given curve:
|
||||
/// 9 bytes.
|
||||
///
|
||||
/// Includes 3-byte ASN.1 DER header:
|
||||
///
|
||||
/// - 1-byte: ASN.1 `SEQUENCE` tag (0x30)
|
||||
/// - 2-byte: length
|
||||
///
|
||||
/// ...followed by two ASN.1 `INTEGER` values, which each have a header whose
|
||||
/// maximum length is the following:
|
||||
///
|
||||
/// - 1-byte: ASN.1 `INTEGER` tag (0x02)
|
||||
/// - 1-byte: length
|
||||
/// - 1-byte: zero to indicate value is positive (`INTEGER` is signed)
|
||||
type MaxOverhead = generic_array::typenum::U9;
|
||||
|
||||
/// Maximum size of an ASN.1 DER encoded signature for the given elliptic curve.
|
||||
// TODO(tarcieri): const generics
|
||||
pub type MaxSize<ScalarSize> = <<ScalarSize as Add>::Output as Add<MaxOverhead>>::Output;
|
||||
|
||||
/// ASN.1 DER-encoded ECDSA signature generic over elliptic curves.
|
||||
pub struct Asn1Signature<C: Curve>
|
||||
where
|
||||
<C::ScalarSize as Add>::Output: Add<MaxOverhead>,
|
||||
MaxSize<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
/// ASN.1 DER-encoded signature data
|
||||
bytes: GenericArray<u8, MaxSize<C::ScalarSize>>,
|
||||
|
||||
/// Length of the signature in bytes (DER is variable-width)
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl<C: Curve> signature::Signature for Asn1Signature<C>
|
||||
where
|
||||
<C::ScalarSize as Add>::Output: Add<MaxOverhead>,
|
||||
MaxSize<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
|
||||
bytes.as_ref().try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Curve> AsRef<[u8]> for Asn1Signature<C>
|
||||
where
|
||||
<C::ScalarSize as Add>::Output: Add<MaxOverhead>,
|
||||
MaxSize<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.bytes.as_slice()[..self.length]
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Curve> Debug for Asn1Signature<C>
|
||||
where
|
||||
<C::ScalarSize as Add>::Output: Add<MaxOverhead>,
|
||||
MaxSize<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Asn1Signature<{:?}> {{ bytes: {:?}) }}",
|
||||
C::default(),
|
||||
self.as_ref()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Curve> TryFrom<&'a [u8]> for Asn1Signature<C>
|
||||
where
|
||||
<C::ScalarSize as Add>::Output: Add<MaxOverhead>,
|
||||
MaxSize<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(slice: &'a [u8]) -> Result<Self, Error> {
|
||||
let length = slice.len();
|
||||
|
||||
// TODO: better validate signature is well-formed ASN.1 DER
|
||||
if <MaxSize<C::ScalarSize>>::to_usize() < length {
|
||||
return Err(Error::new());
|
||||
}
|
||||
|
||||
let mut bytes = GenericArray::default();
|
||||
bytes.as_mut_slice()[..length].copy_from_slice(slice);
|
||||
Ok(Self { bytes, length })
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -6,12 +6,12 @@ pub mod secp256k1;
|
||||
|
||||
pub use self::{nistp256::NistP256, nistp384::NistP384, secp256k1::Secp256k1};
|
||||
|
||||
use core::fmt::Debug;
|
||||
use core::{fmt::Debug, ops::Add};
|
||||
|
||||
/// Elliptic curve in short Weierstrass form suitable for use with ECDSA
|
||||
pub trait Curve: Debug + Default + Send + Sync {
|
||||
/// Size of an integer modulo p (i.e. the curve's order) when serialized
|
||||
/// as octets (i.e. bytes). This also describes the size of an ECDSA
|
||||
/// private key, as well as half the size of a fixed-width signature.
|
||||
type ScalarSize;
|
||||
type ScalarSize: Add; // `Add<Self>` impl from typenum used for doubling
|
||||
}
|
||||
|
||||
@@ -26,5 +26,8 @@ impl Curve for NistP256 {
|
||||
type ScalarSize = U32;
|
||||
}
|
||||
|
||||
/// ASN.1 DER encoded NIST P-256 ECDSA signature
|
||||
pub type Asn1Signature = crate::Asn1Signature<NistP256>;
|
||||
|
||||
/// Fixed-sized (a.k.a. "raw") NIST P-256 ECDSA signature
|
||||
pub type FixedSignature = crate::fixed_signature::FixedSignature<NistP256>;
|
||||
pub type FixedSignature = crate::FixedSignature<NistP256>;
|
||||
|
||||
@@ -27,5 +27,8 @@ impl Curve for NistP384 {
|
||||
type ScalarSize = U48;
|
||||
}
|
||||
|
||||
/// ASN.1 DER encoded NIST P-384 ECDSA signature
|
||||
pub type Asn1Signature = crate::Asn1Signature<NistP384>;
|
||||
|
||||
/// Fixed-sized (a.k.a. "raw") NIST P-384 ECDSA signature
|
||||
pub type FixedSignature = crate::fixed_signature::FixedSignature<NistP384>;
|
||||
pub type FixedSignature = crate::FixedSignature<NistP384>;
|
||||
|
||||
@@ -17,5 +17,8 @@ impl Curve for Secp256k1 {
|
||||
type ScalarSize = U32;
|
||||
}
|
||||
|
||||
/// ASN.1 DER encoded secp256k1 ECDSA signature
|
||||
pub type Asn1Signature = crate::Asn1Signature<Secp256k1>;
|
||||
|
||||
/// Fixed-sized (a.k.a. "raw") secp256k1 ECDSA signature
|
||||
pub type FixedSignature = crate::fixed_signature::FixedSignature<Secp256k1>;
|
||||
pub type FixedSignature = crate::FixedSignature<Secp256k1>;
|
||||
|
||||
@@ -1,47 +1,41 @@
|
||||
//! Fixed-sized (a.k.a. "raw") ECDSA signatures
|
||||
|
||||
use crate::curve::Curve;
|
||||
use core::{fmt, ops::Add};
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::{self, Debug},
|
||||
ops::Add,
|
||||
};
|
||||
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
|
||||
use signature::Error;
|
||||
|
||||
/// Size of a fixed sized signature: double that of the scalar size
|
||||
// TODO(tarcieri): use typenum's `Double` op or switch to const generics
|
||||
pub type Size<ScalarSize> = <ScalarSize as Add<ScalarSize>>::Output;
|
||||
/// Size of a fixed sized signature for the given elliptic curve.
|
||||
// TODO(tarcieri): const generics
|
||||
pub type Size<ScalarSize> = <ScalarSize as Add>::Output;
|
||||
|
||||
/// Fixed-sized (a.k.a. "raw") ECDSA signatures: serialized as fixed-sized
|
||||
/// big endian scalar values with no additional framing.
|
||||
/// Fixed-sized (a.k.a. "raw") ECDSA signatures generic over elliptic curves.
|
||||
///
|
||||
/// These signatures are serialized as fixed-sized big endian scalar values
|
||||
/// with no additional framing.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct FixedSignature<C>
|
||||
pub struct FixedSignature<C: Curve>
|
||||
where
|
||||
C: Curve,
|
||||
C::ScalarSize: Add<C::ScalarSize>,
|
||||
Size<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
bytes: GenericArray<u8, Size<C::ScalarSize>>,
|
||||
}
|
||||
|
||||
impl<C> signature::Signature for FixedSignature<C>
|
||||
impl<C: Curve> signature::Signature for FixedSignature<C>
|
||||
where
|
||||
C: Curve,
|
||||
C::ScalarSize: Add<C::ScalarSize>,
|
||||
Size<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
|
||||
if bytes.as_ref().len() == <Size<C::ScalarSize>>::to_usize() {
|
||||
Ok(Self {
|
||||
bytes: GenericArray::clone_from_slice(bytes.as_ref()),
|
||||
})
|
||||
} else {
|
||||
Err(Error::new())
|
||||
}
|
||||
bytes.as_ref().try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> AsRef<[u8]> for FixedSignature<C>
|
||||
impl<C: Curve> AsRef<[u8]> for FixedSignature<C>
|
||||
where
|
||||
C: Curve,
|
||||
C::ScalarSize: Add<C::ScalarSize>,
|
||||
Size<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
@@ -49,10 +43,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> fmt::Debug for FixedSignature<C>
|
||||
impl<C: Curve> Debug for FixedSignature<C>
|
||||
where
|
||||
C: Curve,
|
||||
C::ScalarSize: Add<C::ScalarSize>,
|
||||
Size<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@@ -64,3 +56,20 @@ where
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: Curve> TryFrom<&'a [u8]> for FixedSignature<C>
|
||||
where
|
||||
Size<C::ScalarSize>: ArrayLength<u8>,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(bytes: &'a [u8]) -> Result<Self, Error> {
|
||||
if bytes.len() == <Size<C::ScalarSize>>::to_usize() {
|
||||
Ok(Self {
|
||||
bytes: GenericArray::clone_from_slice(bytes),
|
||||
})
|
||||
} else {
|
||||
Err(Error::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -11,7 +11,8 @@
|
||||
|
||||
pub use signature;
|
||||
|
||||
pub mod asn1_signature;
|
||||
pub mod curve;
|
||||
mod fixed_signature;
|
||||
pub mod fixed_signature;
|
||||
|
||||
pub use self::{curve::Curve, fixed_signature::FixedSignature};
|
||||
pub use self::{asn1_signature::Asn1Signature, curve::Curve, fixed_signature::FixedSignature};
|
||||
|
||||
+17
-17
@@ -63,6 +63,23 @@ impl AsRef<[u8]> for Signature {
|
||||
}
|
||||
}
|
||||
|
||||
// can't derive `Debug`, `PartialEq`, or `Eq` below because core array types
|
||||
// only have trait implementations for lengths 0..=32
|
||||
// TODO(tarcieri): derive `PartialEq` and `Eq` after const generics are available
|
||||
impl Debug for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ed25519::Signature({:?})", &self.0[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Signature {}
|
||||
|
||||
impl PartialEq for Signature {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_ref().eq(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; SIGNATURE_LENGTH]> for Signature {
|
||||
fn from(bytes: [u8; SIGNATURE_LENGTH]) -> Signature {
|
||||
Signature(bytes)
|
||||
@@ -86,23 +103,6 @@ impl<'a> TryFrom<&'a [u8]> for Signature {
|
||||
}
|
||||
}
|
||||
|
||||
// can't derive `Debug`, `PartialEq`, or `Eq` below because core array types
|
||||
// only have trait implementations for lengths 0..=32
|
||||
// TODO(tarcieri): derive `PartialEq` and `Eq` after const generics are available
|
||||
impl Debug for Signature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ed25519::Signature({:?})", &self.0[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Signature {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_ref().eq(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Signature {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Signature {
|
||||
fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
|
||||
Reference in New Issue
Block a user