Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 85c1abb

Browse files
authoredJun 5, 2025··
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

‎lms/src/lms/public.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::types::Typecode;
99
use crate::{constants::D_INTR, lms::LmsMode};
1010
use digest::{Digest, OutputSizeUser};
1111
use hybrid_array::{Array, ArraySize};
12-
use signature::{Error, Verifier};
12+
use signature::{Error, MultipartVerifier, Verifier};
1313
use typenum::{Sum, U24};
1414

1515
//use crate::signature::Signature as Signature;
@@ -57,12 +57,18 @@ impl<Mode: LmsMode> VerifyingKey<Mode> {
5757

5858
impl<Mode: LmsMode> Verifier<Signature<Mode>> for VerifyingKey<Mode> {
5959
fn verify(&self, msg: &[u8], signature: &Signature<Mode>) -> Result<(), Error> {
60+
self.multipart_verify(&[msg], signature)
61+
}
62+
}
63+
64+
impl<Mode: LmsMode> MultipartVerifier<Signature<Mode>> for VerifyingKey<Mode> {
65+
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature<Mode>) -> Result<(), Error> {
6066
// Compute the LMS Public Key Candidate Tc from the signature,
6167
// message, identifier, pubtype, and ots_typecode, using
6268
// Algorithm 6a.
6369
let key_candidate = signature
6470
.lmots_sig
65-
.recover_pubkey(self.id, signature.q, msg);
71+
.raw_recover_pubkey(self.id, signature.q, msg);
6672

6773
let mut node_num = signature.q + Mode::LEAVES;
6874
let mut tmp = Mode::Hasher::new()

‎lms/src/ots/private.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::types::Identifier;
88
use digest::{Digest, Output};
99
use hybrid_array::Array;
1010
use rand_core::{CryptoRng, TryCryptoRng};
11-
use signature::{Error, RandomizedSignerMut};
11+
use signature::{Error, RandomizedMultipartSignerMut, RandomizedSignerMut};
1212
use zeroize::Zeroize;
1313
//use std::mem::MaybeUninit;
1414

@@ -100,6 +100,16 @@ impl<Mode: LmsOtsMode> RandomizedSignerMut<Signature<Mode>> for SigningKey<Mode>
100100
&mut self,
101101
rng: &mut R,
102102
msg: &[u8],
103+
) -> Result<Signature<Mode>, Error> {
104+
self.try_multipart_sign_with_rng(rng, &[msg])
105+
}
106+
}
107+
108+
impl<Mode: LmsOtsMode> RandomizedMultipartSignerMut<Signature<Mode>> for SigningKey<Mode> {
109+
fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
110+
&mut self,
111+
rng: &mut R,
112+
msg: &[&[u8]],
103113
) -> Result<Signature<Mode>, Error> {
104114
if !self.valid {
105115
return Err(Error::from_source(LmsOtsInvalidPrivateKey {}));
@@ -110,13 +120,13 @@ impl<Mode: LmsOtsMode> RandomizedSignerMut<Signature<Mode>> for SigningKey<Mode>
110120
rng.try_fill_bytes(&mut c).map_err(|_| Error::new())?;
111121

112122
// Q is the randomized message hash
113-
let q = Mode::Hasher::new()
114-
.chain_update(self.id)
115-
.chain_update(self.q.to_be_bytes())
116-
.chain_update(D_MESG)
117-
.chain_update(&c)
118-
.chain_update(msg)
119-
.finalize();
123+
let mut q_hasher = Mode::Hasher::new();
124+
q_hasher.update(self.id);
125+
q_hasher.update(self.q.to_be_bytes());
126+
q_hasher.update(D_MESG);
127+
q_hasher.update(&c);
128+
msg.iter().for_each(|slice| q_hasher.update(slice));
129+
let q = q_hasher.finalize();
120130

121131
// Y is the signature. We iterate over the message hash and checksum expanded into Winternitz coefficients
122132
let y = Mode::expand(&q).into_iter().enumerate().map(|(i, a)| {

‎lms/src/ots/public.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::types::Identifier;
99
use digest::{Output, OutputSizeUser};
1010

1111
use hybrid_array::{Array, ArraySize};
12-
use signature::{Error, Verifier};
12+
use signature::{Error, MultipartVerifier, Verifier};
1313
use std::cmp::Ordering;
1414
use std::ops::Add;
1515
use typenum::{Sum, U2, U24};
@@ -48,9 +48,21 @@ where
4848
{
4949
// this implements algorithm 4a of https://datatracker.ietf.org/doc/html/rfc8554#section-4.6
5050
fn verify(&self, msg: &[u8], signature: &Signature<Mode>) -> Result<(), Error> {
51+
self.multipart_verify(&[msg], signature)
52+
}
53+
}
54+
55+
impl<Mode: LmsOtsMode> MultipartVerifier<Signature<Mode>> for VerifyingKey<Mode>
56+
where
57+
// required to concat Q and cksm(Q)
58+
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U2>,
59+
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArraySize,
60+
{
61+
// this implements algorithm 4a of https://datatracker.ietf.org/doc/html/rfc8554#section-4.6
62+
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature<Mode>) -> Result<(), Error> {
5163
// If the public key is not at least four bytes long, return INVALID.
5264
// We are calling this method on a valid public key so there's no worry here.
53-
let kc = signature.recover_pubkey(self.id, self.q, msg);
65+
let kc = signature.raw_recover_pubkey(self.id, self.q, msg);
5466
// 4. If Kc is equal to K, return VALID; otherwise, return INVALID.
5567
if self.k == kc.k {
5668
Ok(())

‎lms/src/ots/signature.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,25 @@ impl<Mode: LmsOtsMode> Signature<Mode> {
106106
/// algorithm 4b of the LMS RFC. The signature will always be valid for
107107
/// the returned public key candidate.
108108
pub fn recover_pubkey(&self, id: Identifier, q: u32, msg: &[u8]) -> VerifyingKey<Mode> {
109+
self.raw_recover_pubkey(id, q, &[msg])
110+
}
111+
112+
pub(crate) fn raw_recover_pubkey(
113+
&self,
114+
id: Identifier,
115+
q: u32,
116+
msg: &[&[u8]],
117+
) -> VerifyingKey<Mode> {
109118
// algorithm 4b
110119

111120
// Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
112-
let msg_hash = Mode::Hasher::new()
113-
.chain_update(id)
114-
.chain_update(q.to_be_bytes())
115-
.chain_update(D_MESG)
116-
.chain_update(&self.c)
117-
.chain_update(msg)
118-
.finalize();
121+
let mut msg_hasher = Mode::Hasher::new();
122+
msg_hasher.update(id);
123+
msg_hasher.update(q.to_be_bytes());
124+
msg_hasher.update(D_MESG);
125+
msg_hasher.update(&self.c);
126+
msg.iter().for_each(|slice| msg_hasher.update(slice));
127+
let msg_hash = msg_hasher.finalize();
119128

120129
// first part of
121130
// Kc = H(I || u32str(q) || u16str(D_PBLC) || z[0] || z[1] || ... || z[p-1])

‎ml-dsa/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ hybrid-array = { version = "0.3", features = ["extra-sizes"] }
3636
num-traits = "0.2.19"
3737
rand_core = { version = "0.9", optional = true }
3838
sha3 = "0.11.0-rc.0"
39-
signature = "3.0.0-rc.0"
39+
signature = "3.0.0-rc.1"
4040
zeroize = { version = "1.8.1", optional = true, default-features = false }
4141

4242
const-oid = { version = "0.10", features = ["db"], optional = true }

‎ml-dsa/src/lib.rs

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ use core::fmt;
8989

9090
pub use crate::param::{EncodedSignature, EncodedSigningKey, EncodedVerifyingKey, MlDsaParams};
9191
pub use crate::util::B32;
92-
pub use signature::{self, Error};
92+
pub use signature::{self, Error, MultipartSigner, MultipartVerifier};
9393

9494
/// An ML-DSA signature
9595
#[derive(Clone, PartialEq, Debug)]
@@ -168,10 +168,10 @@ where
168168
// This method takes a slice of slices so that we can accommodate the varying calculations (direct
169169
// for test vectors, 0... for sign/sign_deterministic, 1... for the pre-hashed version) without
170170
// having to allocate memory for components.
171-
fn message_representative(tr: &[u8], Mp: &[&[u8]]) -> B64 {
171+
fn message_representative(tr: &[u8], Mp: &[&[&[u8]]]) -> B64 {
172172
let mut h = H::default().absorb(tr);
173173

174-
for m in Mp {
174+
for m in Mp.iter().copied().flatten() {
175175
h = h.absorb(m);
176176
}
177177

@@ -245,7 +245,15 @@ where
245245
/// only supports signing with an empty context string.
246246
impl<P: MlDsaParams> signature::Signer<Signature<P>> for KeyPair<P> {
247247
fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
248-
self.signing_key.sign_deterministic(msg, &[])
248+
self.try_multipart_sign(&[msg])
249+
}
250+
}
251+
252+
/// The `Signer` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA, and
253+
/// only supports signing with an empty context string.
254+
impl<P: MlDsaParams> MultipartSigner<Signature<P>> for KeyPair<P> {
255+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
256+
self.signing_key.raw_sign_deterministic(msg, &[])
249257
}
250258
}
251259

@@ -350,6 +358,13 @@ impl<P: MlDsaParams> SigningKey<P> {
350358
// Algorithm 7 ML-DSA.Sign_internal
351359
// TODO(RLB) Only expose based on a feature. Tests need access, but normal code shouldn't.
352360
pub fn sign_internal(&self, Mp: &[&[u8]], rnd: &B32) -> Signature<P>
361+
where
362+
P: MlDsaParams,
363+
{
364+
self.raw_sign_internal(&[Mp], rnd)
365+
}
366+
367+
fn raw_sign_internal(&self, Mp: &[&[&[u8]]], rnd: &B32) -> Signature<P>
353368
where
354369
P: MlDsaParams,
355370
{
@@ -440,13 +455,17 @@ impl<P: MlDsaParams> SigningKey<P> {
440455
/// This method will return an opaque error if the context string is more than 255 bytes long.
441456
// Algorithm 2 ML-DSA.Sign (optional deterministic variant)
442457
pub fn sign_deterministic(&self, M: &[u8], ctx: &[u8]) -> Result<Signature<P>, Error> {
458+
self.raw_sign_deterministic(&[M], ctx)
459+
}
460+
461+
fn raw_sign_deterministic(&self, M: &[&[u8]], ctx: &[u8]) -> Result<Signature<P>, Error> {
443462
if ctx.len() > 255 {
444463
return Err(Error::new());
445464
}
446465

447466
let rnd = B32::default();
448-
let Mp = &[&[0], &[Truncate::truncate(ctx.len())], ctx, M];
449-
Ok(self.sign_internal(Mp, &rnd))
467+
let Mp = &[&[&[0], &[Truncate::truncate(ctx.len())], ctx], M];
468+
Ok(self.raw_sign_internal(Mp, &rnd))
450469
}
451470

452471
/// Encode the key in a fixed-size byte array.
@@ -492,7 +511,16 @@ impl<P: MlDsaParams> SigningKey<P> {
492511
/// string, use the [`SigningKey::sign_deterministic`] method.
493512
impl<P: MlDsaParams> signature::Signer<Signature<P>> for SigningKey<P> {
494513
fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
495-
self.sign_deterministic(msg, &[])
514+
self.try_multipart_sign(&[msg])
515+
}
516+
}
517+
518+
/// The `Signer` implementation for `SigningKey` uses the optional deterministic variant of ML-DSA, and
519+
/// only supports signing with an empty context string. If you would like to include a context
520+
/// string, use the [`SigningKey::sign_deterministic`] method.
521+
impl<P: MlDsaParams> MultipartSigner<Signature<P>> for SigningKey<P> {
522+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
523+
self.raw_sign_deterministic(msg, &[])
496524
}
497525
}
498526

@@ -576,6 +604,13 @@ impl<P: MlDsaParams> VerifyingKey<P> {
576604
/// and it does not separate the context string from the rest of the message.
577605
// Algorithm 8 ML-DSA.Verify_internal
578606
pub fn verify_internal(&self, Mp: &[&[u8]], sigma: &Signature<P>) -> bool
607+
where
608+
P: MlDsaParams,
609+
{
610+
self.raw_verify_internal(&[Mp], sigma)
611+
}
612+
613+
fn raw_verify_internal(&self, Mp: &[&[&[u8]]], sigma: &Signature<P>) -> bool
579614
where
580615
P: MlDsaParams,
581616
{
@@ -605,12 +640,16 @@ impl<P: MlDsaParams> VerifyingKey<P> {
605640
/// This algorithm reflect the ML-DSA.Verify algorithm from FIPS 204.
606641
// Algorithm 3 ML-DSA.Verify
607642
pub fn verify_with_context(&self, M: &[u8], ctx: &[u8], sigma: &Signature<P>) -> bool {
643+
self.raw_verify_with_context(&[M], ctx, sigma)
644+
}
645+
646+
fn raw_verify_with_context(&self, M: &[&[u8]], ctx: &[u8], sigma: &Signature<P>) -> bool {
608647
if ctx.len() > 255 {
609648
return false;
610649
}
611650

612-
let Mp = &[&[0], &[Truncate::truncate(ctx.len())], ctx, M];
613-
self.verify_internal(Mp, sigma)
651+
let Mp = &[&[&[0], &[Truncate::truncate(ctx.len())], ctx], M];
652+
self.raw_verify_internal(Mp, sigma)
614653
}
615654

616655
fn encode_internal(rho: &B32, t1: &Vector<P::K>) -> EncodedVerifyingKey<P> {
@@ -635,7 +674,13 @@ impl<P: MlDsaParams> VerifyingKey<P> {
635674

636675
impl<P: MlDsaParams> signature::Verifier<Signature<P>> for VerifyingKey<P> {
637676
fn verify(&self, msg: &[u8], signature: &Signature<P>) -> Result<(), Error> {
638-
self.verify_with_context(msg, &[], signature)
677+
self.multipart_verify(&[msg], signature)
678+
}
679+
}
680+
681+
impl<P: MlDsaParams> MultipartVerifier<Signature<P>> for VerifyingKey<P> {
682+
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature<P>) -> Result<(), Error> {
683+
self.raw_verify_with_context(msg, &[], signature)
639684
.then_some(())
640685
.ok_or(Error::new())
641686
}

‎slh-dsa/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ typenum = { version = "1.17.0", features = ["const-generics"] }
2121
sha3 = { version = "0.11.0-rc.0", default-features = false }
2222
zerocopy = { version = "0.7.34", features = ["derive"] }
2323
rand_core = { version = "0.9.2" }
24-
signature = { version = "3.0.0-rc.0", features = ["rand_core"] }
24+
signature = { version = "3.0.0-rc.1", features = ["rand_core"] }
2525
hmac = "0.13.0-prc.0"
2626
sha2 = { version = "0.11.0-rc.0", default-features = false }
2727
digest = "0.11.0-rc.0"

‎slh-dsa/src/hashes.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ pub(crate) trait HashSuite: Sized + Clone + Debug + PartialEq + Eq {
2323
fn prf_msg(
2424
sk_prf: &SkPrf<Self::N>,
2525
opt_rand: &Array<u8, Self::N>,
26-
msg: &[impl AsRef<[u8]>],
26+
msg: &[&[impl AsRef<[u8]>]],
2727
) -> Array<u8, Self::N>;
2828

2929
/// Hashes a message using a given randomizer
3030
fn h_msg(
3131
rand: &Array<u8, Self::N>,
3232
pk_seed: &PkSeed<Self::N>,
3333
pk_root: &Array<u8, Self::N>,
34-
msg: &[impl AsRef<[u8]>],
34+
msg: &[&[impl AsRef<[u8]>]],
3535
) -> Array<u8, Self::M>;
3636

3737
/// PRF that is used to generate the secret values in WOTS+ and FORS private keys.
@@ -76,7 +76,7 @@ mod tests {
7676
let opt_rand = Array::<u8, H::N>::from_fn(|_| 1);
7777
let msg = [2u8; 32];
7878

79-
let result = H::prf_msg(&sk_prf, &opt_rand, &[msg]);
79+
let result = H::prf_msg(&sk_prf, &opt_rand, &[&[msg]]);
8080

8181
assert_eq!(result.as_slice(), expected);
8282
}
@@ -87,7 +87,7 @@ mod tests {
8787
let pk_root = Array::<u8, H::N>::from_fn(|_| 2);
8888
let msg = [3u8; 32];
8989

90-
let result = H::h_msg(&rand, &pk_seed, &pk_root, &[msg]);
90+
let result = H::h_msg(&rand, &pk_seed, &pk_root, &[&[msg]]);
9191

9292
assert_eq!(result.as_slice(), expected);
9393
}

‎slh-dsa/src/hashes/sha2.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ where
6161
fn prf_msg(
6262
sk_prf: &SkPrf<Self::N>,
6363
opt_rand: &Array<u8, Self::N>,
64-
msg: &[impl AsRef<[u8]>],
64+
msg: &[&[impl AsRef<[u8]>]],
6565
) -> Array<u8, Self::N> {
6666
let mut mac = Hmac::<Sha256>::new_from_slice(sk_prf.as_ref()).unwrap();
6767
mac.update(opt_rand.as_slice());
6868
msg.iter()
69+
.copied()
70+
.flatten()
6971
.for_each(|msg_part| mac.update(msg_part.as_ref()));
7072
let result = mac.finalize().into_bytes();
7173
Array::clone_from_slice(&result[..Self::N::USIZE])
@@ -75,13 +77,16 @@ where
7577
rand: &Array<u8, Self::N>,
7678
pk_seed: &PkSeed<Self::N>,
7779
pk_root: &Array<u8, Self::N>,
78-
msg: &[impl AsRef<[u8]>],
80+
msg: &[&[impl AsRef<[u8]>]],
7981
) -> Array<u8, Self::M> {
8082
let mut h = Sha256::new();
8183
h.update(rand);
8284
h.update(pk_seed);
8385
h.update(pk_root);
84-
msg.iter().for_each(|msg_part| h.update(msg_part.as_ref()));
86+
msg.iter()
87+
.copied()
88+
.flatten()
89+
.for_each(|msg_part| h.update(msg_part.as_ref()));
8590
let result = Array(h.finalize().into());
8691
let seed = rand.clone().concat(pk_seed.0.clone()).concat(result);
8792
mgf1::<Sha256, Self::M>(&seed)
@@ -224,11 +229,13 @@ where
224229
fn prf_msg(
225230
sk_prf: &SkPrf<Self::N>,
226231
opt_rand: &Array<u8, Self::N>,
227-
msg: &[impl AsRef<[u8]>],
232+
msg: &[&[impl AsRef<[u8]>]],
228233
) -> Array<u8, Self::N> {
229234
let mut mac = Hmac::<Sha512>::new_from_slice(sk_prf.as_ref()).unwrap();
230235
mac.update(opt_rand.as_slice());
231236
msg.iter()
237+
.copied()
238+
.flatten()
232239
.for_each(|msg_part| mac.update(msg_part.as_ref()));
233240
let result = mac.finalize().into_bytes();
234241
Array::clone_from_slice(&result[..Self::N::USIZE])
@@ -238,13 +245,16 @@ where
238245
rand: &Array<u8, Self::N>,
239246
pk_seed: &PkSeed<Self::N>,
240247
pk_root: &Array<u8, Self::N>,
241-
msg: &[impl AsRef<[u8]>],
248+
msg: &[&[impl AsRef<[u8]>]],
242249
) -> Array<u8, Self::M> {
243250
let mut h = Sha512::new();
244251
h.update(rand);
245252
h.update(pk_seed);
246253
h.update(pk_root);
247-
msg.iter().for_each(|msg_part| h.update(msg_part.as_ref()));
254+
msg.iter()
255+
.copied()
256+
.flatten()
257+
.for_each(|msg_part| h.update(msg_part.as_ref()));
248258
let result = Array(h.finalize().into());
249259
let seed = rand.clone().concat(pk_seed.0.clone()).concat(result);
250260
mgf1::<Sha512, Self::M>(&seed)

‎slh-dsa/src/hashes/shake.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ where
3535
fn prf_msg(
3636
sk_prf: &SkPrf<Self::N>,
3737
opt_rand: &Array<u8, Self::N>,
38-
msg: &[impl AsRef<[u8]>],
38+
msg: &[&[impl AsRef<[u8]>]],
3939
) -> Array<u8, Self::N> {
4040
let mut hasher = Shake256::default();
4141
hasher.update(sk_prf.as_ref());
4242
hasher.update(opt_rand.as_slice());
4343
msg.iter()
44+
.copied()
45+
.flatten()
4446
.for_each(|msg_part| hasher.update(msg_part.as_ref()));
4547
let mut output = Array::<u8, Self::N>::default();
4648
hasher.finalize_xof_into(&mut output);
@@ -51,13 +53,15 @@ where
5153
rand: &Array<u8, Self::N>,
5254
pk_seed: &PkSeed<Self::N>,
5355
pk_root: &Array<u8, Self::N>,
54-
msg: &[impl AsRef<[u8]>],
56+
msg: &[&[impl AsRef<[u8]>]],
5557
) -> Array<u8, Self::M> {
5658
let mut hasher = Shake256::default();
5759
hasher.update(rand.as_slice());
5860
hasher.update(pk_seed.as_ref());
5961
hasher.update(pk_root.as_ref());
6062
msg.iter()
63+
.copied()
64+
.flatten()
6165
.for_each(|msg_part| hasher.update(msg_part.as_ref()));
6266
let mut output = Array::<u8, Self::M>::default();
6367
hasher.finalize_xof_into(&mut output);
@@ -276,7 +280,7 @@ mod tests {
276280

277281
let expected = hex!("bc5c062307df0a41aeeae19ad655f7b2");
278282

279-
let result = H::prf_msg(&sk_prf, &opt_rand, &[msg]);
283+
let result = H::prf_msg(&sk_prf, &opt_rand, &[&[msg]]);
280284

281285
assert_eq!(result.as_slice(), expected);
282286
}

‎slh-dsa/src/signing_key.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::util::split_digest;
44
use crate::verifying_key::VerifyingKey;
55
use crate::{ParameterSet, PkSeed, Sha2L1, Sha2L35, Shake, VerifyingKeyLen};
66
use ::signature::{
7-
Error, KeypairRef, RandomizedSigner, Signer,
7+
Error, KeypairRef, MultipartSigner, RandomizedMultipartSigner, RandomizedSigner, Signer,
88
rand_core::{CryptoRng, TryCryptoRng},
99
};
1010
use hybrid_array::{Array, ArraySize};
@@ -133,6 +133,10 @@ impl<P: ParameterSet> SigningKey<P> {
133133
/// Published for KAT validation purposes but not intended for general use.
134134
/// opt_rand must be a P::N length slice, panics otherwise.
135135
pub fn slh_sign_internal(&self, msg: &[&[u8]], opt_rand: Option<&[u8]>) -> Signature<P> {
136+
self.raw_slh_sign_internal(&[msg], opt_rand)
137+
}
138+
139+
fn raw_slh_sign_internal(&self, msg: &[&[&[u8]]], opt_rand: Option<&[u8]>) -> Signature<P> {
136140
let rand = opt_rand
137141
.unwrap_or(&self.verifying_key.pk_seed.0)
138142
.try_into()
@@ -167,12 +171,21 @@ impl<P: ParameterSet> SigningKey<P> {
167171
msg: &[u8],
168172
ctx: &[u8],
169173
opt_rand: Option<&[u8]>,
174+
) -> Result<Signature<P>, Error> {
175+
self.raw_try_sign_with_context(&[msg], ctx, opt_rand)
176+
}
177+
178+
fn raw_try_sign_with_context(
179+
&self,
180+
msg: &[&[u8]],
181+
ctx: &[u8],
182+
opt_rand: Option<&[u8]>,
170183
) -> Result<Signature<P>, Error> {
171184
let ctx_len = u8::try_from(ctx.len()).map_err(|_| Error::new())?;
172185
let ctx_len_bytes = ctx_len.to_be_bytes();
173186

174-
let ctx_msg = [&[0], &ctx_len_bytes, ctx, msg];
175-
Ok(self.slh_sign_internal(&ctx_msg, opt_rand))
187+
let ctx_msg = [&[&[0], &ctx_len_bytes, ctx], msg];
188+
Ok(self.raw_slh_sign_internal(&ctx_msg, opt_rand))
176189
}
177190

178191
/// Serialize the signing key to a new stack-allocated array
@@ -218,7 +231,13 @@ impl<P: ParameterSet> TryFrom<&[u8]> for SigningKey<P> {
218231

219232
impl<P: ParameterSet> Signer<Signature<P>> for SigningKey<P> {
220233
fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
221-
self.try_sign_with_context(msg, &[], None)
234+
self.try_multipart_sign(&[msg])
235+
}
236+
}
237+
238+
impl<P: ParameterSet> MultipartSigner<Signature<P>> for SigningKey<P> {
239+
fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
240+
self.raw_try_sign_with_context(msg, &[], None)
222241
}
223242
}
224243

@@ -228,10 +247,20 @@ impl<P: ParameterSet> RandomizedSigner<Signature<P>> for SigningKey<P> {
228247
rng: &mut R,
229248
msg: &[u8],
230249
) -> Result<Signature<P>, signature::Error> {
250+
self.try_multipart_sign_with_rng(rng, &[msg])
251+
}
252+
}
253+
254+
impl<P: ParameterSet> RandomizedMultipartSigner<Signature<P>> for SigningKey<P> {
255+
fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
256+
&self,
257+
rng: &mut R,
258+
msg: &[&[u8]],
259+
) -> Result<Signature<P>, Error> {
231260
let mut randomizer = Array::<u8, P::N>::default();
232261
rng.try_fill_bytes(randomizer.as_mut_slice())
233262
.map_err(|_| signature::Error::new())?;
234-
self.try_sign_with_context(msg, &[], Some(&randomizer))
263+
self.raw_try_sign_with_context(msg, &[], Some(&randomizer))
235264
}
236265
}
237266

‎slh-dsa/src/verifying_key.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::Shake;
55
use crate::address::ForsTree;
66
use crate::signature_encoding::Signature;
77
use crate::util::split_digest;
8-
use ::signature::{Error, Verifier};
8+
use ::signature::{Error, MultipartVerifier, Verifier};
99
use hybrid_array::{Array, ArraySize};
1010
use pkcs8::{der, spki};
1111
use rand_core::CryptoRng;
@@ -59,6 +59,14 @@ impl<P: ParameterSet + VerifyingKeyLen> VerifyingKey<P> {
5959
&self,
6060
msg: &[&[u8]],
6161
signature: &Signature<P>,
62+
) -> Result<(), Error> {
63+
self.raw_slh_verify_internal(&[msg], signature)
64+
}
65+
66+
fn raw_slh_verify_internal(
67+
&self,
68+
msg: &[&[&[u8]]],
69+
signature: &Signature<P>,
6270
) -> Result<(), Error> {
6371
let pk_seed = &self.pk_seed;
6472
let randomizer = &signature.randomizer;
@@ -84,12 +92,21 @@ impl<P: ParameterSet + VerifyingKeyLen> VerifyingKey<P> {
8492
msg: &[u8],
8593
ctx: &[u8],
8694
signature: &Signature<P>,
95+
) -> Result<(), Error> {
96+
self.raw_try_verify_with_context(&[msg], ctx, signature)
97+
}
98+
99+
fn raw_try_verify_with_context(
100+
&self,
101+
msg: &[&[u8]],
102+
ctx: &[u8],
103+
signature: &Signature<P>,
87104
) -> Result<(), Error> {
88105
let ctx_len = u8::try_from(ctx.len()).map_err(|_| Error::new())?;
89106
let ctx_len_bytes = ctx_len.to_be_bytes();
90107

91-
let ctx_msg = [&[0], &ctx_len_bytes, ctx, msg];
92-
self.slh_verify_internal(&ctx_msg, signature) // TODO - context processing
108+
let ctx_msg = [&[&[0], &ctx_len_bytes, ctx], msg];
109+
self.raw_slh_verify_internal(&ctx_msg, signature) // TODO - context processing
93110
}
94111

95112
/// Serialize the verifying key to a new stack-allocated array
@@ -151,7 +168,13 @@ impl<P: ParameterSet> TryFrom<&[u8]> for VerifyingKey<P> {
151168

152169
impl<P: ParameterSet> Verifier<Signature<P>> for VerifyingKey<P> {
153170
fn verify(&self, msg: &[u8], signature: &Signature<P>) -> Result<(), Error> {
154-
self.try_verify_with_context(msg, &[], signature) // TODO - context processing
171+
self.multipart_verify(&[msg], signature)
172+
}
173+
}
174+
175+
impl<P: ParameterSet> MultipartVerifier<Signature<P>> for VerifyingKey<P> {
176+
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature<P>) -> Result<(), Error> {
177+
self.raw_try_verify_with_context(msg, &[], signature) // TODO - context processing
155178
}
156179
}
157180

0 commit comments

Comments
 (0)
Please sign in to comment.