diff --git a/ecdsa/src/hazmat.rs b/ecdsa/src/hazmat.rs index daab5f1a..05e4da88 100644 --- a/ecdsa/src/hazmat.rs +++ b/ecdsa/src/hazmat.rs @@ -10,6 +10,10 @@ //! 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::{bigint::Encoding, FieldBytes, PrimeCurve}; + #[cfg(feature = "arithmetic")] use { crate::{RecoveryId, SignatureSize}, @@ -25,8 +29,7 @@ use { #[cfg(feature = "digest")] use { - core::cmp, - elliptic_curve::{bigint::Encoding, FieldSize}, + elliptic_curve::FieldSize, signature::{ digest::{core_api::BlockSizeUser, Digest, FixedOutputReset}, PrehashSignature, @@ -34,10 +37,7 @@ use { }; #[cfg(any(feature = "arithmetic", feature = "digest"))] -use crate::{ - elliptic_curve::{generic_array::ArrayLength, FieldBytes, PrimeCurve}, - Error, Result, Signature, -}; +use crate::{elliptic_curve::generic_array::ArrayLength, Signature}; #[cfg(all(feature = "arithmetic", feature = "digest"))] use signature::digest::FixedOutput; @@ -219,34 +219,6 @@ 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. type Digest: BlockSizeUser + Digest + FixedOutputReset; - - /// 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> { - // 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::::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")] @@ -257,3 +229,35 @@ where { 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(bits: &[u8]) -> Result> { + // Minimum allowed bits size is half the field size + if bits.len() < C::UInt::BYTE_SIZE / 2 { + return Err(Error::new()); + } + + let mut field_bytes = FieldBytes::::default(); + + match bits.len().cmp(&C::UInt::BYTE_SIZE) { + 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::UInt::BYTE_SIZE - 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::UInt::BYTE_SIZE]); + } + } + + Ok(field_bytes) +} diff --git a/ecdsa/src/sign.rs b/ecdsa/src/sign.rs index 6386b894..0b802729 100644 --- a/ecdsa/src/sign.rs +++ b/ecdsa/src/sign.rs @@ -3,7 +3,7 @@ // TODO(tarcieri): support for hardware crypto accelerators use crate::{ - hazmat::{DigestPrimitive, SignPrimitive}, + hazmat::{bits2field, DigestPrimitive, SignPrimitive}, Error, Result, Signature, SignatureSize, }; use core::fmt::{self, Debug}; @@ -137,11 +137,11 @@ where SignatureSize: ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { - let prehash = C::prehash_to_field_bytes(prehash)?; + let scalar = bits2field::(prehash)?; Ok(self .secret_scalar - .try_sign_prehashed_rfc6979::(prehash, &[])? + .try_sign_prehashed_rfc6979::(scalar, &[])? .0) } } diff --git a/ecdsa/src/verify.rs b/ecdsa/src/verify.rs index a3f4015e..bb3f435c 100644 --- a/ecdsa/src/verify.rs +++ b/ecdsa/src/verify.rs @@ -1,7 +1,7 @@ //! ECDSA verification key. use crate::{ - hazmat::{DigestPrimitive, VerifyPrimitive}, + hazmat::{bits2field, DigestPrimitive, VerifyPrimitive}, Error, Result, Signature, SignatureSize, }; use core::{cmp::Ordering, fmt::Debug}; @@ -122,8 +122,8 @@ where SignatureSize: ArrayLength, { fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> Result<()> { - let prehash = C::prehash_to_field_bytes(prehash)?; - self.inner.as_affine().verify_prehashed(prehash, signature) + let field = bits2field::(prehash)?; + self.inner.as_affine().verify_prehashed(field, signature) } }