rfc6979: allow the use of non-block_api-backed hashes (#1360)

Never mind, I'll take care of it
This commit is contained in:
Arthur Gautier
2026-06-06 15:58:40 +00:00
committed by GitHub
parent 57a7cd7519
commit 43d9831ccf
10 changed files with 93 additions and 62 deletions
Generated
+14 -2
View File
@@ -1192,6 +1192,7 @@ dependencies = [
"hex-literal",
"hmac",
"sha2",
"sha3 0.12.0",
]
[[package]]
@@ -1365,6 +1366,17 @@ dependencies = [
"keccak",
]
[[package]]
name = "sha3"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc9bad02c26382724b2d2692c6f179285e4b54eeecd7968f52a50059c3c11759"
dependencies = [
"digest",
"keccak",
"sponge-cursor",
]
[[package]]
name = "shake"
version = "0.1.0"
@@ -1415,7 +1427,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
"sha3",
"sha3 0.11.0",
"signature",
"typenum",
"zerocopy",
@@ -1801,7 +1813,7 @@ dependencies = [
"serde_json",
"serdect",
"sha2",
"sha3",
"sha3 0.11.0",
"signature",
"spki",
"subtle",
+2 -2
View File
@@ -6,7 +6,7 @@ use crate::{Components, signing_key::SigningKey};
use alloc::vec;
use core::cmp::min;
use crypto_bigint::{BoxedUint, NonZero, RandomBits, Resize};
use digest::block_api::EagerHash;
use digest::{Digest, FixedOutputReset, common::BlockSizeUser};
use signature::rand_core::TryCryptoRng;
use zeroize::Zeroizing;
@@ -25,7 +25,7 @@ pub(crate) fn secret_number_rfc6979<D>(
hash: &[u8],
) -> Result<(BoxedUint, BoxedUint), signature::Error>
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutputReset,
{
let q = signing_key.verifying_key().components().q();
let size = (q.bits() / 8) as usize;
+4 -4
View File
@@ -13,7 +13,7 @@ use crypto_bigint::{
BoxedUint, ConcatenatingMul, NonZero, Resize,
modular::{BoxedMontyForm, BoxedMontyParams},
};
use digest::{Update, block_api::EagerHash};
use digest::{Digest, FixedOutputReset, common::BlockSizeUser};
use signature::{
DigestSigner, MultipartSigner, RandomizedDigestSigner, Signer,
hazmat::{PrehashSigner, RandomizedPrehashSigner},
@@ -94,7 +94,7 @@ impl SigningKey {
#[cfg(feature = "hazmat")]
pub fn sign_prehashed_rfc6979<D>(&self, prehash: &[u8]) -> Result<Signature, signature::Error>
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutputReset,
{
let k_kinv = crate::generate::secret_number_rfc6979::<D>(self, prehash)?;
self.sign_prehashed(k_kinv, prehash)
@@ -193,7 +193,7 @@ impl RandomizedPrehashSigner<Signature> for SigningKey {
impl<D> DigestSigner<D, Signature> for SigningKey
where
D: EagerHash + Update,
D: Digest + FixedOutputReset + BlockSizeUser,
{
fn try_sign_digest<F: Fn(&mut D) -> Result<(), signature::Error>>(
&self,
@@ -210,7 +210,7 @@ where
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey
where
D: EagerHash + Update,
D: Digest + FixedOutputReset + BlockSizeUser,
{
fn try_sign_digest_with_rng<
R: TryCryptoRng + ?Sized,
+5 -5
View File
@@ -1,6 +1,6 @@
#![cfg(feature = "hazmat")]
use crypto_bigint::BoxedUint;
use digest::{Update, block_api::EagerHash};
use digest::{Digest, FixedOutputReset, common::BlockSizeUser};
use dsa::{Components, Signature, SigningKey, VerifyingKey};
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512};
@@ -100,15 +100,15 @@ fn dsa_2048_signing_key() -> SigningKey {
/// Generate a signature given the unhashed message and a private key
fn generate_signature<D>(signing_key: SigningKey, data: &[u8]) -> Signature
where
D: EagerHash + Update,
D: Digest + FixedOutputReset + BlockSizeUser,
{
signing_key.sign_digest(|digest: &mut D| Update::update(digest, data))
signing_key.sign_digest(|digest: &mut D| Digest::update(digest, data))
}
/// Generate a signature using the 1024-bit DSA key
fn generate_1024_signature<D>(data: &[u8]) -> Signature
where
D: EagerHash + Update,
D: Digest + FixedOutputReset + BlockSizeUser,
{
generate_signature::<D>(dsa_1024_signing_key(), data)
}
@@ -116,7 +116,7 @@ where
/// Generate a signature using the 2048-bit DSA key
fn generate_2048_signature<D>(data: &[u8]) -> Signature
where
D: EagerHash + Update,
D: Digest + FixedOutputReset + BlockSizeUser,
{
generate_signature::<D>(dsa_2048_signing_key(), data)
}
+4 -4
View File
@@ -31,9 +31,9 @@ use {
};
#[cfg(feature = "digest")]
use digest::block_api::EagerHash;
use digest::{Digest, FixedOutput, FixedOutputReset, common::BlockSizeUser};
/// Bind a preferred [`EagerHash`] algorithm to an elliptic curve type.
/// 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.
@@ -41,7 +41,7 @@ use digest::block_api::EagerHash;
pub trait DigestAlgorithm: EcdsaCurve {
/// Preferred digest to use when computing ECDSA signatures for this
/// elliptic curve. This is typically a member of the SHA-2 family.
type Digest: EagerHash + digest::Update;
type Digest: BlockSizeUser + Digest + FixedOutput + FixedOutputReset;
}
/// Partial implementation of the `bits2int` function as defined in
@@ -166,7 +166,7 @@ pub fn sign_prehashed_rfc6979<C, D>(
) -> Result<(Signature<C>, RecoveryId)>
where
C: EcdsaCurve + CurveArithmetic,
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset,
SignatureSize<C>: ArraySize,
{
// From RFC6979 § 2.4:
+9 -8
View File
@@ -8,7 +8,7 @@ use {
EcdsaCurve, Signature, SignatureSize, SigningKey, VerifyingKey,
hazmat::{DigestAlgorithm, bits2field, sign_prehashed_rfc6979, verify_prehashed},
},
digest::{Digest, block_api::EagerHash},
digest::{Digest, FixedOutputReset, Update},
elliptic_curve::{
AffinePoint, FieldBytesEncoding, FieldBytesSize, Group, PrimeField, ProjectivePoint,
bigint::CheckedAdd,
@@ -108,7 +108,7 @@ impl RecoveryId {
) -> Result<Self>
where
C: EcdsaCurve + CurveArithmetic,
D: EagerHash,
D: Digest,
AffinePoint<C>: DecompressPoint<C> + FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: sec1::ModulusSize,
SignatureSize<C>: ArraySize,
@@ -203,7 +203,7 @@ where
/// Sign the given message digest, returning a signature and recovery ID.
pub fn sign_digest_recoverable<D>(&self, msg_digest: D) -> Result<(Signature<C>, RecoveryId)>
where
D: EagerHash,
D: Digest,
{
self.sign_prehash_recoverable(&msg_digest.finalize())
}
@@ -219,7 +219,7 @@ where
impl<C, D> DigestSigner<D, (Signature<C>, RecoveryId)> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: EagerHash + digest::Update,
D: Digest + Update,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
{
@@ -253,7 +253,7 @@ where
impl<C, D> RandomizedDigestSigner<D, (Signature<C>, RecoveryId)> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: EagerHash + digest::Update,
D: Digest + Update + FixedOutputReset,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
{
@@ -264,7 +264,7 @@ where
) -> Result<(Signature<C>, RecoveryId)> {
let mut digest = D::new();
f(&mut digest)?;
self.sign_prehash_with_rng(rng, &digest.finalize())
self.sign_prehash_with_rng(rng, &digest.finalize_reset())
}
}
@@ -301,7 +301,8 @@ where
{
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<(Signature<C>, RecoveryId)> {
let mut digest = C::Digest::new();
msg.iter().for_each(|slice| digest.update(slice));
msg.iter()
.for_each(|slice| Update::update(&mut digest, slice));
self.sign_digest_recoverable(digest)
}
}
@@ -337,7 +338,7 @@ where
recovery_id: RecoveryId,
) -> Result<Self>
where
D: EagerHash,
D: Digest,
{
Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id)
}
+8 -8
View File
@@ -5,7 +5,7 @@ use crate::{
hazmat::{DigestAlgorithm, bits2field, sign_prehashed_rfc6979},
};
use core::fmt::{self, Debug};
use digest::{Update, block_api::EagerHash, const_oid::AssociatedOid};
use digest::{Digest, FixedOutput, const_oid::AssociatedOid};
use elliptic_curve::{
CurveArithmetic, FieldBytes, Generate, NonZeroScalar, Scalar, SecretKey,
array::ArraySize,
@@ -146,14 +146,14 @@ where
impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: EagerHash + Update,
D: Digest + FixedOutput,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
{
fn try_sign_digest<F: Fn(&mut D) -> Result<()>>(&self, f: F) -> Result<Signature<C>> {
let mut digest = D::new();
f(&mut digest)?;
self.sign_prehash(&digest.finalize())
self.sign_prehash(&digest.finalize_fixed())
}
}
@@ -205,7 +205,7 @@ where
impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: EagerHash + Update,
D: Digest + FixedOutput,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
{
@@ -216,7 +216,7 @@ where
) -> Result<Signature<C>> {
let mut digest = D::new();
f(&mut digest)?;
self.sign_prehash_with_rng(rng, &digest.finalize())
self.sign_prehash_with_rng(rng, &digest.finalize_fixed())
}
}
@@ -284,7 +284,7 @@ where
impl<C, D> DigestSigner<D, SignatureWithOid<C>> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: AssociatedOid + EagerHash + Update,
D: AssociatedOid + Digest + FixedOutput,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
{
@@ -354,7 +354,7 @@ where
impl<C, D> RandomizedDigestSigner<D, der::Signature<C>> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: EagerHash + Update,
D: Digest + FixedOutput,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
der::MaxSize<C>: ArraySize,
@@ -393,7 +393,7 @@ where
impl<D, C> DigestSigner<D, der::Signature<C>> for SigningKey<C>
where
C: EcdsaCurve + CurveArithmetic + DigestAlgorithm,
D: EagerHash + Update,
D: Digest + FixedOutput,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArraySize,
der::MaxSize<C>: ArraySize,
+16 -14
View File
@@ -5,7 +5,7 @@ use crate::{
hazmat::{self, DigestAlgorithm, bits2field},
};
use core::{cmp::Ordering, fmt::Debug};
use digest::{Update, block_api::EagerHash};
use digest::{Digest, Update};
use elliptic_curve::{
AffinePoint, CurveArithmetic, FieldBytesSize, ProjectivePoint, PublicKey,
array::ArraySize,
@@ -144,7 +144,7 @@ where
impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
where
C: EcdsaCurve + CurveArithmetic,
D: EagerHash + Update,
D: Digest + Update,
SignatureSize<C>: ArraySize,
{
fn verify_digest<F: Fn(&mut D) -> Result<()>>(
@@ -194,7 +194,7 @@ where
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature<C>) -> Result<()> {
self.verify_digest(
|digest: &mut C::Digest| {
msg.iter().for_each(|slice| digest.update(slice));
msg.iter().for_each(|slice| Update::update(digest, slice));
Ok(())
},
signature,
@@ -220,28 +220,30 @@ where
SignatureSize<C>: ArraySize,
{
fn multipart_verify(&self, msg: &[&[u8]], sig: &SignatureWithOid<C>) -> Result<()> {
use digest::FixedOutput;
match sig.oid() {
ECDSA_SHA224_OID => {
let mut digest = Sha224::default();
msg.iter().for_each(|slice| digest.update(slice));
self.verify_prehash(&digest.finalize_fixed(), sig.signature())
msg.iter()
.for_each(|slice| Update::update(&mut digest, slice));
self.verify_prehash(&digest.finalize(), sig.signature())
}
ECDSA_SHA256_OID => {
let mut digest = Sha256::default();
msg.iter().for_each(|slice| digest.update(slice));
self.verify_prehash(&digest.finalize_fixed(), sig.signature())
msg.iter()
.for_each(|slice| Update::update(&mut digest, slice));
self.verify_prehash(&digest.finalize(), sig.signature())
}
ECDSA_SHA384_OID => {
let mut digest = Sha384::default();
msg.iter().for_each(|slice| digest.update(slice));
self.verify_prehash(&digest.finalize_fixed(), sig.signature())
msg.iter()
.for_each(|slice| Update::update(&mut digest, slice));
self.verify_prehash(&digest.finalize(), sig.signature())
}
ECDSA_SHA512_OID => {
let mut digest = Sha512::default();
msg.iter().for_each(|slice| digest.update(slice));
self.verify_prehash(&digest.finalize_fixed(), sig.signature())
msg.iter()
.for_each(|slice| Update::update(&mut digest, slice));
self.verify_prehash(&digest.finalize(), sig.signature())
}
_ => Err(Error::new()),
}
@@ -252,7 +254,7 @@ where
impl<C, D> DigestVerifier<D, der::Signature<C>> for VerifyingKey<C>
where
C: EcdsaCurve + CurveArithmetic,
D: EagerHash + Update,
D: Digest + Update,
SignatureSize<C>: ArraySize,
der::MaxSize<C>: ArraySize,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
+1
View File
@@ -22,6 +22,7 @@ hmac = { version = "0.13", default-features = false }
[dev-dependencies]
hex-literal = "1"
sha2 = "0.11"
sha3 = "0.12"
[lints]
workspace = true
+30 -15
View File
@@ -42,11 +42,11 @@ pub use hmac::digest::array::typenum::consts;
use core::fmt::{self, Debug};
use hmac::{
HmacReset,
SimpleHmacReset,
digest::{
KeyInit, Mac, OutputSizeUser,
Digest, FixedOutput, FixedOutputReset, KeyInit, Mac,
array::{Array, ArraySize},
block_api::EagerHash,
common::BlockSizeUser,
},
};
@@ -66,7 +66,7 @@ pub fn generate_k<D, N>(
data: &[u8],
) -> Array<u8, N>
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset,
N: ArraySize,
{
let mut k = Array::default();
@@ -91,7 +91,7 @@ where
#[inline]
pub fn generate_k_mut<D>(x: &[u8], q: &[u8], h: &[u8], data: &[u8], k: &mut [u8])
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset,
{
let k_len = k.len();
assert_eq!(k_len, x.len());
@@ -124,24 +124,24 @@ where
/// deterministic ephemeral scalar `k`.
pub struct HmacDrbg<D>
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutputReset,
{
/// HMAC key `K` (see RFC 6979 Section 3.2.c)
k: HmacReset<D>,
k: SimpleHmacReset<D>,
/// Chaining value `V` (see RFC 6979 Section 3.2.c)
v: Array<u8, <D::Core as OutputSizeUser>::OutputSize>,
v: Array<u8, D::OutputSize>,
}
impl<D> HmacDrbg<D>
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutputReset,
{
/// Initialize `HMAC_DRBG`.
#[must_use]
#[allow(clippy::missing_panics_doc, reason = "should not panic")]
pub fn new(entropy_input: &[u8], nonce: &[u8], personalization_string: &[u8]) -> Self {
let mut k = HmacReset::new(&Default::default());
let mut k = SimpleHmacReset::new(&Default::default());
let mut v = Array::default();
v.fill(0x01);
@@ -152,7 +152,7 @@ where
k.update(entropy_input);
k.update(nonce);
k.update(personalization_string);
k = HmacReset::new_from_slice(&k.finalize().into_bytes()).expect("should work");
k = SimpleHmacReset::new_from_slice(&k.finalize().into_bytes()).expect("should work");
// Steps 3.2.e,g: v = HMAC_k(v)
k.update(&v);
@@ -182,8 +182,8 @@ where
self.k.update(&self.v);
self.k.update(&[0x00]);
self.k =
HmacReset::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("should work");
self.k = SimpleHmacReset::new_from_slice(&self.k.finalize_reset().into_bytes())
.expect("should work");
self.k.update(&self.v);
self.v = self.k.finalize_reset().into_bytes();
}
@@ -191,7 +191,7 @@ where
impl<D> Debug for HmacDrbg<D>
where
D: EagerHash,
D: Digest + BlockSizeUser + FixedOutputReset,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("HmacDrbg").finish_non_exhaustive()
@@ -202,11 +202,12 @@ where
mod tests {
use crate::{
Array,
consts::{U21, U66},
consts::{U12, U21, U66},
generate_k,
};
use hex_literal::hex;
use sha2::{Digest, Sha256, Sha512};
use sha3::Sha3_256;
/// "Detailed Example" from RFC6979 Appendix A.1.
///
@@ -250,4 +251,18 @@ mod tests {
assert_eq!(k, expected_k);
}
/// This test showcase the use with a non-block_api hash
/// This is used for ethereum which uses ecdsa-sha3
#[test]
fn non_block_api() {
let x = hex!("000000000000000000000000");
let q = hex!("ffffffffffffffffffffffff");
let h = hex!("080808080808080808080808");
let data = b"";
let out = generate_k::<Sha3_256, U12>(&x.into(), &q.into(), &h.into(), data);
assert_eq!(out, hex!("d460ac6531e9743d3829850f"));
}
}