@@ -42,6 +42,11 @@ use crate::{
42
42
signing:: SigningKey ,
43
43
} ;
44
44
45
+ #[ cfg( feature = "hazmat" ) ]
46
+ mod stream;
47
+ #[ cfg( feature = "hazmat" ) ]
48
+ pub use self :: stream:: StreamVerifier ;
49
+
45
50
/// An ed25519 public key.
46
51
///
47
52
/// # Note
@@ -186,58 +191,8 @@ impl VerifyingKey {
186
191
self . point . is_small_order ( )
187
192
}
188
193
189
- // A helper function that computes `H(R || A || M)` where `H` is the 512-bit hash function
190
- // given by `CtxDigest` (this is SHA-512 in spec-compliant Ed25519). If `context.is_some()`,
191
- // this does the prehashed variant of the computation using its contents.
192
- #[ allow( non_snake_case) ]
193
- fn compute_challenge < CtxDigest > (
194
- context : Option < & [ u8 ] > ,
195
- R : & CompressedEdwardsY ,
196
- A : & CompressedEdwardsY ,
197
- M : & [ u8 ] ,
198
- ) -> Scalar
199
- where
200
- CtxDigest : Digest < OutputSize = U64 > ,
201
- {
202
- let mut h = CtxDigest :: new ( ) ;
203
- if let Some ( c) = context {
204
- h. update ( b"SigEd25519 no Ed25519 collisions" ) ;
205
- h. update ( [ 1 ] ) ; // Ed25519ph
206
- h. update ( [ c. len ( ) as u8 ] ) ;
207
- h. update ( c) ;
208
- }
209
- h. update ( R . as_bytes ( ) ) ;
210
- h. update ( A . as_bytes ( ) ) ;
211
- h. update ( M ) ;
212
-
213
- Scalar :: from_hash ( h)
214
- }
215
-
216
- // Helper function for verification. Computes the _expected_ R component of the signature. The
217
- // caller compares this to the real R component. If `context.is_some()`, this does the
218
- // prehashed variant of the computation using its contents.
219
- // Note that this returns the compressed form of R and the caller does a byte comparison. This
220
- // means that all our verification functions do not accept non-canonically encoded R values.
221
- // See the validation criteria blog post for more details:
222
- // https://hdevalence.ca/blog/2020-10-04-its-25519am
223
- #[ allow( non_snake_case) ]
224
- fn recompute_R < CtxDigest > (
225
- & self ,
226
- context : Option < & [ u8 ] > ,
227
- signature : & InternalSignature ,
228
- M : & [ u8 ] ,
229
- ) -> CompressedEdwardsY
230
- where
231
- CtxDigest : Digest < OutputSize = U64 > ,
232
- {
233
- let k = Self :: compute_challenge :: < CtxDigest > ( context, & signature. R , & self . compressed , M ) ;
234
- let minus_A: EdwardsPoint = -self . point ;
235
- // Recall the (non-batched) verification equation: -[k]A + [s]B = R
236
- EdwardsPoint :: vartime_double_scalar_mul_basepoint ( & k, & ( minus_A) , & signature. s ) . compress ( )
237
- }
238
-
239
194
/// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R values. (see
240
- /// [`Self::recompute_R `]). `CtxDigest` is the digest used to calculate the pseudorandomness
195
+ /// [`Self::RCompute `]). `CtxDigest` is the digest used to calculate the pseudorandomness
241
196
/// needed for signing. According to the spec, `CtxDigest = Sha512`.
242
197
///
243
198
/// This definition is loose in its parameters so that end-users of the `hazmat` module can
@@ -253,7 +208,7 @@ impl VerifyingKey {
253
208
{
254
209
let signature = InternalSignature :: try_from ( signature) ?;
255
210
256
- let expected_R = self . recompute_R :: < CtxDigest > ( None , & signature, message) ;
211
+ let expected_R = RCompute :: < CtxDigest > :: compute ( self , signature, None , message) ;
257
212
if expected_R == signature. R {
258
213
Ok ( ( ) )
259
214
} else {
@@ -289,7 +244,8 @@ impl VerifyingKey {
289
244
) ;
290
245
291
246
let message = prehashed_message. finalize ( ) ;
292
- let expected_R = self . recompute_R :: < CtxDigest > ( Some ( ctx) , & signature, & message) ;
247
+
248
+ let expected_R = RCompute :: < CtxDigest > :: compute ( self , signature, Some ( ctx) , & message) ;
293
249
294
250
if expected_R == signature. R {
295
251
Ok ( ( ) )
@@ -415,16 +371,30 @@ impl VerifyingKey {
415
371
return Err ( InternalError :: Verify . into ( ) ) ;
416
372
}
417
373
418
- let expected_R = self . recompute_R :: < Sha512 > ( None , & signature, message) ;
374
+ let expected_R = RCompute :: < Sha512 > :: compute ( self , signature, None , message) ;
419
375
if expected_R == signature. R {
420
376
Ok ( ( ) )
421
377
} else {
422
378
Err ( InternalError :: Verify . into ( ) )
423
379
}
424
380
}
425
381
382
+ /// Constructs stream verifier with candidate `signature`.
383
+ ///
384
+ /// Useful for cases where the whole message is not available all at once, allowing the
385
+ /// internal signature state to be updated incrementally and verified at the end. In some cases,
386
+ /// this will reduce the need for additional allocations.
387
+ #[ cfg( feature = "hazmat" ) ]
388
+ pub fn verify_stream (
389
+ & self ,
390
+ signature : & ed25519:: Signature ,
391
+ ) -> Result < StreamVerifier , SignatureError > {
392
+ let signature = InternalSignature :: try_from ( signature) ?;
393
+ Ok ( StreamVerifier :: new ( * self , signature) )
394
+ }
395
+
426
396
/// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm,
427
- /// using strict signture checking as defined by [`Self::verify_strict`].
397
+ /// using strict signature checking as defined by [`Self::verify_strict`].
428
398
///
429
399
/// # Inputs
430
400
///
@@ -477,7 +447,7 @@ impl VerifyingKey {
477
447
}
478
448
479
449
let message = prehashed_message. finalize ( ) ;
480
- let expected_R = self . recompute_R :: < Sha512 > ( Some ( ctx) , & signature , & message) ;
450
+ let expected_R = RCompute :: < Sha512 > :: compute ( self , signature , Some ( ctx) , & message) ;
481
451
482
452
if expected_R == signature. R {
483
453
Ok ( ( ) )
@@ -511,6 +481,79 @@ impl VerifyingKey {
511
481
}
512
482
}
513
483
484
+ /// Helper for verification. Computes the _expected_ R component of the signature. The
485
+ /// caller compares this to the real R component.
486
+ /// This computes `H(R || A || M)` where `H` is the 512-bit hash function
487
+ /// given by `CtxDigest` (this is SHA-512 in spec-compliant Ed25519).
488
+ ///
489
+ /// For pre-hashed variants a `h` with the context already included can be provided.
490
+ /// Note that this returns the compressed form of R and the caller does a byte comparison. This
491
+ /// means that all our verification functions do not accept non-canonically encoded R values.
492
+ /// See the validation criteria blog post for more details:
493
+ /// https://hdevalence.ca/blog/2020-10-04-its-25519am
494
+ pub ( crate ) struct RCompute < CtxDigest > {
495
+ key : VerifyingKey ,
496
+ signature : InternalSignature ,
497
+ h : CtxDigest ,
498
+ }
499
+
500
+ #[ allow( non_snake_case) ]
501
+ impl < CtxDigest > RCompute < CtxDigest >
502
+ where
503
+ CtxDigest : Digest < OutputSize = U64 > ,
504
+ {
505
+ /// If `prehash_ctx.is_some()`, this does the prehashed variant of the computation using its
506
+ /// contents.
507
+ pub ( crate ) fn compute (
508
+ key : & VerifyingKey ,
509
+ signature : InternalSignature ,
510
+ prehash_ctx : Option < & [ u8 ] > ,
511
+ message : & [ u8 ] ,
512
+ ) -> CompressedEdwardsY {
513
+ let mut c = Self :: new ( key, signature, prehash_ctx) ;
514
+ c. update ( message) ;
515
+ c. finish ( )
516
+ }
517
+
518
+ pub ( crate ) fn new (
519
+ key : & VerifyingKey ,
520
+ signature : InternalSignature ,
521
+ prehash_ctx : Option < & [ u8 ] > ,
522
+ ) -> Self {
523
+ let R = & signature. R ;
524
+ let A = & key. compressed ;
525
+
526
+ let mut h = CtxDigest :: new ( ) ;
527
+ if let Some ( c) = prehash_ctx {
528
+ h. update ( b"SigEd25519 no Ed25519 collisions" ) ;
529
+ h. update ( [ 1 ] ) ; // Ed25519ph
530
+ h. update ( [ c. len ( ) as u8 ] ) ;
531
+ h. update ( c) ;
532
+ }
533
+
534
+ h. update ( R . as_bytes ( ) ) ;
535
+ h. update ( A . as_bytes ( ) ) ;
536
+ Self {
537
+ key : * key,
538
+ signature,
539
+ h,
540
+ }
541
+ }
542
+
543
+ pub ( crate ) fn update ( & mut self , m : & [ u8 ] ) {
544
+ self . h . update ( m)
545
+ }
546
+
547
+ pub ( crate ) fn finish ( self ) -> CompressedEdwardsY {
548
+ let k = Scalar :: from_hash ( self . h ) ;
549
+
550
+ let minus_A: EdwardsPoint = -self . key . point ;
551
+ // Recall the (non-batched) verification equation: -[k]A + [s]B = R
552
+ EdwardsPoint :: vartime_double_scalar_mul_basepoint ( & k, & ( minus_A) , & self . signature . s )
553
+ . compress ( )
554
+ }
555
+ }
556
+
514
557
impl Verifier < ed25519:: Signature > for VerifyingKey {
515
558
/// Verify a signature on a message with this keypair's public key.
516
559
///
0 commit comments