diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/ecdsa/src | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/ecdsa/src')
-rw-r--r-- | vendor/ecdsa/src/der.rs | 349 | ||||
-rw-r--r-- | vendor/ecdsa/src/dev.rs | 221 | ||||
-rw-r--r-- | vendor/ecdsa/src/hazmat.rs | 261 | ||||
-rw-r--r-- | vendor/ecdsa/src/lib.rs | 421 | ||||
-rw-r--r-- | vendor/ecdsa/src/recovery.rs | 110 | ||||
-rw-r--r-- | vendor/ecdsa/src/sign.rs | 424 | ||||
-rw-r--r-- | vendor/ecdsa/src/verify.rs | 325 |
7 files changed, 2111 insertions, 0 deletions
diff --git a/vendor/ecdsa/src/der.rs b/vendor/ecdsa/src/der.rs new file mode 100644 index 000000000..5925df443 --- /dev/null +++ b/vendor/ecdsa/src/der.rs @@ -0,0 +1,349 @@ +//! Support for ECDSA signatures encoded as ASN.1 DER. + +use crate::{Error, Result}; +use core::{ + fmt, + ops::{Add, Range}, +}; +use der::{asn1::UIntRef, Decode, Encode, Reader}; +use elliptic_curve::{ + bigint::Encoding as _, + consts::U9, + generic_array::{ArrayLength, GenericArray}, + FieldSize, PrimeCurve, +}; + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +#[cfg(feature = "serde")] +use serdect::serde::{de, ser, Deserialize, Serialize}; + +/// 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) +pub type MaxOverhead = U9; + +/// Maximum size of an ASN.1 DER encoded signature for the given elliptic curve. +pub type MaxSize<C> = <<FieldSize<C> as Add>::Output as Add<MaxOverhead>>::Output; + +/// Byte array containing a serialized ASN.1 signature +type SignatureBytes<C> = GenericArray<u8, MaxSize<C>>; + +/// ASN.1 DER-encoded signature. +/// +/// Generic over the scalar size of the elliptic curve. +pub struct Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + /// ASN.1 DER-encoded signature data + bytes: SignatureBytes<C>, + + /// Range of the `r` value within the signature + r_range: Range<usize>, + + /// Range of the `s` value within the signature + s_range: Range<usize>, +} + +impl<C> signature::Signature for Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + /// Parse an ASN.1 DER-encoded ECDSA signature from a byte slice + fn from_bytes(bytes: &[u8]) -> Result<Self> { + bytes.try_into() + } +} + +#[allow(clippy::len_without_is_empty)] +impl<C> Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + /// Get the length of the signature in bytes + pub fn len(&self) -> usize { + self.s_range.end + } + + /// Borrow this signature as a byte slice + pub fn as_bytes(&self) -> &[u8] { + &self.bytes.as_slice()[..self.len()] + } + + /// Serialize this signature as a boxed byte slice + #[cfg(feature = "alloc")] + pub fn to_bytes(&self) -> Box<[u8]> { + self.as_bytes().to_vec().into_boxed_slice() + } + + /// Create an ASN.1 DER encoded signature from big endian `r` and `s` scalars + pub(crate) fn from_scalar_bytes(r: &[u8], s: &[u8]) -> der::Result<Self> { + let r = UIntRef::new(r)?; + let s = UIntRef::new(s)?; + + let mut bytes = SignatureBytes::<C>::default(); + let mut writer = der::SliceWriter::new(&mut bytes); + + writer.sequence((r.encoded_len()? + s.encoded_len()?)?, |seq| { + seq.encode(&r)?; + seq.encode(&s) + })?; + + writer + .finish()? + .try_into() + .map_err(|_| der::Tag::Sequence.value_error()) + } + + /// Get the `r` component of the signature (leading zeros removed) + pub(crate) fn r(&self) -> &[u8] { + &self.bytes[self.r_range.clone()] + } + + /// Get the `s` component of the signature (leading zeros removed) + pub(crate) fn s(&self) -> &[u8] { + &self.bytes[self.s_range.clone()] + } +} + +impl<C> AsRef<[u8]> for Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl<C> fmt::Debug for Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ecdsa::der::Signature<{:?}>(", C::default())?; + + for &byte in self.as_ref() { + write!(f, "{:02X}", byte)?; + } + + write!(f, ")") + } +} + +impl<C> TryFrom<&[u8]> for Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + type Error = Error; + + fn try_from(input: &[u8]) -> Result<Self> { + let (r, s) = decode_der(input).map_err(|_| Error::new())?; + + if r.as_bytes().len() > C::UInt::BYTE_SIZE || s.as_bytes().len() > C::UInt::BYTE_SIZE { + return Err(Error::new()); + } + + let r_range = find_scalar_range(input, r.as_bytes())?; + let s_range = find_scalar_range(input, s.as_bytes())?; + + if s_range.end != input.len() { + return Err(Error::new()); + } + + let mut bytes = SignatureBytes::<C>::default(); + bytes[..s_range.end].copy_from_slice(input); + + Ok(Signature { + bytes, + r_range, + s_range, + }) + } +} + +impl<C> TryFrom<Signature<C>> for super::Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + type Error = Error; + + fn try_from(sig: Signature<C>) -> Result<super::Signature<C>> { + let mut bytes = super::SignatureBytes::<C>::default(); + let r_begin = C::UInt::BYTE_SIZE.saturating_sub(sig.r().len()); + let s_begin = bytes.len().saturating_sub(sig.s().len()); + bytes[r_begin..C::UInt::BYTE_SIZE].copy_from_slice(sig.r()); + bytes[s_begin..].copy_from_slice(sig.s()); + Self::try_from(bytes.as_slice()) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl<C> Serialize for Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serdect::slice::serialize_hex_upper_or_bin(&self.as_bytes(), serializer) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl<'de, C> Deserialize<'de> for Signature<C> +where + C: PrimeCurve, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + let mut buf = SignatureBytes::<C>::default(); + let slice = serdect::slice::deserialize_hex_or_bin(&mut buf, deserializer)?; + Self::try_from(slice).map_err(de::Error::custom) + } +} + +/// Decode the `r` and `s` components of a DER-encoded ECDSA signature. +fn decode_der(der_bytes: &[u8]) -> der::Result<(UIntRef<'_>, UIntRef<'_>)> { + let mut reader = der::SliceReader::new(der_bytes)?; + let header = der::Header::decode(&mut reader)?; + header.tag.assert_eq(der::Tag::Sequence)?; + + let ret = reader.read_nested(header.length, |reader| { + let r = UIntRef::decode(reader)?; + let s = UIntRef::decode(reader)?; + Ok((r, s)) + })?; + + reader.finish(ret) +} + +/// Locate the range within a slice at which a particular subslice is located +fn find_scalar_range(outer: &[u8], inner: &[u8]) -> Result<Range<usize>> { + let outer_start = outer.as_ptr() as usize; + let inner_start = inner.as_ptr() as usize; + let start = inner_start + .checked_sub(outer_start) + .ok_or_else(Error::new)?; + let end = start.checked_add(inner.len()).ok_or_else(Error::new)?; + Ok(Range { start, end }) +} + +#[cfg(all(feature = "digest", feature = "hazmat"))] +impl<C> signature::PrehashSignature for Signature<C> +where + C: PrimeCurve + crate::hazmat::DigestPrimitive, + MaxSize<C>: ArrayLength<u8>, + <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, +{ + type Digest = C::Digest; +} + +#[cfg(all(test, feature = "arithmetic"))] +mod tests { + use elliptic_curve::dev::MockCurve; + use signature::Signature as _; + + type Signature = crate::Signature<MockCurve>; + + const EXAMPLE_SIGNATURE: [u8; 64] = [ + 0xf3, 0xac, 0x80, 0x61, 0xb5, 0x14, 0x79, 0x5b, 0x88, 0x43, 0xe3, 0xd6, 0x62, 0x95, 0x27, + 0xed, 0x2a, 0xfd, 0x6b, 0x1f, 0x6a, 0x55, 0x5a, 0x7a, 0xca, 0xbb, 0x5e, 0x6f, 0x79, 0xc8, + 0xc2, 0xac, 0x8b, 0xf7, 0x78, 0x19, 0xca, 0x5, 0xa6, 0xb2, 0x78, 0x6c, 0x76, 0x26, 0x2b, + 0xf7, 0x37, 0x1c, 0xef, 0x97, 0xb2, 0x18, 0xe9, 0x6f, 0x17, 0x5a, 0x3c, 0xcd, 0xda, 0x2a, + 0xcc, 0x5, 0x89, 0x3, + ]; + + #[test] + fn test_fixed_to_asn1_signature_roundtrip() { + let signature1 = Signature::from_bytes(&EXAMPLE_SIGNATURE).unwrap(); + + // Convert to ASN.1 DER and back + let asn1_signature = signature1.to_der(); + let signature2 = Signature::from_der(asn1_signature.as_ref()).unwrap(); + + assert_eq!(signature1, signature2); + } + + #[test] + fn test_asn1_too_short_signature() { + assert!(Signature::from_der(&[]).is_err()); + assert!(Signature::from_der(&[der::Tag::Sequence.into()]).is_err()); + assert!(Signature::from_der(&[der::Tag::Sequence.into(), 0x00]).is_err()); + assert!(Signature::from_der(&[ + der::Tag::Sequence.into(), + 0x03, + der::Tag::Integer.into(), + 0x01, + 0x01 + ]) + .is_err()); + } + + #[test] + fn test_asn1_non_der_signature() { + // A minimal 8-byte ASN.1 signature parses OK. + assert!(Signature::from_der(&[ + der::Tag::Sequence.into(), + 0x06, // length of below + der::Tag::Integer.into(), + 0x01, // length of value + 0x01, // value=1 + der::Tag::Integer.into(), + 0x01, // length of value + 0x01, // value=1 + ]) + .is_ok()); + + // But length fields that are not minimally encoded should be rejected, as they are not + // valid DER, cf. + // https://github.com/google/wycheproof/blob/2196000605e4/testvectors/ecdsa_secp256k1_sha256_test.json#L57-L66 + assert!(Signature::from_der(&[ + der::Tag::Sequence.into(), + 0x81, // extended length: 1 length byte to come + 0x06, // length of below + der::Tag::Integer.into(), + 0x01, // length of value + 0x01, // value=1 + der::Tag::Integer.into(), + 0x01, // length of value + 0x01, // value=1 + ]) + .is_err()); + } +} diff --git a/vendor/ecdsa/src/dev.rs b/vendor/ecdsa/src/dev.rs new file mode 100644 index 000000000..ce00c7fbf --- /dev/null +++ b/vendor/ecdsa/src/dev.rs @@ -0,0 +1,221 @@ +//! Development-related functionality. + +// TODO(tarcieri): implement full set of tests from ECDSA2VS +// <https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/dss2/ecdsa2vs.pdf> + +/// ECDSA test vector +pub struct TestVector { + /// Private scalar + pub d: &'static [u8], + + /// Public key x-coordinate (`Qx`) + pub q_x: &'static [u8], + + /// Public key y-coordinate (`Qy`) + pub q_y: &'static [u8], + + /// Ephemeral scalar (a.k.a. nonce) + pub k: &'static [u8], + + /// Message digest (prehashed) + pub m: &'static [u8], + + /// Signature `r` component + pub r: &'static [u8], + + /// Signature `s` component + pub s: &'static [u8], +} + +/// Define ECDSA signing test. +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! new_signing_test { + ($curve:path, $vectors:expr) => { + use $crate::{ + elliptic_curve::{ + bigint::Encoding, generic_array::GenericArray, group::ff::PrimeField, Curve, + ProjectiveArithmetic, Scalar, + }, + hazmat::SignPrimitive, + }; + + fn decode_scalar(bytes: &[u8]) -> Option<Scalar<$curve>> { + if bytes.len() == <$curve as Curve>::UInt::BYTE_SIZE { + Scalar::<$curve>::from_repr(GenericArray::clone_from_slice(bytes)).into() + } else { + None + } + } + + #[test] + fn ecdsa_signing() { + for vector in $vectors { + let d = decode_scalar(vector.d).expect("invalid vector.d"); + let k = decode_scalar(vector.k).expect("invalid vector.m"); + let z = GenericArray::clone_from_slice(vector.m); + let sig = d.try_sign_prehashed(k, z).expect("ECDSA sign failed").0; + + assert_eq!(vector.r, sig.r().to_bytes().as_slice()); + assert_eq!(vector.s, sig.s().to_bytes().as_slice()); + } + } + }; +} + +/// Define ECDSA verification test. +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! new_verification_test { + ($curve:path, $vectors:expr) => { + use $crate::{ + elliptic_curve::{ + generic_array::GenericArray, + group::ff::PrimeField, + sec1::{EncodedPoint, FromEncodedPoint}, + AffinePoint, ProjectiveArithmetic, Scalar, + }, + hazmat::VerifyPrimitive, + Signature, + }; + + #[test] + fn ecdsa_verify_success() { + for vector in $vectors { + let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates( + GenericArray::from_slice(vector.q_x), + GenericArray::from_slice(vector.q_y), + false, + ); + + let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap(); + let z = GenericArray::clone_from_slice(vector.m); + + let sig = Signature::from_scalars( + GenericArray::clone_from_slice(vector.r), + GenericArray::clone_from_slice(vector.s), + ) + .unwrap(); + + let result = q.verify_prehashed(z, &sig); + assert!(result.is_ok()); + } + } + + #[test] + fn ecdsa_verify_invalid_s() { + for vector in $vectors { + let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates( + GenericArray::from_slice(vector.q_x), + GenericArray::from_slice(vector.q_y), + false, + ); + + let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap(); + let z = GenericArray::clone_from_slice(vector.m); + + // Flip a bit in `s` + let mut s_tweaked = GenericArray::clone_from_slice(vector.s); + s_tweaked[0] ^= 1; + + let sig = + Signature::from_scalars(GenericArray::clone_from_slice(vector.r), s_tweaked) + .unwrap(); + + let result = q.verify_prehashed(z, &sig); + assert!(result.is_err()); + } + } + + // TODO(tarcieri): test invalid Q, invalid r, invalid m + }; +} + +/// Define a Wycheproof verification test. +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! new_wycheproof_test { + ($name:ident, $test_name: expr, $curve:path) => { + use $crate::{elliptic_curve::sec1::EncodedPoint, signature::Verifier, Signature}; + + #[test] + fn $name() { + use blobby::Blob5Iterator; + use elliptic_curve::{bigint::Encoding as _, generic_array::typenum::Unsigned}; + + // Build a field element but allow for too-short input (left pad with zeros) + // or too-long input (check excess leftmost bytes are zeros). + fn element_from_padded_slice<C: elliptic_curve::Curve>( + data: &[u8], + ) -> elliptic_curve::FieldBytes<C> { + let point_len = C::UInt::BYTE_SIZE; + if data.len() >= point_len { + let offset = data.len() - point_len; + for v in data.iter().take(offset) { + assert_eq!(*v, 0, "EcdsaVerifier: point too large"); + } + elliptic_curve::FieldBytes::<C>::clone_from_slice(&data[offset..]) + } else { + // Provided slice is too short and needs to be padded with zeros + // on the left. Build a combined exact iterator to do this. + let iter = core::iter::repeat(0) + .take(point_len - data.len()) + .chain(data.iter().cloned()); + elliptic_curve::FieldBytes::<C>::from_exact_iter(iter).unwrap() + } + } + + fn run_test( + wx: &[u8], + wy: &[u8], + msg: &[u8], + sig: &[u8], + pass: bool, + ) -> Option<&'static str> { + let x = element_from_padded_slice::<$curve>(wx); + let y = element_from_padded_slice::<$curve>(wy); + let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates( + &x, &y, /* compress= */ false, + ); + let verifying_key = + $crate::VerifyingKey::<$curve>::from_encoded_point(&q_encoded).unwrap(); + + let sig = match Signature::from_der(sig) { + Ok(s) => s, + Err(_) if !pass => return None, + Err(_) => return Some("failed to parse signature ASN.1"), + }; + + match verifying_key.verify(msg, &sig) { + Ok(_) if pass => None, + Ok(_) => Some("signature verify unexpectedly succeeded"), + Err(_) if !pass => None, + Err(_) => Some("signature verify failed"), + } + } + + let data = include_bytes!(concat!("test_vectors/data/", $test_name, ".blb")); + + for (i, row) in Blob5Iterator::new(data).unwrap().enumerate() { + let [wx, wy, msg, sig, status] = row.unwrap(); + let pass = match status[0] { + 0 => false, + 1 => true, + _ => panic!("invalid value for pass flag"), + }; + if let Some(desc) = run_test(wx, wy, msg, sig, pass) { + panic!( + "\n\ + Failed test โ{}: {}\n\ + wx:\t{:?}\n\ + wy:\t{:?}\n\ + msg:\t{:?}\n\ + sig:\t{:?}\n\ + pass:\t{}\n", + i, desc, wx, wy, msg, sig, pass, + ); + } + } + } + }; +} diff --git a/vendor/ecdsa/src/hazmat.rs b/vendor/ecdsa/src/hazmat.rs new file mode 100644 index 000000000..3ca3fc4ce --- /dev/null +++ b/vendor/ecdsa/src/hazmat.rs @@ -0,0 +1,261 @@ +//! Low-level ECDSA primitives. +//! +//! # โ ๏ธ Warning: Hazmat! +//! +//! YOU PROBABLY DON'T WANT TO USE THESE! +//! +//! These primitives are easy-to-misuse low-level interfaces. +//! +//! If you are an end user / non-expert in cryptography, do not use these! +//! Failure to use them correctly can lead to catastrophic failures including +//! FULL PRIVATE KEY RECOVERY! + +#[cfg(feature = "arithmetic")] +use { + crate::{RecoveryId, SignatureSize}, + core::borrow::Borrow, + elliptic_curve::{ + group::Curve as _, + ops::{Invert, LinearCombination, Reduce}, + subtle::CtOption, + AffineArithmetic, AffineXCoordinate, Field, Group, ProjectiveArithmetic, ProjectivePoint, + Scalar, ScalarArithmetic, + }, +}; + +#[cfg(feature = "digest")] +use { + core::cmp, + elliptic_curve::{bigint::Encoding, FieldSize}, + signature::{digest::Digest, PrehashSignature}, +}; + +#[cfg(any(feature = "arithmetic", feature = "digest"))] +use crate::{ + elliptic_curve::{generic_array::ArrayLength, FieldBytes, PrimeCurve}, + Error, Result, Signature, +}; + +#[cfg(all(feature = "arithmetic", feature = "digest"))] +use signature::digest::FixedOutput; + +#[cfg(all(feature = "rfc6979"))] +use { + elliptic_curve::ScalarCore, + signature::digest::{core_api::BlockSizeUser, FixedOutputReset}, +}; + +/// Try to sign the given prehashed message using ECDSA. +/// +/// This trait is intended to be implemented on a type with access to the +/// secret scalar via `&self`, such as particular curve's `Scalar` type. +#[cfg(feature = "arithmetic")] +#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] +pub trait SignPrimitive<C>: Field + Into<FieldBytes<C>> + Reduce<C::UInt> + Sized +where + C: PrimeCurve + ProjectiveArithmetic + ScalarArithmetic<Scalar = Self>, + SignatureSize<C>: ArrayLength<u8>, +{ + /// Try to sign the prehashed message. + /// + /// Accepts the following arguments: + /// + /// - `k`: ephemeral scalar value. MUST BE UNIFORMLY RANDOM!!! + /// - `z`: message digest to be signed. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY + /// SECURE DIGEST ALGORITHM!!! + /// + /// # Returns + /// + /// ECDSA [`Signature`] and, when possible/desired, a [`RecoveryId`] + /// which can be used to recover the verifying key for a given signature. + #[allow(non_snake_case)] + fn try_sign_prehashed<K>( + &self, + k: K, + z: FieldBytes<C>, + ) -> Result<(Signature<C>, Option<RecoveryId>)> + where + K: Borrow<Self> + Invert<Output = CtOption<Self>>, + { + if k.borrow().is_zero().into() { + return Err(Error::new()); + } + + let z = Self::from_be_bytes_reduced(z); + + // Compute scalar inversion of ๐ + let k_inv = Option::<Scalar<C>>::from(k.invert()).ok_or_else(Error::new)?; + + // Compute ๐น = ๐ร๐ฎ + let R = (C::ProjectivePoint::generator() * k.borrow()).to_affine(); + + // Lift x-coordinate of ๐น (element of base field) into a serialized big + // integer, then reduce it into an element of the scalar field + let r = Self::from_be_bytes_reduced(R.x()); + + // Compute ๐ as a signature over ๐ and ๐. + let s = k_inv * (z + (r * self)); + + if s.is_zero().into() { + return Err(Error::new()); + } + + // TODO(tarcieri): support for computing recovery ID + Ok((Signature::from_scalars(r, s)?, None)) + } + + /// Try to sign the given message digest deterministically using the method + /// described in [RFC6979] for computing ECDSA ephemeral scalar `k`. + /// + /// Accepts the following parameters: + /// - `z`: message digest to be signed. + /// - `ad`: optional additional data, e.g. added entropy from an RNG + /// + /// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979 + #[cfg(all(feature = "rfc6979"))] + #[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))] + fn try_sign_prehashed_rfc6979<D>( + &self, + z: FieldBytes<C>, + ad: &[u8], + ) -> Result<(Signature<C>, Option<RecoveryId>)> + where + Self: From<ScalarCore<C>>, + C::UInt: for<'a> From<&'a Self>, + D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset, + { + let x = C::UInt::from(self); + let k = rfc6979::generate_k::<D, C::UInt>(&x, &C::ORDER, &z, ad); + let k = Self::from(ScalarCore::<C>::new(*k).unwrap()); + self.try_sign_prehashed(k, z) + } + + /// Try to sign the given digest instance using the method described in + /// [RFC6979]. + /// + /// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979 + #[cfg(all(feature = "rfc6979"))] + #[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))] + fn try_sign_digest_rfc6979<D>( + &self, + msg_digest: D, + ad: &[u8], + ) -> Result<(Signature<C>, Option<RecoveryId>)> + where + Self: From<ScalarCore<C>>, + C::UInt: for<'a> From<&'a Self>, + D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset, + { + self.try_sign_prehashed_rfc6979::<D>(msg_digest.finalize_fixed(), ad) + } +} + +/// Verify the given prehashed message using ECDSA. +/// +/// This trait is intended to be implemented on type which can access +/// the affine point represeting the public key via `&self`, such as a +/// particular curve's `AffinePoint` type. +#[cfg(feature = "arithmetic")] +#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] +pub trait VerifyPrimitive<C>: AffineXCoordinate<C> + Copy + Sized +where + C: PrimeCurve + AffineArithmetic<AffinePoint = Self> + ProjectiveArithmetic, + Scalar<C>: Reduce<C::UInt>, + SignatureSize<C>: ArrayLength<u8>, +{ + /// Verify the prehashed message against the provided signature + /// + /// Accepts the following arguments: + /// + /// - `z`: message digest to be verified. MUST BE OUTPUT OF A + /// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!! + /// - `sig`: signature to be verified against the key and message + fn verify_prehashed(&self, z: FieldBytes<C>, sig: &Signature<C>) -> Result<()> { + let z = Scalar::<C>::from_be_bytes_reduced(z); + let (r, s) = sig.split_scalars(); + let s_inv = *s.invert(); + let u1 = z * s_inv; + let u2 = *r * s_inv; + let x = ProjectivePoint::<C>::lincomb( + &ProjectivePoint::<C>::generator(), + &u1, + &ProjectivePoint::<C>::from(*self), + &u2, + ) + .to_affine() + .x(); + + if Scalar::<C>::from_be_bytes_reduced(x) == *r { + Ok(()) + } else { + Err(Error::new()) + } + } + + /// Verify message digest against the provided signature. + #[cfg(feature = "digest")] + #[cfg_attr(docsrs, doc(cfg(feature = "digest")))] + fn verify_digest<D>(&self, msg_digest: D, sig: &Signature<C>) -> Result<()> + where + D: FixedOutput<OutputSize = FieldSize<C>>, + { + self.verify_prehashed(msg_digest.finalize_fixed(), sig) + } +} + +/// Bind a preferred [`Digest`] algorithm to an elliptic curve type. +/// +/// Generally there is a preferred variety of the SHA-2 family used with ECDSA +/// for a particular elliptic curve. +/// +/// This trait can be used to specify it, and with it receive a blanket impl of +/// [`PrehashSignature`], used by [`signature_derive`][1]) for the [`Signature`] +/// type for a particular elliptic curve. +/// +/// [1]: https://github.com/RustCrypto/traits/tree/master/signature/derive +#[cfg(feature = "digest")] +#[cfg_attr(docsrs, doc(cfg(feature = "digest")))] +pub trait DigestPrimitive: PrimeCurve { + /// Preferred digest to use when computing ECDSA signatures for this + /// elliptic curve. This is typically a member of the SHA-2 family. + // TODO(tarcieri): add BlockSizeUser + FixedOutput(Reset) bounds in next breaking release + // These bounds ensure the digest algorithm can be used for HMAC-DRBG for RFC6979 + type Digest: Digest; + + /// Compute field bytes for a prehash (message digest), either zero-padding + /// or truncating if the prehash size does not match the field size. + fn prehash_to_field_bytes(prehash: &[u8]) -> Result<FieldBytes<Self>> { + // Minimum allowed prehash size is half the field size + if prehash.len() < Self::UInt::BYTE_SIZE / 2 { + return Err(Error::new()); + } + + let mut field_bytes = FieldBytes::<Self>::default(); + + // This is a operation according to RFC6979 Section 2.3.2. and SEC1 Section 2.3.8. + // https://datatracker.ietf.org/doc/html/rfc6979#section-2.3.2 + // https://www.secg.org/sec1-v2.pdf + match prehash.len().cmp(&Self::UInt::BYTE_SIZE) { + cmp::Ordering::Equal => field_bytes.copy_from_slice(prehash), + cmp::Ordering::Less => { + // If prehash is smaller than the field size, pad with zeroes on the left + field_bytes[(Self::UInt::BYTE_SIZE - prehash.len())..].copy_from_slice(prehash); + } + cmp::Ordering::Greater => { + // If prehash is larger than the field size, truncate + field_bytes.copy_from_slice(&prehash[..Self::UInt::BYTE_SIZE]); + } + } + + Ok(field_bytes) + } +} + +#[cfg(feature = "digest")] +impl<C> PrehashSignature for Signature<C> +where + C: DigestPrimitive, + <FieldSize<C> as core::ops::Add>::Output: ArrayLength<u8>, +{ + type Digest = C::Digest; +} diff --git a/vendor/ecdsa/src/lib.rs b/vendor/ecdsa/src/lib.rs new file mode 100644 index 000000000..9e339ba8f --- /dev/null +++ b/vendor/ecdsa/src/lib.rs @@ -0,0 +1,421 @@ +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc = include_str!("../README.md")] +#![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" +)] +#![forbid(unsafe_code)] +#![warn( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_sign_loss, + clippy::checked_conversions, + clippy::implicit_saturating_sub, + clippy::panic, + clippy::panic_in_result_fn, + clippy::unwrap_used, + missing_docs, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] + +//! ## `serde` support +//! +//! When the `serde` feature of this crate is enabled, `Serialize` and +//! `Deserialize` impls are provided for the [`Signature`] and [`VerifyingKey`] +//! types. +//! +//! Please see type-specific documentation for more information. +//! +//! ## Interop +//! +//! Any crates which provide an implementation of ECDSA for a particular +//! elliptic curve can leverage the types from this crate, along with the +//! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in +//! a generic, interoperable way by leveraging the [`Signature`] type with in +//! conjunction with the [`signature::Signer`] and [`signature::Verifier`] +//! traits. +//! +//! For example, the [`ring-compat`] crate implements the [`signature::Signer`] +//! and [`signature::Verifier`] traits in conjunction with the +//! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to +//! wrap the ECDSA implementations from [*ring*] in a generic, interoperable +//! API. +//! +//! [`k256`]: https://docs.rs/k256 +//! [`p256`]: https://docs.rs/p256 +//! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html +//! [`p384`]: https://docs.rs/p384 +//! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html +//! [`ring-compat`]: https://docs.rs/ring-compat +//! [*ring*]: https://docs.rs/ring + +#[cfg(feature = "alloc")] +extern crate alloc; + +mod recovery; + +#[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")] +mod sign; + +#[cfg(feature = "verify")] +mod verify; + +pub use crate::recovery::RecoveryId; + +// Re-export the `elliptic-curve` crate (and select types) +pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve}; + +// Re-export the `signature` crate (and select types) +pub use signature::{self, Error, Result}; + +#[cfg(feature = "sign")] +#[cfg_attr(docsrs, doc(cfg(feature = "sign")))] +pub use crate::sign::SigningKey; + +#[cfg(feature = "verify")] +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +pub use crate::verify::VerifyingKey; + +use core::{ + fmt::{self, Debug}, + ops::Add, +}; +use elliptic_curve::{ + bigint::Encoding as _, + generic_array::{sequence::Concat, ArrayLength, GenericArray}, + FieldBytes, FieldSize, ScalarCore, +}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +#[cfg(feature = "arithmetic")] +use { + core::str, + elliptic_curve::{ff::PrimeField, IsHigh, NonZeroScalar, ScalarArithmetic}, +}; + +#[cfg(feature = "serde")] +use serdect::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; + +/// Fixed-size byte array containing an ECDSA signature +pub type SignatureBytes<C> = GenericArray<u8, SignatureSize<C>>; + +/// ECDSA signature (fixed-size). Generic over elliptic curve types. +/// +/// Serialized as fixed-sized big endian scalar values with no added framing: +/// +/// - `r`: field element size for the given curve, big-endian +/// - `s`: field element size for the given curve, big-endian +/// +/// For example, in a curve with a 256-bit modulus like NIST P-256 or +/// secp256k1, `r` and `s` will both be 32-bytes, resulting in a signature +/// with a total of 64-bytes. +/// +/// ASN.1 DER-encoded signatures also supported via the +/// [`Signature::from_der`] and [`Signature::to_der`] methods. +/// +/// # `serde` support +/// +/// When the `serde` feature of this crate is enabled, it provides support for +/// serializing and deserializing ECDSA signatures using the `Serialize` and +/// `Deserialize` traits. +/// +/// The serialization uses a 64-byte fixed encoding when used with binary +/// formats, and a hexadecimal encoding when used with text formats. +#[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>, +{ + /// Parse a signature from ASN.1 DER + #[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) + } + + /// Create a [`Signature`] from the serialized `r` and `s` scalar values + /// which comprise the signature. + 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()) + } + + /// Split the signature into its `r` and `s` components, represented as bytes. + 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), + ) + } + + /// Serialize this signature as ASN.1 DER + #[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") + } + + /// Convert this signature into a byte vector. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + pub fn to_vec(&self) -> Vec<u8> { + self.bytes.to_vec() + } +} + +#[cfg(feature = "arithmetic")] +#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] +impl<C> Signature<C> +where + C: PrimeCurve + ScalarArithmetic, + SignatureSize<C>: ArrayLength<u8>, +{ + /// Get the `r` component of this signature + pub fn r(&self) -> NonZeroScalar<C> { + NonZeroScalar::try_from(self.split_bytes().0.as_slice()) + .expect("r-component ensured valid in constructor") + } + + /// Get the `s` component of this signature + pub fn s(&self) -> NonZeroScalar<C> { + NonZeroScalar::try_from(self.split_bytes().1.as_slice()) + .expect("s-component ensured valid in constructor") + } + + /// Split the signature into its `r` and `s` scalars. + pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) { + (self.r(), self.s()) + } + + /// Normalize signature into "low S" form as described in + /// [BIP 0062: Dealing with Malleability][1]. + /// + /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki + pub fn normalize_s(&self) -> Option<Self> { + let s = self.s(); + + if s.is_high().into() { + let neg_s = -s; + let mut result = self.clone(); + result.bytes[C::UInt::BYTE_SIZE..].copy_from_slice(&neg_s.to_repr()); + Some(result) + } else { + None + } + } +} + +impl<C> signature::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())?; + + for &byte in self.as_ref() { + write!(f, "{:02X}", byte)?; + } + + write!(f, ")") + } +} + +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), + }) + } +} + +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()); + } + + // This check is mainly to ensure `hex.split_at` below won't panic + 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, + SignatureSize<C>: ArrayLength<u8>, +{ + fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serdect::array::serialize_hex_upper_or_bin(&self.bytes, serializer) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl<'de, C> Deserialize<'de> for Signature<C> +where + C: PrimeCurve, + SignatureSize<C>: ArrayLength<u8>, +{ + fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + let mut bytes = SignatureBytes::<C>::default(); + serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?; + Self::try_from(bytes.as_slice()).map_err(de::Error::custom) + } +} diff --git a/vendor/ecdsa/src/recovery.rs b/vendor/ecdsa/src/recovery.rs new file mode 100644 index 000000000..c923bbad7 --- /dev/null +++ b/vendor/ecdsa/src/recovery.rs @@ -0,0 +1,110 @@ +//! Public key recovery support. + +use crate::{Error, Result}; + +/// Recovery IDs, a.k.a. "recid". +/// +/// This is an integer value `0`, `1`, `2`, or `3` included along with a +/// signature which is used during the recovery process to select the correct +/// public key from the signature. +/// +/// It consists of two bits of information: +/// +/// - low bit (0/1): was the y-coordinate of the affine point resulting from +/// the fixed-base multiplication ๐ร๐ฎ odd? This part of the algorithm +/// functions similar to point decompression. +/// - hi bit (3/4): did the affine x-coordinate of ๐ร๐ฎ overflow the order of +/// the scalar field, requiring a reduction when computing `r`? +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct RecoveryId(u8); + +impl RecoveryId { + /// Maximum supported value for the recovery ID (inclusive). + pub const MAX: u8 = 3; + + /// Create a new [`RecoveryId`] from the following 1-bit arguments: + /// + /// - `is_y_odd`: is the affine y-coordinate of ๐ร๐ฎ odd? + /// - `is_x_reduced`: did the affine x-coordinate of ๐ร๐ฎ overflow the curve order? + pub const fn new(is_y_odd: bool, is_x_reduced: bool) -> Self { + Self((is_x_reduced as u8) << 1 | (is_y_odd as u8)) + } + + /// Did the affine x-coordinate of ๐ร๐ฎ overflow the curve order? + pub const fn is_x_reduced(self) -> bool { + (self.0 & 0b10) != 0 + } + + /// Is the affine y-coordinate of ๐ร๐ฎ odd? + pub const fn is_y_odd(self) -> bool { + (self.0 & 1) != 0 + } + + /// Convert a `u8` into a [`RecoveryId`]. + pub const fn from_byte(byte: u8) -> Option<Self> { + if byte <= Self::MAX { + Some(Self(byte)) + } else { + None + } + } + + /// Convert this [`RecoveryId`] into a `u8`. + pub const fn to_byte(self) -> u8 { + self.0 + } +} + +impl TryFrom<u8> for RecoveryId { + type Error = Error; + + fn try_from(byte: u8) -> Result<Self> { + Self::from_byte(byte).ok_or_else(Error::new) + } +} + +impl From<RecoveryId> for u8 { + fn from(id: RecoveryId) -> u8 { + id.0 + } +} + +#[cfg(test)] +mod tests { + use super::RecoveryId; + + #[test] + fn new() { + assert_eq!(RecoveryId::new(false, false).to_byte(), 0); + assert_eq!(RecoveryId::new(true, false).to_byte(), 1); + assert_eq!(RecoveryId::new(false, true).to_byte(), 2); + assert_eq!(RecoveryId::new(true, true).to_byte(), 3); + } + + #[test] + fn try_from() { + for n in 0u8..=3 { + assert_eq!(RecoveryId::try_from(n).unwrap().to_byte(), n); + } + + for n in 4u8..=255 { + assert!(RecoveryId::try_from(n).is_err()); + } + } + + #[test] + fn is_x_reduced() { + assert_eq!(RecoveryId::try_from(0).unwrap().is_x_reduced(), false); + assert_eq!(RecoveryId::try_from(1).unwrap().is_x_reduced(), false); + assert_eq!(RecoveryId::try_from(2).unwrap().is_x_reduced(), true); + assert_eq!(RecoveryId::try_from(3).unwrap().is_x_reduced(), true); + } + + #[test] + fn is_y_odd() { + assert_eq!(RecoveryId::try_from(0).unwrap().is_y_odd(), false); + assert_eq!(RecoveryId::try_from(1).unwrap().is_y_odd(), true); + assert_eq!(RecoveryId::try_from(2).unwrap().is_y_odd(), false); + assert_eq!(RecoveryId::try_from(3).unwrap().is_y_odd(), true); + } +} diff --git a/vendor/ecdsa/src/sign.rs b/vendor/ecdsa/src/sign.rs new file mode 100644 index 000000000..12d00edb2 --- /dev/null +++ b/vendor/ecdsa/src/sign.rs @@ -0,0 +1,424 @@ +//! ECDSA signing key. + +// TODO(tarcieri): support for hardware crypto accelerators + +use crate::{ + hazmat::{DigestPrimitive, SignPrimitive}, + Error, Result, Signature, SignatureSize, +}; +use core::fmt::{self, Debug}; +use elliptic_curve::{ + generic_array::ArrayLength, + group::ff::PrimeField, + ops::{Invert, Reduce}, + subtle::{Choice, ConstantTimeEq, CtOption}, + zeroize::{Zeroize, ZeroizeOnDrop}, + FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar, SecretKey, +}; +use signature::{ + digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset}, + hazmat::PrehashSigner, + rand_core::{CryptoRng, RngCore}, + DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer, +}; + +#[cfg(feature = "pem")] +use { + crate::elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument}, + core::str::FromStr, +}; + +#[cfg(feature = "pkcs8")] +use crate::elliptic_curve::{ + pkcs8::{self, AssociatedOid, DecodePrivateKey}, + sec1::{self, FromEncodedPoint, ToEncodedPoint}, + AffinePoint, +}; + +#[cfg(feature = "verify")] +use {crate::verify::VerifyingKey, elliptic_curve::PublicKey, signature::Keypair}; + +/// ECDSA signing key. Generic over elliptic curves. +/// +/// Requires an [`elliptic_curve::ProjectiveArithmetic`] impl on the curve, and a +/// [`SignPrimitive`] impl on its associated `Scalar` type. +#[derive(Clone)] +#[cfg_attr(docsrs, doc(cfg(feature = "sign")))] +pub struct SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + /// ECDSA signing keys are non-zero elements of a given curve's scalar field. + secret_scalar: NonZeroScalar<C>, + + /// Verifying key which corresponds to this signing key. + #[cfg(feature = "verify")] + verifying_key: VerifyingKey<C>, +} + +impl<C> SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + /// Generate a cryptographically random [`SigningKey`]. + pub fn random(rng: impl CryptoRng + RngCore) -> Self { + NonZeroScalar::<C>::random(rng).into() + } + + /// Initialize signing key from a raw scalar serialized as a byte slice. + pub fn from_bytes(bytes: &[u8]) -> Result<Self> { + SecretKey::<C>::from_be_bytes(bytes) + .map(|sk| sk.to_nonzero_scalar().into()) + .map_err(|_| Error::new()) + } + + /// Serialize this [`SigningKey`] as bytes + pub fn to_bytes(&self) -> FieldBytes<C> { + self.secret_scalar.to_repr() + } + + /// Borrow the secret [`NonZeroScalar`] value for this key. + /// + /// # โ ๏ธ Warning + /// + /// This value is key material. + /// + /// Please treat it with the care it deserves! + pub fn as_nonzero_scalar(&self) -> &NonZeroScalar<C> { + &self.secret_scalar + } + + /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`] + // TODO(tarcieri): make this return `&VerifyingKey<C>` in the next breaking release + #[cfg(feature = "verify")] + #[cfg_attr(docsrs, doc(cfg(feature = "verify")))] + pub fn verifying_key(&self) -> VerifyingKey<C> { + self.verifying_key + } +} + +#[cfg(feature = "verify")] +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +impl<C> AsRef<VerifyingKey<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn as_ref(&self) -> &VerifyingKey<C> { + &self.verifying_key + } +} + +impl<C> ConstantTimeEq for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.secret_scalar.ct_eq(&other.secret_scalar) + } +} + +impl<C> Debug for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SigningKey").finish_non_exhaustive() + } +} + +impl<C> Drop for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn drop(&mut self) { + self.secret_scalar.zeroize(); + } +} + +impl<C> ZeroizeOnDrop for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ +} + +/// Constant-time comparison +impl<C> Eq for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ +} + +/// Constant-time comparison +impl<C> PartialEq for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn eq(&self, other: &SigningKey<C>) -> bool { + self.ct_eq(other).into() + } +} + +impl<C> From<SecretKey<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn from(secret_key: SecretKey<C>) -> Self { + Self::from(&secret_key) + } +} + +impl<C> From<&SecretKey<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn from(secret_key: &SecretKey<C>) -> Self { + secret_key.to_nonzero_scalar().into() + } +} + +impl<C> From<SigningKey<C>> for SecretKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn from(key: SigningKey<C>) -> Self { + key.secret_scalar.into() + } +} + +impl<C> From<&SigningKey<C>> for SecretKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn from(secret_key: &SigningKey<C>) -> Self { + secret_key.secret_scalar.into() + } +} + +impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + C::UInt: for<'a> From<&'a Scalar<C>>, + D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + + SignatureSize<C>: ArrayLength<u8>, +{ + /// Sign message digest using a deterministic ephemeral scalar (`k`) + /// computed using the algorithm described in [RFC6979 ยง 3.2]. + /// + /// [RFC6979 ยง 3.2]: https://tools.ietf.org/html/rfc6979#section-3 + fn try_sign_digest(&self, msg_digest: D) -> Result<Signature<C>> { + Ok(self + .secret_scalar + .try_sign_digest_rfc6979::<D>(msg_digest, &[])? + .0) + } +} + +#[cfg(feature = "verify")] +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +impl<C> Keypair<Signature<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + type VerifyingKey = VerifyingKey<C>; +} + +impl<C> PrehashSigner<Signature<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, + C::Digest: BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset, + C::UInt: for<'a> From<&'a Scalar<C>>, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature<C>> { + let prehash = C::prehash_to_field_bytes(prehash)?; + + Ok(self + .secret_scalar + .try_sign_prehashed_rfc6979::<C::Digest>(prehash, &[])? + .0) + } +} + +impl<C> Signer<Signature<C>> for SigningKey<C> +where + Self: DigestSigner<C::Digest, Signature<C>>, + C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> { + self.try_sign_digest(C::Digest::new_with_prefix(msg)) + } +} + +impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + C::UInt: for<'a> From<&'a Scalar<C>>, + D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + /// Sign message prehash using an ephemeral scalar (`k`) derived according + /// to a variant of RFC 6979 (Section 3.6) which supplies additional + /// entropy from an RNG. + fn try_sign_digest_with_rng( + &self, + mut rng: impl CryptoRng + RngCore, + msg_digest: D, + ) -> Result<Signature<C>> { + let mut ad = FieldBytes::<C>::default(); + rng.fill_bytes(&mut ad); + Ok(self + .secret_scalar + .try_sign_digest_rfc6979::<D>(msg_digest, &ad)? + .0) + } +} + +impl<C> RandomizedSigner<Signature<C>> for SigningKey<C> +where + Self: RandomizedDigestSigner<C::Digest, Signature<C>>, + C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn try_sign_with_rng(&self, rng: impl CryptoRng + RngCore, msg: &[u8]) -> Result<Signature<C>> { + self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg)) + } +} + +impl<C> From<NonZeroScalar<C>> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn from(secret_scalar: NonZeroScalar<C>) -> Self { + #[cfg(feature = "verify")] + let public_key = PublicKey::from_secret_scalar(&secret_scalar); + + Self { + secret_scalar, + #[cfg(feature = "verify")] + verifying_key: public_key.into(), + } + } +} + +impl<C> TryFrom<&[u8]> for SigningKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + type Error = Error; + + fn try_from(bytes: &[u8]) -> Result<Self> { + Self::from_bytes(bytes) + } +} + +#[cfg(feature = "verify")] +impl<C> From<&SigningKey<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn from(signing_key: &SigningKey<C>) -> VerifyingKey<C> { + signing_key.verifying_key() + } +} + +#[cfg(feature = "pkcs8")] +#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] +impl<C> TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + type Error = pkcs8::Error; + + fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> { + SecretKey::try_from(private_key_info).map(Into::into) + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl<C> EncodePrivateKey for SigningKey<C> +where + C: AssociatedOid + PrimeCurve + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> { + SecretKey::from(self.secret_scalar).to_pkcs8_der() + } +} + +#[cfg(feature = "pkcs8")] +#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] +impl<C> DecodePrivateKey for SigningKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl<C> FromStr for SigningKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, + Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>, + SignatureSize<C>: ArrayLength<u8>, +{ + type Err = Error; + + fn from_str(s: &str) -> Result<Self> { + Self::from_pkcs8_pem(s).map_err(|_| Error::new()) + } +} diff --git a/vendor/ecdsa/src/verify.rs b/vendor/ecdsa/src/verify.rs new file mode 100644 index 000000000..5aa8bcb8d --- /dev/null +++ b/vendor/ecdsa/src/verify.rs @@ -0,0 +1,325 @@ +//! ECDSA verification key. + +use crate::{ + hazmat::{DigestPrimitive, VerifyPrimitive}, + Error, Result, Signature, SignatureSize, +}; +use core::{cmp::Ordering, fmt::Debug}; +use elliptic_curve::{ + generic_array::ArrayLength, + ops::Reduce, + sec1::{self, EncodedPoint, FromEncodedPoint, ToEncodedPoint}, + AffinePoint, FieldSize, PointCompression, PrimeCurve, ProjectiveArithmetic, PublicKey, Scalar, +}; +use signature::{ + digest::{Digest, FixedOutput}, + hazmat::PrehashVerifier, + DigestVerifier, Verifier, +}; + +#[cfg(feature = "pkcs8")] +use elliptic_curve::pkcs8::{self, AssociatedOid, DecodePublicKey}; + +#[cfg(feature = "pem")] +use elliptic_curve::pkcs8::EncodePublicKey; + +#[cfg(feature = "pem")] +use core::str::FromStr; + +#[cfg(all(feature = "pem", feature = "serde"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "serde"))))] +use serdect::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 +/// [`VerifyPrimitive`] impl on its associated `AffinePoint` type. +/// +/// # `serde` support +/// +/// When the `serde` feature of this crate is enabled, it provides support for +/// serializing and deserializing ECDSA signatures using the `Serialize` and +/// `Deserialize` traits. +/// +/// The serialization leverages the encoding used by the [`PublicKey`] type, +/// which is a binary-oriented ASN.1 DER encoding. +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +#[derive(Clone, Debug)] +pub struct VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, +{ + pub(crate) inner: PublicKey<C>, +} + +impl<C> VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + /// Initialize [`VerifyingKey`] from a SEC1-encoded public key. + pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> { + PublicKey::from_sec1_bytes(bytes) + .map(|pk| Self { inner: pk }) + .map_err(|_| Error::new()) + } + + /// Initialize [`VerifyingKey`] from an affine point. + /// + /// Returns an [`Error`] if the given affine point is the additive identity + /// (a.k.a. point at infinity). + pub fn from_affine(affine: AffinePoint<C>) -> Result<Self> { + Ok(Self { + inner: PublicKey::from_affine(affine).map_err(|_| Error::new())?, + }) + } + + /// Initialize [`VerifyingKey`] from an [`EncodedPoint`]. + pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> { + Option::from(PublicKey::<C>::from_encoded_point(public_key)) + .map(|public_key| Self { inner: public_key }) + .ok_or_else(Error::new) + } + + /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally + /// applying point compression. + pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> { + self.inner.to_encoded_point(compress) + } + + /// Borrow the inner [`AffinePoint`] for this public key. + pub fn as_affine(&self) -> &AffinePoint<C> { + self.inner.as_affine() + } +} + +impl<C> AsRef<AffinePoint<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + fn as_ref(&self) -> &AffinePoint<C> { + self.as_affine() + } +} + +impl<C> Copy for VerifyingKey<C> where C: PrimeCurve + ProjectiveArithmetic {} + +impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + D: Digest + FixedOutput<OutputSize = FieldSize<C>>, + AffinePoint<C>: VerifyPrimitive<C>, + Scalar<C>: Reduce<C::UInt>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> { + self.inner.as_affine().verify_digest(msg_digest, signature) + } +} + +impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, + AffinePoint<C>: VerifyPrimitive<C>, + Scalar<C>: Reduce<C::UInt>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> { + let prehash = C::prehash_to_field_bytes(prehash)?; + self.inner.as_affine().verify_prehashed(prehash, signature) + } +} + +impl<C> Verifier<Signature<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, + C::Digest: FixedOutput<OutputSize = FieldSize<C>>, + AffinePoint<C>: VerifyPrimitive<C>, + Scalar<C>: Reduce<C::UInt>, + SignatureSize<C>: ArrayLength<u8>, +{ + fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> { + self.verify_digest(C::Digest::new_with_prefix(msg), signature) + } +} + +impl<C> From<&VerifyingKey<C>> for EncodedPoint<C> +where + C: PrimeCurve + ProjectiveArithmetic + PointCompression, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + fn from(verifying_key: &VerifyingKey<C>) -> EncodedPoint<C> { + verifying_key.to_encoded_point(C::COMPRESS_POINTS) + } +} + +impl<C> From<PublicKey<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, +{ + fn from(public_key: PublicKey<C>) -> VerifyingKey<C> { + VerifyingKey { inner: public_key } + } +} + +impl<C> From<&PublicKey<C>> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, +{ + fn from(public_key: &PublicKey<C>) -> VerifyingKey<C> { + (*public_key).into() + } +} + +impl<C> From<VerifyingKey<C>> for PublicKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, +{ + fn from(verifying_key: VerifyingKey<C>) -> PublicKey<C> { + verifying_key.inner + } +} + +impl<C> From<&VerifyingKey<C>> for PublicKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, +{ + fn from(verifying_key: &VerifyingKey<C>) -> PublicKey<C> { + (*verifying_key).into() + } +} + +impl<C> Eq for VerifyingKey<C> where C: PrimeCurve + ProjectiveArithmetic {} + +impl<C> PartialEq for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, +{ + fn eq(&self, other: &Self) -> bool { + self.inner.eq(&other.inner) + } +} + +impl<C> PartialOrd for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.inner.partial_cmp(&other.inner) + } +} + +impl<C> Ord for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.inner.cmp(&other.inner) + } +} + +impl<C> TryFrom<&[u8]> for VerifyingKey<C> +where + C: PrimeCurve + ProjectiveArithmetic, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + type Error = Error; + + fn try_from(bytes: &[u8]) -> Result<Self> { + Self::from_sec1_bytes(bytes) + } +} + +#[cfg(feature = "pkcs8")] +#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] +impl<C> TryFrom<pkcs8::SubjectPublicKeyInfo<'_>> for VerifyingKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + type Error = pkcs8::spki::Error; + + fn try_from(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result<Self> { + PublicKey::try_from(spki).map(|inner| Self { inner }) + } +} + +#[cfg(feature = "pkcs8")] +#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] +impl<C> DecodePublicKey for VerifyingKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl<C> EncodePublicKey for VerifyingKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> { + self.inner.to_public_key_der() + } +} + +#[cfg(feature = "pem")] +#[cfg_attr(docsrs, doc(cfg(feature = "pem")))] +impl<C> FromStr for VerifyingKey<C> +where + C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression, + AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, + FieldSize<C>: sec1::ModulusSize, +{ + type Err = Error; + + fn from_str(s: &str) -> Result<Self> { + Self::from_public_key_pem(s).map_err(|_| Error::new()) + } +} + +#[cfg(all(feature = "pem", feature = "serde"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "serde"))))] +impl<C> Serialize for VerifyingKey<C> +where + C: PrimeCurve + AssociatedOid + 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(cfg(all(feature = "pem", feature = "serde"))))] +impl<'de, C> Deserialize<'de> for VerifyingKey<C> +where + C: PrimeCurve + AssociatedOid + 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) + } +} |