Skip to content

Commit

Permalink
[WIP] ecdsa::curve::secp256k1::RecoverableSignature
Browse files Browse the repository at this point in the history
Adds a signature type for Ethereum-style recoverable signatures.
  • Loading branch information
tarcieri committed Jun 28, 2020
1 parent 2110ec6 commit cf1a5d8
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 15 deletions.
13 changes: 8 additions & 5 deletions ecdsa/src/asn1_signature.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! ASN.1 DER-encoded ECDSA signatures

use crate::generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
use crate::{convert::ScalarPair, curve::Curve};
use crate::{
convert::ScalarPair,
curve::Curve,
generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
Error,
};
use core::{
convert::{TryFrom, TryInto},
fmt::{self, Debug},
ops::Add,
};
use signature::Error;

/// Maximum overhead of an ASN.1 DER-encoded ECDSA signature for a given curve:
/// 9-bytes.
Expand Down Expand Up @@ -76,14 +79,14 @@ where
}
}

impl<'a, C: Curve> TryFrom<&'a [u8]> for Asn1Signature<C>
impl<C: Curve> TryFrom<&[u8]> for Asn1Signature<C>
where
MaxSize<C::ScalarSize>: ArrayLength<u8>,
<C::ScalarSize as Add>::Output: ArrayLength<u8> + Add<MaxOverhead>,
{
type Error = Error;

fn try_from(slice: &'a [u8]) -> Result<Self, Error> {
fn try_from(slice: &[u8]) -> Result<Self, Error> {
let length = slice.len();

if <MaxSize<C::ScalarSize>>::to_usize() < length {
Expand Down
7 changes: 5 additions & 2 deletions ecdsa/src/curve/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//! secp256k1 elliptic curve

pub mod recoverable_signature;

pub use k256::{PublicKey, Secp256k1, SecretKey};
pub use recoverable_signature::{RecoverableSignature, RecoveryId};

/// ASN.1 DER encoded secp256k1 ECDSA signature
/// ASN.1 DER encoded secp256k1 ECDSA signature.
pub type Asn1Signature = crate::Asn1Signature<Secp256k1>;

/// Fixed-sized (a.k.a. "raw") secp256k1 ECDSA signature
/// Fixed-sized (a.k.a. "raw") secp256k1 ECDSA signature.
pub type FixedSignature = crate::FixedSignature<Secp256k1>;

#[cfg(feature = "digest")]
Expand Down
101 changes: 101 additions & 0 deletions ecdsa/src/curve/secp256k1/recoverable_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Ethereum-style "recoverable signatures"

use super::FixedSignature;
use crate::{
generic_array::{typenum::U64, GenericArray},
Error,
};
use core::{
convert::{TryFrom, TryInto},
fmt::{self, Debug},
};

/// Size of an Ethereum-style recoverable signature in bytes
pub const SIZE: usize = 65;

/// Ethereum-style "recoverable signatures" which allow for the recovery of
/// the signer's [`PublicKey`] from the signature itself.
///
/// These consist of [`FixedSignature`] followed by a 1-byte [`RecoveryId`]
/// (65-bytes total)
#[derive(Copy, Clone)]
pub struct RecoverableSignature {
bytes: [u8; SIZE],
}

impl RecoverableSignature {
/// Get the [`RecoveryId`] for this signature
pub fn recovery_id(self) -> RecoveryId {
self.bytes[0].try_into().expect("invalid recovery ID")
}
}

impl signature::Signature for RecoverableSignature {
fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
bytes.try_into()
}
}

impl AsRef<[u8]> for RecoverableSignature {
fn as_ref(&self) -> &[u8] {
&self.bytes[..]
}
}

impl Debug for RecoverableSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RecoverableSignature {{ bytes: {:?}) }}", self.as_ref())
}
}

// TODO(tarcieri): derive `Eq` after const generics are available
impl Eq for RecoverableSignature {}

// TODO(tarcieri): derive `PartialEq` after const generics are available
impl PartialEq for RecoverableSignature {
fn eq(&self, other: &Self) -> bool {
self.as_ref().eq(other.as_ref())
}
}

impl TryFrom<&[u8]> for RecoverableSignature {
type Error = Error;

fn try_from(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() == SIZE && RecoveryId::try_from(bytes[64]).is_ok() {
let mut arr = [0u8; SIZE];
arr.copy_from_slice(bytes);
Ok(Self { bytes: arr })
} else {
Err(Error::new())
}
}
}

impl From<RecoverableSignature> for FixedSignature {
fn from(sig: RecoverableSignature) -> FixedSignature {
GenericArray::<u8, U64>::clone_from_slice(&sig.bytes[..64]).into()
}
}

/// Identifier used to compute a `PublicKey` from a [`RecoverableSignature`]
#[derive(Copy, Clone, Debug)]
pub struct RecoveryId(u8);

impl TryFrom<u8> for RecoveryId {
type Error = Error;

fn try_from(byte: u8) -> Result<Self, Error> {
if byte < 4 {
Ok(Self(byte))
} else {
Err(Error::new())
}
}
}

impl From<RecoveryId> for u8 {
fn from(recovery_id: RecoveryId) -> u8 {
recovery_id.0
}
}
12 changes: 7 additions & 5 deletions ecdsa/src/fixed_signature.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Fixed-sized (a.k.a. "raw") ECDSA signatures

use crate::curve::Curve;
use crate::generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
use crate::{
curve::Curve,
generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
Error,
};
use core::{
convert::{TryFrom, TryInto},
fmt::{self, Debug},
ops::Add,
};
use signature::Error;

/// Size of a fixed sized signature for the given elliptic curve.
pub type Size<ScalarSize> = <ScalarSize as Add>::Output;
Expand Down Expand Up @@ -56,13 +58,13 @@ where
}
}

impl<'a, C: Curve> TryFrom<&'a [u8]> for FixedSignature<C>
impl<C: Curve> TryFrom<&[u8]> for FixedSignature<C>
where
Size<C::ScalarSize>: ArrayLength<u8>,
{
type Error = Error;

fn try_from(bytes: &'a [u8]) -> Result<Self, Error> {
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() == <Size<C::ScalarSize>>::to_usize() {
Ok(Self {
bytes: GenericArray::clone_from_slice(bytes),
Expand Down
8 changes: 5 additions & 3 deletions ecdsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ pub mod fixed_signature;
#[cfg(feature = "test-vectors")]
pub mod test_vectors;

// Re-export the `signature` crate
pub use signature;

pub use self::{asn1_signature::Asn1Signature, fixed_signature::FixedSignature};

// Re-export the `elliptic-curve` crate (and select types)
pub use elliptic_curve::{
self, generic_array,
weierstrass::{Curve, PublicKey},
SecretKey,
};

// Re-export the `signature` crate (and select types)
pub use signature::{self, Error};

0 comments on commit cf1a5d8

Please sign in to comment.