From 3ed9867409ddb392ddc6787168ca32150a897161 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 18 Jan 2024 01:18:59 +0000 Subject: [PATCH] ecdsa: remove `SignPrimitive` and `VerifyPrimitive` traits (#793) The backstory of these traits was once upon a time we didn't yet have the trait structure in place to express algorithms like ECDSA signing and verification generically, so each crate (at the time just `k256` and `p256`) had a nearly duplicated implementation of ECDSA, with `k256` including tweaks for low-S normalization. Now the `ecdsa` crate contains fully generic implementations of both algorithms, and with the `EcdsaCurve` trait, carries a `NORMALIZE_S` preference, so these traits are just needless indirection at this point. This removes the traits, converting non-trivial methods into static functions in the `hazmat` module, namely `sign_prehashed_rfc6979`. --- ecdsa/src/dev.rs | 39 +++++---- ecdsa/src/hazmat.rs | 189 ++++++++++++++--------------------------- ecdsa/src/recovery.rs | 39 ++++----- ecdsa/src/signing.rs | 84 +++++++++--------- ecdsa/src/verifying.rs | 25 +++--- 5 files changed, 154 insertions(+), 222 deletions(-) diff --git a/ecdsa/src/dev.rs b/ecdsa/src/dev.rs index 00feec26..1317d8ba 100644 --- a/ecdsa/src/dev.rs +++ b/ecdsa/src/dev.rs @@ -43,14 +43,14 @@ macro_rules! new_signing_test { array::{typenum::Unsigned, Array}, bigint::Encoding, group::ff::PrimeField, - Curve, CurveArithmetic, Scalar, + Curve, CurveArithmetic, FieldBytes, Scalar, }, - hazmat::SignPrimitive, + hazmat::sign_prehashed, }; fn decode_scalar(bytes: &[u8]) -> Option> { if bytes.len() == <$curve as Curve>::FieldBytesSize::USIZE { - Scalar::<$curve>::from_repr(Array::clone_from_slice(bytes)).into() + Scalar::<$curve>::from_repr(bytes.try_into().unwrap()).into() } else { None } @@ -67,8 +67,10 @@ macro_rules! new_signing_test { vector.m.len(), "invalid vector.m (must be field-sized digest)" ); - let z = Array::clone_from_slice(vector.m); - let sig = d.try_sign_prehashed(k, &z).expect("ECDSA sign failed").0; + let z = FieldBytes::<$curve>::try_from(vector.m).unwrap(); + let sig = sign_prehashed::<$curve>(&d, &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()); @@ -88,8 +90,8 @@ macro_rules! new_verification_test { sec1::{EncodedPoint, FromEncodedPoint}, AffinePoint, CurveArithmetic, Scalar, }, - hazmat::VerifyPrimitive, - Signature, + signature::hazmat::PrehashVerifier, + Signature, VerifyingKey, }; #[test] @@ -101,16 +103,15 @@ macro_rules! new_verification_test { false, ); - let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap(); - let z = Array::clone_from_slice(vector.m); + let q = VerifyingKey::<$curve>::from_encoded_point(&q_encoded).unwrap(); let sig = Signature::from_scalars( - Array::clone_from_slice(vector.r), - Array::clone_from_slice(vector.s), + Array::try_from(vector.r).unwrap(), + Array::try_from(vector.s).unwrap(), ) .unwrap(); - let result = q.verify_prehashed(&z, &sig); + let result = q.verify_prehash(vector.m, &sig); assert!(result.is_ok()); } } @@ -124,17 +125,15 @@ macro_rules! new_verification_test { false, ); - let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap(); - let z = Array::clone_from_slice(vector.m); + let q = VerifyingKey::<$curve>::from_encoded_point(&q_encoded).unwrap(); + let r = Array::try_from(vector.r).unwrap(); // Flip a bit in `s` - let mut s_tweaked = Array::clone_from_slice(vector.s); + let mut s_tweaked = Array::try_from(vector.s).unwrap(); s_tweaked[0] ^= 1; - let sig = - Signature::from_scalars(Array::clone_from_slice(vector.r), s_tweaked).unwrap(); - - let result = q.verify_prehashed(&z, &sig); + let sig = Signature::from_scalars(r, s_tweaked).unwrap(); + let result = q.verify_prehash(vector.m, &sig); assert!(result.is_err()); } } @@ -169,7 +168,7 @@ macro_rules! new_wycheproof_test { for v in data.iter().take(offset) { assert_eq!(*v, 0, "EcdsaVerifier: point too large"); } - elliptic_curve::FieldBytes::::clone_from_slice(&data[offset..]) + elliptic_curve::FieldBytes::::try_from(&data[offset..]).unwrap() } else { // Provided slice is too short and needs to be padded with zeros // on the left. Build a combined exact iterator to do this. diff --git a/ecdsa/src/hazmat.rs b/ecdsa/src/hazmat.rs index d6e6a0c2..09e999b7 100644 --- a/ecdsa/src/hazmat.rs +++ b/ecdsa/src/hazmat.rs @@ -23,7 +23,6 @@ use { ops::{Invert, LinearCombination, MulByGenerator, Reduce}, point::AffineCoordinates, scalar::IsHigh, - subtle::CtOption, CurveArithmetic, ProjectivePoint, Scalar, }, }; @@ -38,120 +37,11 @@ use { }; #[cfg(feature = "rfc6979")] -use elliptic_curve::{FieldBytesEncoding, ScalarPrimitive}; +use elliptic_curve::FieldBytesEncoding; #[cfg(any(feature = "arithmetic", feature = "digest"))] use crate::{elliptic_curve::array::ArraySize, Signature}; -/// 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")] -pub trait SignPrimitive: - AsRef - + Into> - + IsHigh - + PrimeField> - + Reduce> - + Sized -where - C: EcdsaCurve + CurveArithmetic, - SignatureSize: ArraySize, -{ - /// 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. - fn try_sign_prehashed( - &self, - k: K, - z: &FieldBytes, - ) -> Result<(Signature, Option)> - where - K: AsRef + Invert>, - { - sign_prehashed(self, k, z).map(|(sig, recid)| (sig, (Some(recid)))) - } - - /// 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, i.e. `H(m)`. Does not have to be reduced in advance. - /// - `ad`: optional additional data, e.g. added entropy from an RNG - /// - /// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979 - #[cfg(feature = "rfc6979")] - fn try_sign_prehashed_rfc6979( - &self, - z: &FieldBytes, - ad: &[u8], - ) -> Result<(Signature, Option)> - where - Self: From> + Invert>, - D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, - { - // From RFC6979 ยง 2.4: - // - // H(m) is transformed into an integer modulo q using the bits2int - // transform and an extra modular reduction: - // - // h = bits2int(H(m)) mod q - let z2 = as Reduce>::reduce_bytes(z); - - let k = Scalar::::from_repr(rfc6979::generate_k::( - &self.to_repr(), - &C::ORDER.encode_field_bytes(), - &z2.to_repr(), - ad, - )) - .unwrap(); - - self.try_sign_prehashed::(k, z) - } -} - -/// 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")] -pub trait VerifyPrimitive: AffineCoordinates> + Copy + Sized -where - C: EcdsaCurve + CurveArithmetic, - SignatureSize: ArraySize, -{ - /// Verify the prehashed message against the provided ECDSA 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, sig: &Signature) -> Result<()> { - verify_prehashed(&ProjectivePoint::::from(*self), z, sig) - } - - /// Verify message digest against the provided signature. - #[cfg(feature = "digest")] - fn verify_digest(&self, msg_digest: D, sig: &Signature) -> Result<()> - where - D: FixedOutput, - { - self.verify_prehashed(&bits2field::(&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 @@ -220,34 +110,37 @@ pub fn bits2field(bits: &[u8]) -> Result> { /// - `z`: message digest to be signed. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY /// SECURE DIGEST ALGORITHM!!! /// +/// # Low-S Normalization +/// +/// This function will apply low-S normalization if `::NORMALIZE_S` is true. +/// /// # Returns /// -/// ECDSA [`Signature`] and, when possible/desired, a [`RecoveryId`] -/// which can be used to recover the verifying key for a given signature. +/// ECDSA [`Signature`] and a [`RecoveryId`] which can be used to recover the verifying key for a +/// given signature. #[cfg(feature = "arithmetic")] #[allow(non_snake_case)] -pub fn sign_prehashed( +pub fn sign_prehashed( d: &Scalar, - k: K, + k: &Scalar, z: &FieldBytes, ) -> Result<(Signature, RecoveryId)> where C: EcdsaCurve + CurveArithmetic, - K: AsRef> + Invert>>, SignatureSize: ArraySize, { // TODO(tarcieri): use `NonZeroScalar` for `k`. - if k.as_ref().is_zero().into() { + if k.is_zero().into() { return Err(Error::new()); } let z = as Reduce>::reduce_bytes(z); // Compute scalar inversion of ๐‘˜ - let k_inv = Option::>::from(k.invert()).ok_or_else(Error::new)?; + let k_inv = Option::>::from(Invert::invert(k)).ok_or_else(Error::new)?; // Compute ๐‘น = ๐‘˜ร—๐‘ฎ - let R = ProjectivePoint::::mul_by_generator(k.as_ref()).to_affine(); + let R = ProjectivePoint::::mul_by_generator(k).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 @@ -258,19 +151,69 @@ where let s = k_inv * (z + (r * d)); // NOTE: `Signature::from_scalars` checks that both `r` and `s` are non-zero. - let signature = Signature::from_scalars(r, s)?; - let recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced); + let mut signature = Signature::from_scalars(r, s)?; + let mut recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced); + + // Apply low-S normalization if the curve is configured for it + if C::NORMALIZE_S { + recovery_id.0 ^= s.is_high().unwrap_u8(); + signature = signature.normalize_s(); + } + Ok((signature, recovery_id)) } +/// Try to sign the given message digest deterministically using the method +/// described in [RFC6979] for computing ECDSA ephemeral scalar `k`. +/// +/// Accepts the following parameters: +/// - `d`: signing key. MUST BE UNIFORMLY RANDOM!!! +/// - `z`: message digest to be signed, i.e. `H(m)`. Does not have to be reduced in advance. +/// - `ad`: optional additional data, e.g. added entropy from an RNG +/// +/// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979 +#[cfg(feature = "rfc6979")] +pub fn sign_prehashed_rfc6979( + d: &Scalar, + z: &FieldBytes, + ad: &[u8], +) -> Result<(Signature, RecoveryId)> +where + C: EcdsaCurve + CurveArithmetic, + D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, + SignatureSize: ArraySize, +{ + // From RFC6979 ยง 2.4: + // + // H(m) is transformed into an integer modulo q using the bits2int + // transform and an extra modular reduction: + // + // h = bits2int(H(m)) mod q + let z2 = as Reduce>::reduce_bytes(z); + + let k = Scalar::::from_repr(rfc6979::generate_k::( + &d.to_repr(), + &C::ORDER.encode_field_bytes(), + &z2.to_repr(), + ad, + )) + .unwrap(); + + sign_prehashed(d, &k, z) +} + /// Verify the prehashed message against the provided ECDSA signature. /// /// Accepts the following arguments: /// /// - `q`: public key with which to verify the signature. -/// - `z`: message digest to be verified. MUST BE OUTPUT OF A -/// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!! +/// - `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. +/// +/// # Low-S Normalization +/// +/// This is a low-level function that does *NOT* apply the `EcdsaCurve::NORMALIZE_S` checks. #[cfg(feature = "arithmetic")] pub fn verify_prehashed( q: &ProjectivePoint, diff --git a/ecdsa/src/recovery.rs b/ecdsa/src/recovery.rs index 77f86ab7..639045a8 100644 --- a/ecdsa/src/recovery.rs +++ b/ecdsa/src/recovery.rs @@ -4,14 +4,14 @@ use crate::{Error, Result}; #[cfg(feature = "signing")] use { - crate::{hazmat::SignPrimitive, SigningKey}, + crate::{hazmat::sign_prehashed_rfc6979, SigningKey}, elliptic_curve::subtle::CtOption, signature::{hazmat::PrehashSigner, DigestSigner, Signer}, }; #[cfg(feature = "verifying")] use { - crate::{hazmat::VerifyPrimitive, VerifyingKey}, + crate::{hazmat::verify_prehashed, VerifyingKey}, elliptic_curve::{ bigint::CheckedAdd, ops::{LinearCombination, Reduce}, @@ -19,7 +19,6 @@ use { sec1::{self, FromEncodedPoint, ToEncodedPoint}, AffinePoint, FieldBytesEncoding, FieldBytesSize, Group, PrimeField, ProjectivePoint, }, - signature::hazmat::PrehashVerifier, }; #[cfg(any(feature = "signing", feature = "verifying"))] @@ -46,7 +45,7 @@ use { /// - 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); +pub struct RecoveryId(pub(crate) u8); impl RecoveryId { /// Maximum supported value for the recovery ID (inclusive). @@ -97,8 +96,7 @@ impl RecoveryId { ) -> Result where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - AffinePoint: - DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArraySize, { @@ -116,8 +114,7 @@ impl RecoveryId { where C: EcdsaCurve + CurveArithmetic, D: Digest, - AffinePoint: - DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArraySize, { @@ -134,8 +131,7 @@ impl RecoveryId { ) -> Result where C: EcdsaCurve + CurveArithmetic, - AffinePoint: - DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArraySize, { @@ -171,17 +167,13 @@ impl From for u8 { impl SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { /// Sign the given message prehash, returning a signature and recovery ID. pub fn sign_prehash_recoverable(&self, prehash: &[u8]) -> Result<(Signature, RecoveryId)> { let z = bits2field::(prehash)?; - let (sig, recid) = self - .as_nonzero_scalar() - .try_sign_prehashed_rfc6979::(&z, &[])?; - - Ok((sig, recid.ok_or_else(Error::new)?)) + sign_prehashed_rfc6979::(self.as_nonzero_scalar(), &z, &[]) } /// Sign the given message digest, returning a signature and recovery ID. @@ -204,7 +196,7 @@ impl DigestSigner, RecoveryId)> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, D: Digest, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign_digest(&self, msg_digest: D) -> Result<(Signature, RecoveryId)> { @@ -216,7 +208,7 @@ where impl PrehashSigner<(Signature, RecoveryId)> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn sign_prehash(&self, prehash: &[u8]) -> Result<(Signature, RecoveryId)> { @@ -228,7 +220,7 @@ where impl Signer<(Signature, RecoveryId)> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign(&self, msg: &[u8]) -> Result<(Signature, RecoveryId)> { @@ -240,8 +232,7 @@ where impl VerifyingKey where C: EcdsaCurve + CurveArithmetic, - AffinePoint: - DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + AffinePoint: DecompressPoint + FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, SignatureSize: ArraySize, { @@ -308,7 +299,11 @@ where let vk = Self::from_affine(pk.into())?; // Ensure signature verifies with the recovered key - vk.verify_prehash(prehash, signature)?; + verify_prehashed::( + &ProjectivePoint::::from(*vk.as_affine()), + &bits2field::(prehash)?, + signature, + )?; Ok(vk) } diff --git a/ecdsa/src/signing.rs b/ecdsa/src/signing.rs index 373dee36..bb40916a 100644 --- a/ecdsa/src/signing.rs +++ b/ecdsa/src/signing.rs @@ -2,7 +2,7 @@ use crate::{ ecdsa_oid_for_digest, - hazmat::{bits2field, DigestPrimitive, SignPrimitive}, + hazmat::{bits2field, sign_prehashed_rfc6979, DigestPrimitive}, EcdsaCurve, Error, Result, Signature, SignatureSize, SignatureWithOid, }; use core::fmt::{self, Debug}; @@ -66,7 +66,7 @@ use elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument}; pub struct SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { /// ECDSA signing keys are non-zero elements of a given curve's scalar field. @@ -80,7 +80,7 @@ where impl SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { /// Generate a cryptographically random [`SigningKey`]. @@ -137,7 +137,7 @@ impl DigestSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign_digest(&self, msg_digest: D) -> Result> { @@ -152,15 +152,12 @@ where impl PrehashSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { let z = bits2field::(prehash)?; - Ok(self - .secret_scalar - .try_sign_prehashed_rfc6979::(&z, &[])? - .0) + Ok(sign_prehashed_rfc6979::(self.secret_scalar.as_ref(), &z, &[])?.0) } } @@ -171,7 +168,7 @@ where impl Signer> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign(&self, msg: &[u8]) -> Result> { @@ -183,7 +180,7 @@ impl RandomizedDigestSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign_digest_with_rng( @@ -198,7 +195,7 @@ where impl RandomizedPrehashSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn sign_prehash_with_rng( @@ -209,10 +206,7 @@ where let z = bits2field::(prehash)?; let mut ad = FieldBytes::::default(); rng.fill_bytes(&mut ad); - Ok(self - .secret_scalar - .try_sign_prehashed_rfc6979::(&z, &ad)? - .0) + Ok(sign_prehashed_rfc6979::(self.secret_scalar.as_ref(), &z, &ad)?.0) } } @@ -220,7 +214,7 @@ impl RandomizedSigner> for SigningKey where Self: RandomizedDigestSigner>, C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign_with_rng(&self, rng: &mut impl CryptoRngCore, msg: &[u8]) -> Result> { @@ -232,7 +226,7 @@ impl DigestSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, D: AssociatedOid + Digest + FixedOutput, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign_digest(&self, msg_digest: D) -> Result> { @@ -246,7 +240,7 @@ impl Signer> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, C::Digest: AssociatedOid, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn try_sign(&self, msg: &[u8]) -> Result> { @@ -258,7 +252,7 @@ where impl PrehashSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -272,7 +266,7 @@ where impl Signer> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -287,7 +281,7 @@ impl RandomizedDigestSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -306,7 +300,7 @@ where impl RandomizedPrehashSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -325,7 +319,7 @@ where impl RandomizedSigner> for SigningKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -347,7 +341,7 @@ where impl AsRef> for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn as_ref(&self) -> &VerifyingKey { @@ -358,7 +352,7 @@ where impl ConstantTimeEq for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn ct_eq(&self, other: &Self) -> Choice { @@ -369,7 +363,7 @@ where impl Debug for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -380,7 +374,7 @@ where impl Drop for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn drop(&mut self) { @@ -392,14 +386,14 @@ where impl Eq for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { } impl PartialEq for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn eq(&self, other: &SigningKey) -> bool { @@ -410,7 +404,7 @@ where impl From> for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(secret_scalar: NonZeroScalar) -> Self { @@ -428,7 +422,7 @@ where impl From> for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(secret_key: SecretKey) -> Self { @@ -439,7 +433,7 @@ where impl From<&SecretKey> for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(secret_key: &SecretKey) -> Self { @@ -450,7 +444,7 @@ where impl From> for SecretKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(key: SigningKey) -> Self { @@ -461,7 +455,7 @@ where impl From<&SigningKey> for SecretKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(secret_key: &SigningKey) -> Self { @@ -472,7 +466,7 @@ where impl TryFrom<&[u8]> for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { type Error = Error; @@ -485,7 +479,7 @@ where impl ZeroizeOnDrop for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { } @@ -494,7 +488,7 @@ where impl From> for VerifyingKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(signing_key: SigningKey) -> VerifyingKey { @@ -506,7 +500,7 @@ where impl From<&SigningKey> for VerifyingKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn from(signing_key: &SigningKey) -> VerifyingKey { @@ -518,7 +512,7 @@ where impl KeypairRef for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { type VerifyingKey = VerifyingKey; @@ -528,7 +522,7 @@ where impl AssociatedAlgorithmIdentifier for SigningKey where C: EcdsaCurve + AssociatedOid + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { type Params = ObjectIdentifier; @@ -541,7 +535,7 @@ where impl SignatureAlgorithmIdentifier for SigningKey where C: EcdsaCurve + CurveArithmetic, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, Signature: AssociatedAlgorithmIdentifier>, { @@ -557,7 +551,7 @@ where C: EcdsaCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { type Error = pkcs8::Error; @@ -573,7 +567,7 @@ where C: EcdsaCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { fn to_pkcs8_der(&self) -> pkcs8::Result { @@ -587,7 +581,7 @@ where C: EcdsaCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, - Scalar: Invert>> + SignPrimitive, + Scalar: Invert>>, SignatureSize: ArraySize, { type Err = Error; diff --git a/ecdsa/src/verifying.rs b/ecdsa/src/verifying.rs index d919f090..2c1af09f 100644 --- a/ecdsa/src/verifying.rs +++ b/ecdsa/src/verifying.rs @@ -1,15 +1,16 @@ //! ECDSA verifying: checking signatures are authentic using a [`VerifyingKey`]. use crate::{ - hazmat::{bits2field, DigestPrimitive, VerifyPrimitive}, + hazmat::{self, bits2field, DigestPrimitive}, EcdsaCurve, Error, Result, Signature, SignatureSize, }; use core::{cmp::Ordering, fmt::Debug}; use elliptic_curve::{ array::ArraySize, point::PointCompression, + scalar::IsHigh, sec1::{self, CompressedPoint, EncodedPoint, FromEncodedPoint, ToEncodedPoint}, - AffinePoint, CurveArithmetic, FieldBytesSize, PublicKey, + AffinePoint, CurveArithmetic, FieldBytesSize, ProjectivePoint, PublicKey, }; use signature::{ digest::{Digest, FixedOutput}, @@ -146,30 +147,34 @@ impl DigestVerifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic, D: Digest + FixedOutput, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, { fn verify_digest(&self, msg_digest: D, signature: &Signature) -> Result<()> { - self.inner.as_affine().verify_digest(msg_digest, signature) + self.verify_prehash(&msg_digest.finalize(), signature) } } impl PrehashVerifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, { fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> Result<()> { - let field = bits2field::(prehash)?; - self.inner.as_affine().verify_prehashed(&field, signature) + if C::NORMALIZE_S && signature.s().is_high().into() { + return Err(Error::new()); + } + + hazmat::verify_prehashed::( + &ProjectivePoint::::from(*self.inner.as_affine()), + &bits2field::(prehash)?, + signature, + ) } } impl Verifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, { fn verify(&self, msg: &[u8], signature: &Signature) -> Result<()> { @@ -181,7 +186,6 @@ where impl Verifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, { fn verify(&self, msg: &[u8], sig: &SignatureWithOid) -> Result<()> { @@ -200,7 +204,6 @@ impl DigestVerifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic, D: Digest + FixedOutput, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -215,7 +218,6 @@ where impl PrehashVerifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize, @@ -230,7 +232,6 @@ where impl Verifier> for VerifyingKey where C: EcdsaCurve + CurveArithmetic + DigestPrimitive, - AffinePoint: VerifyPrimitive, SignatureSize: ArraySize, der::MaxSize: ArraySize, as Add>::Output: Add + ArraySize,