Skip to content

Commit

Permalink
ecdsa: serde support (#406)
Browse files Browse the repository at this point in the history
Leverages the `serde` support added upstream in `elliptic-curve`:
RustCrypto/traits#818

Adds support for `serde` serialization of `Signature` and `VerifyingKey`
  • Loading branch information
tarcieri authored Nov 19, 2021
1 parent e306f16 commit 64ce58c
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 16 deletions.
29 changes: 15 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ecdsa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ digest = ["signature/digest-preview"]
hazmat = []
pkcs8 = ["elliptic-curve/pkcs8", "der"]
pem = ["elliptic-curve/pem", "pkcs8"]
serde = ["elliptic-curve/serde"]
sign = ["arithmetic", "digest", "hazmat", "hmac"]
std = ["alloc", "elliptic-curve/std", "signature/std"]
verify = ["arithmetic", "digest", "hazmat"]
Expand Down
143 changes: 142 additions & 1 deletion ecdsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ use elliptic_curve::{
};

#[cfg(feature = "arithmetic")]
use elliptic_curve::{ff::PrimeField, IsHigh, NonZeroScalar, ScalarArithmetic};
use {
core::str,
elliptic_curve::{ff::PrimeField, IsHigh, NonZeroScalar, ScalarArithmetic},
};

#[cfg(feature = "serde")]
use elliptic_curve::serde::{de, ser, Deserialize, Serialize};

/// Size of a fixed sized signature for the given elliptic curve.
pub type SignatureSize<C> = <FieldSize<C> as Add>::Output;
Expand Down Expand Up @@ -290,3 +296,138 @@ where
})
}
}

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 + ScalarArithmetic,
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)
}
}
}

#[cfg(all(feature = "arithmetic", feature = "serde"))]
#[cfg_attr(docsrs, doc(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(|slice| 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))
}
}
}
38 changes: 37 additions & 1 deletion ecdsa/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ use elliptic_curve::{
use signature::{digest::Digest, DigestVerifier, Verifier};

#[cfg(feature = "pkcs8")]
use crate::elliptic_curve::{
use elliptic_curve::{
pkcs8::{self, DecodePublicKey},
AlgorithmParameters,
};

#[cfg(feature = "pem")]
use core::str::FromStr;

#[cfg(all(feature = "pem", feature = "serde"))]
#[cfg_attr(docsrs, doc(all(feature = "pem", feature = "serde")))]
use elliptic_curve::serde::{de, ser, Deserialize, Serialize};

/// ECDSA verification key (i.e. public key). Generic over elliptic curves.
///
/// Requires an [`elliptic_curve::ProjectiveArithmetic`] impl on the curve, and a
Expand Down Expand Up @@ -223,3 +227,35 @@ where
Self::from_public_key_pem(s).map_err(|_| Error::new())
}
}

#[cfg(all(feature = "pem", feature = "serde"))]
#[cfg_attr(docsrs, doc(all(feature = "pem", feature = "serde")))]
impl<C> Serialize for VerifyingKey<C>
where
C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldSize<C>: sec1::ModulusSize,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
self.inner.serialize(serializer)
}
}

#[cfg(all(feature = "pem", feature = "serde"))]
#[cfg_attr(docsrs, doc(all(feature = "pem", feature = "serde")))]
impl<'de, C> Deserialize<'de> for VerifyingKey<C>
where
C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldSize<C>: sec1::ModulusSize,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
PublicKey::<C>::deserialize(deserializer).map(Into::into)
}
}

0 comments on commit 64ce58c

Please sign in to comment.