Skip to content

Commit 85c1abb

Browse files
authored
Implement MultipartSigner/Verifier (#982)
1 parent 1fb2f88 commit 85c1abb

23 files changed

+387
-77
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dsa/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ crypto-primes = { version = "=0.7.0-pre.1", default-features = false }
2222
pkcs8 = { version = "0.11.0-rc.1", default-features = false, features = ["alloc"] }
2323
rfc6979 = { version = "0.5.0-rc.0" }
2424
sha2 = { version = "0.11.0-rc.0", default-features = false }
25-
signature = { version = "3.0.0-rc.0", default-features = false, features = ["alloc", "digest", "rand_core"] }
25+
signature = { version = "3.0.0-rc.1", default-features = false, features = ["alloc", "digest", "rand_core"] }
2626
zeroize = { version = "1", default-features = false }
2727

2828
[dev-dependencies]

dsa/src/signing_key.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use pkcs8::{
2222
#[cfg(feature = "hazmat")]
2323
use signature::rand_core::CryptoRng;
2424
use signature::{
25-
DigestSigner, RandomizedDigestSigner, Signer,
25+
DigestSigner, MultipartSigner, RandomizedDigestSigner, Signer,
2626
hazmat::{PrehashSigner, RandomizedPrehashSigner},
2727
rand_core::TryCryptoRng,
2828
};
@@ -149,7 +149,14 @@ impl ZeroizeOnDrop for SigningKey {}
149149

150150
impl Signer<Signature> for SigningKey {
151151
fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
152-
let digest = sha2::Sha256::new_with_prefix(msg);
152+
self.try_multipart_sign(&[msg])
153+
}
154+
}
155+
156+
impl MultipartSigner<Signature> for SigningKey {
157+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature, signature::Error> {
158+
let mut digest = sha2::Sha256::new();
159+
msg.iter().for_each(|slice| digest.update(slice));
153160
self.try_sign_digest(digest)
154161
}
155162
}

dsa/src/verifying_key.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use pkcs8::{
1717
},
1818
spki,
1919
};
20-
use signature::{DigestVerifier, Verifier, hazmat::PrehashVerifier};
20+
use signature::{DigestVerifier, MultipartVerifier, Verifier, hazmat::PrehashVerifier};
2121

2222
/// DSA public key.
2323
#[derive(Clone, Debug, PartialEq, PartialOrd)]
@@ -108,7 +108,19 @@ impl VerifyingKey {
108108

109109
impl Verifier<Signature> for VerifyingKey {
110110
fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> {
111-
self.verify_digest(sha2::Sha256::new_with_prefix(msg), signature)
111+
self.multipart_verify(&[msg], signature)
112+
}
113+
}
114+
115+
impl MultipartVerifier<Signature> for VerifyingKey {
116+
fn multipart_verify(
117+
&self,
118+
msg: &[&[u8]],
119+
signature: &Signature,
120+
) -> Result<(), signature::Error> {
121+
let mut digest = sha2::Sha256::new();
122+
msg.iter().for_each(|slice| digest.update(slice));
123+
self.verify_digest(digest, signature)
112124
}
113125
}
114126

ecdsa/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rust-version = "1.85"
1818

1919
[dependencies]
2020
elliptic-curve = { version = "0.14.0-rc.3", default-features = false, features = ["sec1"] }
21-
signature = { version = "3.0.0-rc.0", default-features = false, features = ["rand_core"] }
21+
signature = { version = "3.0.0-rc.1", default-features = false, features = ["rand_core"] }
2222
zeroize = { version = "1.5", default-features = false }
2323

2424
# optional dependencies

ecdsa/src/recovery.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use {
77
crate::{SigningKey, hazmat::sign_prehashed_rfc6979},
88
elliptic_curve::{FieldBytes, subtle::CtOption},
99
signature::{
10-
DigestSigner, RandomizedDigestSigner, Signer,
10+
DigestSigner, MultipartSigner, RandomizedDigestSigner, Signer,
1111
digest::FixedOutput,
1212
hazmat::{PrehashSigner, RandomizedPrehashSigner},
1313
rand_core::TryCryptoRng,
@@ -291,7 +291,21 @@ where
291291
SignatureSize<C>: ArraySize,
292292
{
293293
fn try_sign(&self, msg: &[u8]) -> Result<(Signature<C>, RecoveryId)> {
294-
self.sign_recoverable(msg)
294+
self.try_multipart_sign(&[msg])
295+
}
296+
}
297+
298+
#[cfg(feature = "signing")]
299+
impl<C> MultipartSigner<(Signature<C>, RecoveryId)> for SigningKey<C>
300+
where
301+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
302+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
303+
SignatureSize<C>: ArraySize,
304+
{
305+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<(Signature<C>, RecoveryId)> {
306+
let mut digest = C::Digest::new();
307+
msg.iter().for_each(|slice| digest.update(slice));
308+
self.sign_digest_recoverable(digest)
295309
}
296310
}
297311

ecdsa/src/signing.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use elliptic_curve::{
1515
zeroize::{Zeroize, ZeroizeOnDrop},
1616
};
1717
use signature::{
18-
DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer,
18+
DigestSigner, MultipartSigner, RandomizedDigestSigner, RandomizedMultipartSigner,
19+
RandomizedSigner, Signer,
1920
hazmat::{PrehashSigner, RandomizedPrehashSigner},
2021
rand_core::{CryptoRng, TryCryptoRng},
2122
};
@@ -176,7 +177,20 @@ where
176177
SignatureSize<C>: ArraySize,
177178
{
178179
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> {
179-
self.try_sign_digest(C::Digest::new_with_prefix(msg))
180+
self.try_multipart_sign(&[msg])
181+
}
182+
}
183+
184+
impl<C> MultipartSigner<Signature<C>> for SigningKey<C>
185+
where
186+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
187+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
188+
SignatureSize<C>: ArraySize,
189+
{
190+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> core::result::Result<Signature<C>, Error> {
191+
let mut digest = C::Digest::new();
192+
msg.iter().for_each(|slice| digest.update(slice));
193+
self.try_sign_digest(digest)
180194
}
181195
}
182196

@@ -234,7 +248,25 @@ where
234248
rng: &mut R,
235249
msg: &[u8],
236250
) -> Result<Signature<C>> {
237-
self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg))
251+
self.try_multipart_sign_with_rng(rng, &[msg])
252+
}
253+
}
254+
255+
impl<C> RandomizedMultipartSigner<Signature<C>> for SigningKey<C>
256+
where
257+
Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
258+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
259+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
260+
SignatureSize<C>: ArraySize,
261+
{
262+
fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
263+
&self,
264+
rng: &mut R,
265+
msg: &[&[u8]],
266+
) -> Result<Signature<C>> {
267+
let mut digest = C::Digest::new();
268+
msg.iter().for_each(|slice| digest.update(slice));
269+
self.try_sign_digest_with_rng(rng, digest)
238270
}
239271
}
240272

@@ -260,7 +292,21 @@ where
260292
SignatureSize<C>: ArraySize,
261293
{
262294
fn try_sign(&self, msg: &[u8]) -> Result<SignatureWithOid<C>> {
263-
self.try_sign_digest(C::Digest::new_with_prefix(msg))
295+
self.try_multipart_sign(&[msg])
296+
}
297+
}
298+
299+
impl<C> MultipartSigner<SignatureWithOid<C>> for SigningKey<C>
300+
where
301+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
302+
C::Digest: AssociatedOid,
303+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
304+
SignatureSize<C>: ArraySize,
305+
{
306+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<SignatureWithOid<C>> {
307+
let mut digest = C::Digest::new();
308+
msg.iter().for_each(|slice| digest.update(slice));
309+
self.try_sign_digest(digest)
264310
}
265311
}
266312

@@ -364,6 +410,25 @@ where
364410
}
365411
}
366412

413+
#[cfg(feature = "der")]
414+
impl<C> RandomizedMultipartSigner<der::Signature<C>> for SigningKey<C>
415+
where
416+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
417+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>>,
418+
SignatureSize<C>: ArraySize,
419+
der::MaxSize<C>: ArraySize,
420+
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
421+
{
422+
fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
423+
&self,
424+
rng: &mut R,
425+
msg: &[&[u8]],
426+
) -> Result<der::Signature<C>> {
427+
RandomizedMultipartSigner::<Signature<C>>::try_multipart_sign_with_rng(self, rng, msg)
428+
.map(Into::into)
429+
}
430+
}
431+
367432
//
368433
// Other trait impls
369434
//

ecdsa/src/verifying.rs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use elliptic_curve::{
1313
sec1::{self, CompressedPoint, EncodedPoint, FromEncodedPoint, ToEncodedPoint},
1414
};
1515
use signature::{
16-
DigestVerifier, Verifier,
16+
DigestVerifier, MultipartVerifier, Verifier,
1717
digest::{Digest, FixedOutput},
1818
hazmat::PrehashVerifier,
1919
};
@@ -178,7 +178,19 @@ where
178178
SignatureSize<C>: ArraySize,
179179
{
180180
fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
181-
self.verify_digest(C::Digest::new_with_prefix(msg), signature)
181+
self.multipart_verify(&[msg], signature)
182+
}
183+
}
184+
185+
impl<C> MultipartVerifier<Signature<C>> for VerifyingKey<C>
186+
where
187+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
188+
SignatureSize<C>: ArraySize,
189+
{
190+
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature<C>) -> Result<()> {
191+
let mut digest = C::Digest::new();
192+
msg.iter().for_each(|slice| digest.update(slice));
193+
self.verify_digest(digest, signature)
182194
}
183195
}
184196

@@ -189,11 +201,38 @@ where
189201
SignatureSize<C>: ArraySize,
190202
{
191203
fn verify(&self, msg: &[u8], sig: &SignatureWithOid<C>) -> Result<()> {
204+
self.multipart_verify(&[msg], sig)
205+
}
206+
}
207+
208+
#[cfg(feature = "sha2")]
209+
impl<C> MultipartVerifier<SignatureWithOid<C>> for VerifyingKey<C>
210+
where
211+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
212+
SignatureSize<C>: ArraySize,
213+
{
214+
fn multipart_verify(&self, msg: &[&[u8]], sig: &SignatureWithOid<C>) -> Result<()> {
192215
match sig.oid() {
193-
ECDSA_SHA224_OID => self.verify_prehash(&Sha224::digest(msg), sig.signature()),
194-
ECDSA_SHA256_OID => self.verify_prehash(&Sha256::digest(msg), sig.signature()),
195-
ECDSA_SHA384_OID => self.verify_prehash(&Sha384::digest(msg), sig.signature()),
196-
ECDSA_SHA512_OID => self.verify_prehash(&Sha512::digest(msg), sig.signature()),
216+
ECDSA_SHA224_OID => {
217+
let mut digest = Sha224::new();
218+
msg.iter().for_each(|slice| digest.update(slice));
219+
self.verify_prehash(&digest.finalize(), sig.signature())
220+
}
221+
ECDSA_SHA256_OID => {
222+
let mut digest = Sha256::new();
223+
msg.iter().for_each(|slice| digest.update(slice));
224+
self.verify_prehash(&digest.finalize(), sig.signature())
225+
}
226+
ECDSA_SHA384_OID => {
227+
let mut digest = Sha384::new();
228+
msg.iter().for_each(|slice| digest.update(slice));
229+
self.verify_prehash(&digest.finalize(), sig.signature())
230+
}
231+
ECDSA_SHA512_OID => {
232+
let mut digest = Sha512::new();
233+
msg.iter().for_each(|slice| digest.update(slice));
234+
self.verify_prehash(&digest.finalize(), sig.signature())
235+
}
197236
_ => Err(Error::new()),
198237
}
199238
}
@@ -242,6 +281,20 @@ where
242281
}
243282
}
244283

284+
#[cfg(feature = "der")]
285+
impl<C> MultipartVerifier<der::Signature<C>> for VerifyingKey<C>
286+
where
287+
C: EcdsaCurve + CurveArithmetic + DigestPrimitive,
288+
SignatureSize<C>: ArraySize,
289+
der::MaxSize<C>: ArraySize,
290+
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
291+
{
292+
fn multipart_verify(&self, msg: &[&[u8]], signature: &der::Signature<C>) -> Result<()> {
293+
let signature = Signature::<C>::try_from(signature.clone())?;
294+
MultipartVerifier::<Signature<C>>::multipart_verify(self, msg, &signature)
295+
}
296+
}
297+
245298
//
246299
// Other trait impls
247300
//

ed25519/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ edition = "2024"
1818
rust-version = "1.85"
1919

2020
[dependencies]
21-
signature = { version = "3.0.0-rc.0", default-features = false }
21+
signature = { version = "3.0.0-rc.1", default-features = false }
2222

2323
# optional dependencies
2424
pkcs8 = { version = "0.11.0-rc.2", optional = true }

lms/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rand = "0.9.0"
1818
sha2 = "0.11.0-rc.0"
1919
static_assertions = "1.1.0"
2020
rand_core = "0.9.0"
21-
signature = { version = "3.0.0-rc.0", features = ["alloc", "digest", "rand_core"] }
21+
signature = { version = "3.0.0-rc.1", features = ["alloc", "digest", "rand_core"] }
2222
typenum = { version = "1.17.0", features = ["const-generics"] }
2323
zeroize = "1.8.1"
2424

lms/src/lms/private.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::types::{Identifier, Typecode};
88
use digest::{Digest, Output, OutputSizeUser};
99
use hybrid_array::{Array, ArraySize};
1010
use rand_core::{CryptoRng, TryCryptoRng};
11-
use signature::{Error, RandomizedSignerMut};
11+
use signature::{Error, RandomizedMultipartSignerMut, RandomizedSignerMut};
1212

1313
use core::array::TryFromSliceError;
1414
use std::cmp::Ordering;
@@ -109,14 +109,25 @@ impl<Mode: LmsMode> RandomizedSignerMut<Signature<Mode>> for SigningKey<Mode> {
109109
&mut self,
110110
rng: &mut R,
111111
msg: &[u8],
112+
) -> Result<Signature<Mode>, Error> {
113+
self.try_multipart_sign_with_rng(rng, &[msg])
114+
}
115+
}
116+
117+
// this implements the algorithm from Appendix D in <https://datatracker.ietf.org/doc/html/rfc8554#appendix-D>
118+
impl<Mode: LmsMode> RandomizedMultipartSignerMut<Signature<Mode>> for SigningKey<Mode> {
119+
fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
120+
&mut self,
121+
rng: &mut R,
122+
msg: &[&[u8]],
112123
) -> Result<Signature<Mode>, Error> {
113124
if self.q >= Mode::LEAVES {
114125
return Err(Error::from_source(LmsOutOfPrivateKeys {}));
115126
}
116127

117128
let mut ots_priv_key =
118129
OtsPrivateKey::<Mode::OtsMode>::new_from_seed(self.q, self.id, &self.seed);
119-
let ots_sig = ots_priv_key.try_sign_with_rng(rng, msg)?;
130+
let ots_sig = ots_priv_key.try_multipart_sign_with_rng(rng, msg)?;
120131

121132
let r = (1 << Mode::H) + self.q;
122133

0 commit comments

Comments
 (0)