@@ -51,11 +51,10 @@ use hybrid_array::{
51
51
U75 , U80 , U88 , Unsigned ,
52
52
} ,
53
53
} ;
54
- use signature:: digest:: Update ;
55
54
use signature:: { DigestSigner , DigestVerifier , MultipartSigner , MultipartVerifier , Signer } ;
56
55
57
56
#[ cfg( feature = "rand_core" ) ]
58
- use signature:: RandomizedDigestSigner ;
57
+ use signature:: { RandomizedDigestSigner , RandomizedMultipartSigner , RandomizedSigner } ;
59
58
60
59
#[ cfg( feature = "rand_core" ) ]
61
60
use rand_core:: { CryptoRng , TryCryptoRng } ;
@@ -171,17 +170,46 @@ where
171
170
const ALGORITHM_IDENTIFIER : AlgorithmIdentifierRef < ' static > = P :: ALGORITHM_IDENTIFIER ;
172
171
}
173
172
174
- // This method takes a slice of slices so that we can accommodate the varying calculations (direct
175
- // for test vectors, 0... for sign/sign_deterministic, 1... for the pre-hashed version) without
176
- // having to allocate memory for components.
177
- fn message_representative ( tr : & [ u8 ] , Mp : & [ & [ & [ u8 ] ] ] ) -> B64 {
178
- let mut h = H :: default ( ) . absorb ( tr) ;
173
+ struct MuBuilder ( H ) ;
179
174
180
- for m in Mp . iter ( ) . copied ( ) . flatten ( ) {
181
- h = h. absorb ( m) ;
175
+ impl MuBuilder {
176
+ fn new ( tr : & [ u8 ] , ctx : & [ u8 ] ) -> Self {
177
+ let mut h = H :: default ( ) ;
178
+ h = h. absorb ( tr) ;
179
+ h = h. absorb ( & [ 0 ] ) ;
180
+ h = h. absorb ( & [ Truncate :: truncate ( ctx. len ( ) ) ] ) ;
181
+ h = h. absorb ( ctx) ;
182
+
183
+ Self ( h)
184
+ }
185
+
186
+ fn internal ( tr : & [ u8 ] , Mp : & [ & [ u8 ] ] ) -> B64 {
187
+ let mut h = H :: default ( ) . absorb ( tr) ;
188
+
189
+ for m in Mp {
190
+ h = h. absorb ( m) ;
191
+ }
192
+
193
+ h. squeeze_new ( )
194
+ }
195
+
196
+ fn message ( mut self , M : & [ & [ u8 ] ] ) -> B64 {
197
+ for m in M {
198
+ self . 0 = self . 0 . absorb ( m) ;
199
+ }
200
+
201
+ self . 0 . squeeze_new ( )
202
+ }
203
+
204
+ fn finish ( mut self ) -> B64 {
205
+ self . 0 . squeeze_new ( )
182
206
}
207
+ }
183
208
184
- h. squeeze_new ( )
209
+ impl AsMut < Shake256 > for MuBuilder {
210
+ fn as_mut ( & mut self ) -> & mut Shake256 {
211
+ self . 0 . updatable ( )
212
+ }
185
213
}
186
214
187
215
/// An ML-DSA key pair
@@ -388,18 +416,7 @@ impl<P: MlDsaParams> SigningKey<P> {
388
416
where
389
417
P : MlDsaParams ,
390
418
{
391
- self . raw_sign_internal ( & [ Mp ] , rnd)
392
- }
393
-
394
- fn raw_sign_internal ( & self , Mp : & [ & [ & [ u8 ] ] ] , rnd : & B32 ) -> Signature < P >
395
- where
396
- P : MlDsaParams ,
397
- {
398
- // Compute the message representative
399
- // XXX(RLB): This line incorporates some of the logic from ML-DSA.sign to avoid computing
400
- // the concatenated M'.
401
- // XXX(RLB) Should the API represent this as an input?
402
- let mu = message_representative ( & self . tr , Mp ) ;
419
+ let mu = MuBuilder :: internal ( & self . tr , Mp ) ;
403
420
self . raw_sign_mu ( & mu, rnd)
404
421
}
405
422
@@ -469,6 +486,16 @@ impl<P: MlDsaParams> SigningKey<P> {
469
486
M : & [ u8 ] ,
470
487
ctx : & [ u8 ] ,
471
488
rng : & mut R ,
489
+ ) -> Result < Signature < P > , Error > {
490
+ self . raw_sign_randomized ( & [ M ] , ctx, rng)
491
+ }
492
+
493
+ #[ cfg( feature = "rand_core" ) ]
494
+ fn raw_sign_randomized < R : TryCryptoRng + ?Sized > (
495
+ & self ,
496
+ Mp : & [ & [ u8 ] ] ,
497
+ ctx : & [ u8 ] ,
498
+ rng : & mut R ,
472
499
) -> Result < Signature < P > , Error > {
473
500
if ctx. len ( ) > 255 {
474
501
return Err ( Error :: new ( ) ) ;
@@ -477,8 +504,8 @@ impl<P: MlDsaParams> SigningKey<P> {
477
504
let mut rnd = B32 :: default ( ) ;
478
505
rng. try_fill_bytes ( & mut rnd) . map_err ( |_| Error :: new ( ) ) ?;
479
506
480
- let Mp = & [ & [ 0 ] , & [ Truncate :: truncate ( ctx . len ( ) ) ] , ctx, M ] ;
481
- Ok ( self . sign_internal ( Mp , & rnd) )
507
+ let mu = MuBuilder :: new ( & self . tr , ctx) . message ( Mp ) ;
508
+ Ok ( self . raw_sign_mu ( & mu , & rnd) )
482
509
}
483
510
484
511
/// This method reflects the randomized ML-DSA.Sign algorithm with a pre-computed μ.
@@ -517,14 +544,13 @@ impl<P: MlDsaParams> SigningKey<P> {
517
544
self . raw_sign_mu ( mu, & rnd)
518
545
}
519
546
520
- fn raw_sign_deterministic ( & self , M : & [ & [ u8 ] ] , ctx : & [ u8 ] ) -> Result < Signature < P > , Error > {
547
+ fn raw_sign_deterministic ( & self , Mp : & [ & [ u8 ] ] , ctx : & [ u8 ] ) -> Result < Signature < P > , Error > {
521
548
if ctx. len ( ) > 255 {
522
549
return Err ( Error :: new ( ) ) ;
523
550
}
524
551
525
- let rnd = B32 :: default ( ) ;
526
- let Mp = & [ & [ & [ 0 ] , & [ Truncate :: truncate ( ctx. len ( ) ) ] , ctx] , M ] ;
527
- Ok ( self . raw_sign_internal ( Mp , & rnd) )
552
+ let mu = MuBuilder :: new ( & self . tr , ctx) . message ( Mp ) ;
553
+ Ok ( self . sign_mu_deterministic ( & mu) )
528
554
}
529
555
530
556
/// Encode the key in a fixed-size byte array.
@@ -608,9 +634,9 @@ impl<P: MlDsaParams> DigestSigner<Shake256, Signature<P>> for SigningKey<P> {
608
634
& self ,
609
635
f : F ,
610
636
) -> Result < Signature < P > , Error > {
611
- let mut digest = Shake256 :: default ( ) . chain ( self . tr ) . chain ( [ 0 , 0 ] ) ;
612
- f ( & mut digest ) ?;
613
- let mu = H :: pre_digest ( digest ) . squeeze_new ( ) ;
637
+ let mut mu = MuBuilder :: new ( & self . tr , & [ ] ) ;
638
+ f ( mu . as_mut ( ) ) ?;
639
+ let mu = mu . finish ( ) ;
614
640
615
641
Ok ( self . sign_mu_deterministic ( & mu) )
616
642
}
@@ -640,13 +666,27 @@ impl<P: MlDsaParams> signature::Keypair for SigningKey<P> {
640
666
/// context string. If you would like to include a context string, use the
641
667
/// [`SigningKey::sign_randomized`] method.
642
668
#[ cfg( feature = "rand_core" ) ]
643
- impl < P : MlDsaParams > signature :: RandomizedSigner < Signature < P > > for SigningKey < P > {
669
+ impl < P : MlDsaParams > RandomizedSigner < Signature < P > > for SigningKey < P > {
644
670
fn try_sign_with_rng < R : TryCryptoRng + ?Sized > (
645
671
& self ,
646
672
rng : & mut R ,
647
673
msg : & [ u8 ] ,
648
674
) -> Result < Signature < P > , Error > {
649
- self . sign_randomized ( msg, & [ ] , rng)
675
+ self . try_multipart_sign_with_rng ( rng, & [ msg] )
676
+ }
677
+ }
678
+
679
+ /// The `RandomizedSigner` implementation for `SigningKey` only supports signing with an empty
680
+ /// context string. If you would like to include a context string, use the
681
+ /// [`SigningKey::sign_randomized`] method.
682
+ #[ cfg( feature = "rand_core" ) ]
683
+ impl < P : MlDsaParams > RandomizedMultipartSigner < Signature < P > > for SigningKey < P > {
684
+ fn try_multipart_sign_with_rng < R : TryCryptoRng + ?Sized > (
685
+ & self ,
686
+ rng : & mut R ,
687
+ msg : & [ & [ u8 ] ] ,
688
+ ) -> Result < Signature < P > , Error > {
689
+ self . raw_sign_randomized ( msg, & [ ] , rng)
650
690
}
651
691
}
652
692
@@ -663,9 +703,9 @@ impl<P: MlDsaParams> RandomizedDigestSigner<Shake256, Signature<P>> for SigningK
663
703
rng : & mut R ,
664
704
f : F ,
665
705
) -> Result < Signature < P > , Error > {
666
- let mut digest = Shake256 :: default ( ) . chain ( self . tr ) . chain ( [ 0 , 0 ] ) ;
667
- f ( & mut digest ) ?;
668
- let mu = H :: pre_digest ( digest ) . squeeze_new ( ) ;
706
+ let mut mu = MuBuilder :: new ( & self . tr , & [ ] ) ;
707
+ f ( mu . as_mut ( ) ) ?;
708
+ let mu = mu . finish ( ) ;
669
709
670
710
self . sign_mu_randomized ( & mu, rng)
671
711
}
@@ -736,19 +776,11 @@ impl<P: MlDsaParams> VerifyingKey<P> {
736
776
/// include the domain separator that distinguishes between the normal and pre-hashed cases,
737
777
/// and it does not separate the context string from the rest of the message.
738
778
// Algorithm 8 ML-DSA.Verify_internal
739
- pub fn verify_internal ( & self , Mp : & [ & [ u8 ] ] , sigma : & Signature < P > ) -> bool
740
- where
741
- P : MlDsaParams ,
742
- {
743
- self . raw_verify_internal ( & [ Mp ] , sigma)
744
- }
745
-
746
- fn raw_verify_internal ( & self , Mp : & [ & [ & [ u8 ] ] ] , sigma : & Signature < P > ) -> bool
779
+ pub fn verify_internal ( & self , M : & [ u8 ] , sigma : & Signature < P > ) -> bool
747
780
where
748
781
P : MlDsaParams ,
749
782
{
750
- // Compute the message representative
751
- let mu = message_representative ( & self . tr , Mp ) ;
783
+ let mu = MuBuilder :: internal ( & self . tr , & [ M ] ) ;
752
784
self . raw_verify_mu ( & mu, sigma)
753
785
}
754
786
@@ -793,8 +825,8 @@ impl<P: MlDsaParams> VerifyingKey<P> {
793
825
return false ;
794
826
}
795
827
796
- let Mp = & [ & [ & [ 0 ] , & [ Truncate :: truncate ( ctx . len ( ) ) ] , ctx] , M ] ;
797
- self . raw_verify_internal ( Mp , sigma)
828
+ let mu = MuBuilder :: new ( & self . tr , ctx) . message ( M ) ;
829
+ self . verify_mu ( & mu , sigma)
798
830
}
799
831
800
832
fn encode_internal ( rho : & B32 , t1 : & Vector < P :: K > ) -> EncodedVerifyingKey < P > {
@@ -837,9 +869,9 @@ impl<P: MlDsaParams> DigestVerifier<Shake256, Signature<P>> for VerifyingKey<P>
837
869
f : F ,
838
870
signature : & Signature < P > ,
839
871
) -> Result < ( ) , Error > {
840
- let mut digest = Shake256 :: default ( ) . chain ( self . tr ) . chain ( [ 0 , 0 ] ) ;
841
- f ( & mut digest ) ?;
842
- let mu = H :: pre_digest ( digest ) . squeeze_new ( ) ;
872
+ let mut mu = MuBuilder :: new ( & self . tr , & [ ] ) ;
873
+ f ( mu . as_mut ( ) ) ?;
874
+ let mu = mu . finish ( ) ;
843
875
844
876
self . raw_verify_mu ( & mu, signature)
845
877
. then_some ( ( ) )
@@ -1060,6 +1092,7 @@ where
1060
1092
mod test {
1061
1093
use super :: * ;
1062
1094
use crate :: param:: * ;
1095
+ use signature:: digest:: Update ;
1063
1096
1064
1097
#[ test]
1065
1098
fn output_sizes ( ) {
@@ -1142,7 +1175,7 @@ mod test {
1142
1175
let rnd = Array ( [ 0u8 ; 32 ] ) ;
1143
1176
let sig = sk. sign_internal ( & [ M ] , & rnd) ;
1144
1177
1145
- assert ! ( vk. verify_internal( & [ M ] , & sig) ) ;
1178
+ assert ! ( vk. verify_internal( M , & sig) ) ;
1146
1179
}
1147
1180
1148
1181
#[ test]
@@ -1179,7 +1212,7 @@ mod test {
1179
1212
let sig_dec = Signature :: < P > :: decode ( & sig_enc) . unwrap ( ) ;
1180
1213
1181
1214
assert_eq ! ( sig_dec, sig) ;
1182
- assert ! ( vk. verify_internal( & [ M ] , & sig_dec) ) ;
1215
+ assert ! ( vk. verify_internal( M , & sig_dec) ) ;
1183
1216
}
1184
1217
}
1185
1218
@@ -1202,7 +1235,7 @@ mod test {
1202
1235
1203
1236
let M = b"Hello world" ;
1204
1237
let rnd = Array ( [ 0u8 ; 32 ] ) ;
1205
- let mu = message_representative ( & sk. tr , & [ & [ M ] ] ) ;
1238
+ let mu = MuBuilder :: internal ( & sk. tr , & [ M ] ) ;
1206
1239
let sig = sk. raw_sign_mu ( & mu, & rnd) ;
1207
1240
1208
1241
assert ! ( vk. raw_verify_mu( & mu, & sig) ) ;
@@ -1224,10 +1257,10 @@ mod test {
1224
1257
1225
1258
let M = b"Hello world" ;
1226
1259
let rnd = Array ( [ 0u8 ; 32 ] ) ;
1227
- let mu = message_representative ( & sk. tr , & [ & [ M ] ] ) ;
1260
+ let mu = MuBuilder :: internal ( & sk. tr , & [ M ] ) ;
1228
1261
let sig = sk. raw_sign_mu ( & mu, & rnd) ;
1229
1262
1230
- assert ! ( vk. verify_internal( & [ M ] , & sig) ) ;
1263
+ assert ! ( vk. verify_internal( M , & sig) ) ;
1231
1264
}
1232
1265
sign_mu_verify_internal :: < MlDsa44 > ( ) ;
1233
1266
sign_mu_verify_internal :: < MlDsa65 > ( ) ;
@@ -1246,7 +1279,7 @@ mod test {
1246
1279
1247
1280
let M = b"Hello world" ;
1248
1281
let rnd = Array ( [ 0u8 ; 32 ] ) ;
1249
- let mu = message_representative ( & sk. tr , & [ & [ M ] ] ) ;
1282
+ let mu = MuBuilder :: internal ( & sk. tr , & [ M ] ) ;
1250
1283
let sig = sk. sign_internal ( & [ M ] , & rnd) ;
1251
1284
1252
1285
assert ! ( vk. raw_verify_mu( & mu, & sig) ) ;
0 commit comments