Skip to content

Commit

Permalink
ecdsa: impl signature::hazmat::{PrehashSigner, PrehashVerifier}
Browse files Browse the repository at this point in the history
Adds "hazmat" trait impls to `SigningKey` and `VerifyingKey`
respectively which allow computing signatures over raw message digests.
See RustCrypto/traits#1099.

The implementation allows digests which are shorter or longer than the
field size of the curve, using zero-padding if the digest is too short,
and truncating if it's too long. The minimum digest size is set to half
of the curve's field size.
  • Loading branch information
tarcieri committed Sep 12, 2022
1 parent e664c82 commit ebd5355
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 7 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ecdsa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ rust-version = "1.57"

[dependencies]
elliptic-curve = { version = "0.12", default-features = false, features = ["digest", "sec1"] }
signature = { version = ">=1.5, <1.7", default-features = false, features = ["rand-preview"] }
signature = { version = ">=1.6.1, <1.7", default-features = false, features = ["hazmat-preview", "rand-preview"] }

# optional dependencies
der = { version = "0.6", optional = true }
Expand Down
32 changes: 30 additions & 2 deletions ecdsa/src/hazmat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use {

#[cfg(feature = "digest")]
use {
elliptic_curve::FieldSize,
core::cmp,
elliptic_curve::{bigint::Encoding, FieldSize},
signature::{digest::Digest, PrehashSignature},
};

Expand Down Expand Up @@ -216,8 +217,35 @@ where
#[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
pub trait DigestPrimitive: PrimeCurve {
/// Preferred digest to use when computing ECDSA signatures for this
/// elliptic curve. This should be a member of the SHA-2 family.
/// 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();

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
field_bytes[..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")]
Expand Down
20 changes: 20 additions & 0 deletions ecdsa/src/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use elliptic_curve::{
};
use signature::{
digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset},
hazmat::PrehashSigner,
rand_core::{CryptoRng, RngCore},
DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer,
};
Expand Down Expand Up @@ -192,6 +193,7 @@ where
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`)
Expand All @@ -203,6 +205,24 @@ where
}
}

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
.inner
.try_sign_prehashed_rfc6979::<C::Digest>(prehash, &[])?
.0)
}
}

impl<C> Signer<Signature<C>> for SigningKey<C>
where
Self: DigestSigner<C::Digest, Signature<C>>,
Expand Down
20 changes: 18 additions & 2 deletions ecdsa/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ use elliptic_curve::{
sec1::{self, EncodedPoint, FromEncodedPoint, ToEncodedPoint},
AffinePoint, FieldSize, PointCompression, PrimeCurve, ProjectiveArithmetic, PublicKey, Scalar,
};
use signature::digest::{Digest, FixedOutput};
use signature::{DigestVerifier, Verifier};
use signature::{
digest::{Digest, FixedOutput},
hazmat::PrehashVerifier,
DigestVerifier, Verifier,
};

#[cfg(feature = "pkcs8")]
use elliptic_curve::pkcs8::{self, AssociatedOid, DecodePublicKey};
Expand Down Expand Up @@ -117,6 +120,19 @@ where
}
}

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,
Expand Down

0 comments on commit ebd5355

Please sign in to comment.