diff options
Diffstat (limited to 'vendor/ecdsa/src/hazmat.rs')
-rw-r--r-- | vendor/ecdsa/src/hazmat.rs | 235 |
1 files changed, 132 insertions, 103 deletions
diff --git a/vendor/ecdsa/src/hazmat.rs b/vendor/ecdsa/src/hazmat.rs index 3ca3fc4ce..6e59f2e2e 100644 --- a/vendor/ecdsa/src/hazmat.rs +++ b/vendor/ecdsa/src/hazmat.rs @@ -10,50 +10,53 @@ //! Failure to use them correctly can lead to catastrophic failures including //! FULL PRIVATE KEY RECOVERY! +use crate::{Error, Result}; +use core::cmp; +use elliptic_curve::{generic_array::typenum::Unsigned, FieldBytes, PrimeCurve}; + #[cfg(feature = "arithmetic")] use { crate::{RecoveryId, SignatureSize}, - core::borrow::Borrow, elliptic_curve::{ - group::Curve as _, - ops::{Invert, LinearCombination, Reduce}, + ff::PrimeField, + group::{Curve as _, Group}, + ops::{Invert, LinearCombination, MulByGenerator, Reduce}, + point::AffineCoordinates, + scalar::IsHigh, subtle::CtOption, - AffineArithmetic, AffineXCoordinate, Field, Group, ProjectiveArithmetic, ProjectivePoint, - Scalar, ScalarArithmetic, + CurveArithmetic, ProjectivePoint, Scalar, }, }; #[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, + elliptic_curve::FieldBytesSize, + signature::{ + digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset}, + PrehashSignature, + }, }; -#[cfg(all(feature = "arithmetic", feature = "digest"))] -use signature::digest::FixedOutput; +#[cfg(feature = "rfc6979")] +use elliptic_curve::{FieldBytesEncoding, ScalarPrimitive}; -#[cfg(all(feature = "rfc6979"))] -use { - elliptic_curve::ScalarCore, - signature::digest::{core_api::BlockSizeUser, FixedOutputReset}, -}; +#[cfg(any(feature = "arithmetic", feature = "digest"))] +use crate::{elliptic_curve::generic_array::ArrayLength, 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")] -#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] -pub trait SignPrimitive<C>: Field + Into<FieldBytes<C>> + Reduce<C::UInt> + Sized +pub trait SignPrimitive<C>: + AsRef<Self> + + Into<FieldBytes<C>> + + IsHigh + + PrimeField<Repr = FieldBytes<C>> + + Reduce<C::Uint, Bytes = FieldBytes<C>> + + Sized where - C: PrimeCurve + ProjectiveArithmetic + ScalarArithmetic<Scalar = Self>, + C: PrimeCurve + CurveArithmetic + CurveArithmetic<Scalar = Self>, SignatureSize<C>: ArrayLength<u8>, { /// Try to sign the prehashed message. @@ -72,26 +75,27 @@ where fn try_sign_prehashed<K>( &self, k: K, - z: FieldBytes<C>, + z: &FieldBytes<C>, ) -> Result<(Signature<C>, Option<RecoveryId>)> where - K: Borrow<Self> + Invert<Output = CtOption<Self>>, + K: AsRef<Self> + Invert<Output = CtOption<Self>>, { - if k.borrow().is_zero().into() { + if k.as_ref().is_zero().into() { return Err(Error::new()); } - let z = Self::from_be_bytes_reduced(z); + let z = <Self as Reduce<C::Uint>>::reduce_bytes(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(); + let R = ProjectivePoint::<C>::mul_by_generator(k.as_ref()).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()); + let r = Self::reduce_bytes(&R.x()); + let x_is_reduced = r.to_repr() != R.x(); // Compute ๐ as a signature over ๐ and ๐. let s = k_inv * (z + (r * self)); @@ -100,8 +104,9 @@ where return Err(Error::new()); } - // TODO(tarcieri): support for computing recovery ID - Ok((Signature::from_scalars(r, s)?, None)) + let signature = Signature::from_scalars(r, s)?; + let recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced); + Ok((signature, Some(recovery_id))) } /// Try to sign the given message digest deterministically using the method @@ -113,40 +118,24 @@ where /// /// [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>, + 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, + Self: From<ScalarPrimitive<C>> + Invert<Output = CtOption<Self>>, + D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldBytesSize<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) - } + let k = Scalar::<C>::from_repr(rfc6979::generate_k::<D, _>( + &self.to_repr(), + &C::ORDER.encode_field_bytes(), + z, + ad, + )) + .unwrap(); - /// 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) + self.try_sign_prehashed::<Self>(k, z) } } @@ -156,11 +145,9 @@ where /// 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 +pub trait VerifyPrimitive<C>: AffineCoordinates<FieldRepr = FieldBytes<C>> + Copy + Sized where - C: PrimeCurve + AffineArithmetic<AffinePoint = Self> + ProjectiveArithmetic, - Scalar<C>: Reduce<C::UInt>, + C: PrimeCurve + CurveArithmetic<AffinePoint = Self> + CurveArithmetic, SignatureSize<C>: ArrayLength<u8>, { /// Verify the prehashed message against the provided signature @@ -170,10 +157,10 @@ where /// - `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); + fn verify_prehashed(&self, z: &FieldBytes<C>, sig: &Signature<C>) -> Result<()> { + let z = Scalar::<C>::reduce_bytes(z); let (r, s) = sig.split_scalars(); - let s_inv = *s.invert(); + let s_inv = *s.invert_vartime(); let u1 = z * s_inv; let u2 = *r * s_inv; let x = ProjectivePoint::<C>::lincomb( @@ -185,7 +172,7 @@ where .to_affine() .x(); - if Scalar::<C>::from_be_bytes_reduced(x) == *r { + if *r == Scalar::<C>::reduce_bytes(&x) { Ok(()) } else { Err(Error::new()) @@ -194,12 +181,11 @@ where /// 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>>, + D: FixedOutput<OutputSize = FieldBytesSize<C>>, { - self.verify_prehashed(msg_digest.finalize_fixed(), sig) + self.verify_prehashed(&msg_digest.finalize_fixed(), sig) } } @@ -214,48 +200,91 @@ where /// /// [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) - } + type Digest: BlockSizeUser + + Digest + + FixedOutput<OutputSize = FieldBytesSize<Self>> + + FixedOutputReset; } #[cfg(feature = "digest")] impl<C> PrehashSignature for Signature<C> where C: DigestPrimitive, - <FieldSize<C> as core::ops::Add>::Output: ArrayLength<u8>, + <FieldBytesSize<C> as core::ops::Add>::Output: ArrayLength<u8>, { type Digest = C::Digest; } + +/// Partial implementation of the `bits2int` function as defined in +/// [RFC6979 ยง 2.3.2] as well as [SEC1] ยง 2.3.8. +/// +/// This is used to convert a message digest whose size may be smaller or +/// larger than the size of the curve's scalar field into a serialized +/// (unreduced) field element. +/// +/// [RFC6979 ยง 2.3.2]: https://datatracker.ietf.org/doc/html/rfc6979#section-2.3.2 +/// [SEC1]: https://www.secg.org/sec1-v2.pdf +pub fn bits2field<C: PrimeCurve>(bits: &[u8]) -> Result<FieldBytes<C>> { + // Minimum allowed bits size is half the field size + if bits.len() < C::FieldBytesSize::USIZE / 2 { + return Err(Error::new()); + } + + let mut field_bytes = FieldBytes::<C>::default(); + + match bits.len().cmp(&C::FieldBytesSize::USIZE) { + cmp::Ordering::Equal => field_bytes.copy_from_slice(bits), + cmp::Ordering::Less => { + // If bits is smaller than the field size, pad with zeroes on the left + field_bytes[(C::FieldBytesSize::USIZE - bits.len())..].copy_from_slice(bits); + } + cmp::Ordering::Greater => { + // If bits is larger than the field size, truncate + field_bytes.copy_from_slice(&bits[..C::FieldBytesSize::USIZE]); + } + } + + Ok(field_bytes) +} + +#[cfg(test)] +mod tests { + use super::bits2field; + use elliptic_curve::dev::MockCurve; + use hex_literal::hex; + + #[test] + fn bits2field_too_small() { + assert!(bits2field::<MockCurve>(b"").is_err()); + } + + #[test] + fn bits2field_size_less() { + let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + let field_bytes = bits2field::<MockCurve>(&prehash).unwrap(); + assert_eq!( + field_bytes.as_slice(), + &hex!("00000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + ); + } + + #[test] + fn bits2field_size_eq() { + let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + let field_bytes = bits2field::<MockCurve>(&prehash).unwrap(); + assert_eq!(field_bytes.as_slice(), &prehash); + } + + #[test] + fn bits2field_size_greater() { + let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); + let field_bytes = bits2field::<MockCurve>(&prehash).unwrap(); + assert_eq!( + field_bytes.as_slice(), + &hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + ); + } +} |