diff --git a/Cargo.lock b/Cargo.lock index 8e679358..0897aca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,15 +168,6 @@ dependencies = [ "signature", ] -[[package]] -name = "ed25519" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc" -dependencies = [ - "signature", -] - [[package]] name = "ed25519" version = "1.3.0" @@ -193,6 +184,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" +dependencies = [ + "signature", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" @@ -200,7 +200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", - "ed25519 1.2.0", + "ed25519 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand", "serde", "sha2", @@ -222,7 +222,7 @@ dependencies = [ [[package]] name = "elliptic-curve" version = "0.11.0-pre" -source = "git+https://github.com/RustCrypto/traits.git#7d813023da0ce6e418e4f7bf3b0dda9914a891cb" +source = "git+https://github.com/RustCrypto/traits.git#88d5ea3ad633d13c9ac3b064c084f645830d7713" dependencies = [ "crypto-bigint 0.3.2", "der 0.5.1", @@ -233,6 +233,7 @@ dependencies = [ "pem-rfc7468 0.2.3", "rand_core 0.6.3", "sec1", + "serde", "subtle", "zeroize", ] @@ -495,7 +496,7 @@ dependencies = [ "aead", "digest", "ecdsa 0.12.4", - "ed25519 1.2.0", + "ed25519 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array", "opaque-debug", "p256", @@ -506,9 +507,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2503adcd8c83e7081b3f9a3173f328f4d6cd50e116aaa324389b46f2d771d595" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" dependencies = [ "der 0.5.1", "generic-array", diff --git a/ecdsa/Cargo.toml b/ecdsa/Cargo.toml index 48d3c16c..6ce62eec 100644 --- a/ecdsa/Cargo.toml +++ b/ecdsa/Cargo.toml @@ -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"] diff --git a/ecdsa/src/lib.rs b/ecdsa/src/lib.rs index cc2720ed..1a275ded 100644 --- a/ecdsa/src/lib.rs +++ b/ecdsa/src/lib.rs @@ -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 = as Add>::Output; @@ -290,3 +296,138 @@ where }) } } + +impl fmt::Display for Signature +where + C: PrimeCurve, + SignatureSize: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:X}", self) + } +} + +impl fmt::LowerHex for Signature +where + C: PrimeCurve, + SignatureSize: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in &self.bytes { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + +impl fmt::UpperHex for Signature +where + C: PrimeCurve, + SignatureSize: ArrayLength, +{ + 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 str::FromStr for Signature +where + C: PrimeCurve + ScalarArithmetic, + SignatureSize: ArrayLength, +{ + type Err = Error; + + fn from_str(hex: &str) -> Result { + 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::>() + .map_err(|_| Error::new())?; + + let s = s_hex + .parse::>() + .map_err(|_| Error::new())?; + + Self::from_scalars(r, s) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl Serialize for Signature +where + C: PrimeCurve + ScalarArithmetic, + SignatureSize: ArrayLength, +{ + #[cfg(not(feature = "alloc"))] + fn serialize(&self, serializer: S) -> core::result::Result + where + S: ser::Serializer, + { + self.as_ref().serialize(serializer) + } + + #[cfg(feature = "alloc")] + fn serialize(&self, serializer: S) -> core::result::Result + 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 +where + C: PrimeCurve + ScalarArithmetic, + SignatureSize: ArrayLength, +{ + #[cfg(not(feature = "alloc"))] + fn deserialize(deserializer: D) -> core::result::Result + 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(deserializer: D) -> core::result::Result + 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)) + } + } +} diff --git a/ecdsa/src/verify.rs b/ecdsa/src/verify.rs index 32edd186..616bff68 100644 --- a/ecdsa/src/verify.rs +++ b/ecdsa/src/verify.rs @@ -14,7 +14,7 @@ use elliptic_curve::{ use signature::{digest::Digest, DigestVerifier, Verifier}; #[cfg(feature = "pkcs8")] -use crate::elliptic_curve::{ +use elliptic_curve::{ pkcs8::{self, DecodePublicKey}, AlgorithmParameters, }; @@ -22,6 +22,10 @@ use crate::elliptic_curve::{ #[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 @@ -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 Serialize for VerifyingKey +where + C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic + PointCompression, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: sec1::ModulusSize, +{ + fn serialize(&self, serializer: S) -> core::result::Result + 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 +where + C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic + PointCompression, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: sec1::ModulusSize, +{ + fn deserialize(deserializer: D) -> core::result::Result + where + D: de::Deserializer<'de>, + { + PublicKey::::deserialize(deserializer).map(Into::into) + } +}