@@ -106,6 +106,14 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
106
106
try st .verify ();
107
107
}
108
108
109
+ /// Verify the signature against a pre-hashed message and public key.
110
+ /// The message must have already been hashed using the scheme's hash function.
111
+ /// Returns SignatureVerificationError if the signature is invalid for the given message and key.
112
+ pub fn verifyPrehashed (sig : Signature , msg_hash : [Hash .digest_length ]u8 , public_key : PublicKey ) VerifyError ! void {
113
+ var st = try sig .verifier (public_key );
114
+ return st .verifyPrehashed (msg_hash );
115
+ }
116
+
109
117
/// Return the raw signature (r, s) in big-endian format.
110
118
pub fn toBytes (sig : Signature ) [encoded_length ]u8 {
111
119
var bytes : [encoded_length ]u8 = undefined ;
@@ -203,18 +211,16 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
203
211
self .h .update (data );
204
212
}
205
213
206
- /// Compute a signature over the entire message .
207
- pub fn finalize (self : * Signer ) (IdentityElementError || NonCanonicalError )! Signature {
214
+ /// Compute a signature over a hash .
215
+ fn finalizePrehashed (self : * Signer , msg_hash : [ Hash . digest_length ] u8 ) (IdentityElementError || NonCanonicalError )! Signature {
208
216
const scalar_encoded_length = Curve .scalar .encoded_length ;
209
217
const h_len = @max (Hash .digest_length , scalar_encoded_length );
210
- var h : [h_len ]u8 = [_ ]u8 {0 } ** h_len ;
211
- const h_slice = h [h_len - Hash .digest_length .. h_len ];
212
- self .h .final (h_slice );
218
+ var h : [h_len ]u8 = [_ ]u8 {0 } ** (h_len - Hash .digest_length ) ++ msg_hash ;
213
219
214
220
std .debug .assert (h .len >= scalar_encoded_length );
215
221
const z = reduceToScalar (scalar_encoded_length , h [0.. scalar_encoded_length ].* );
216
222
217
- const k = deterministicScalar (h_slice .* , self .secret_key .bytes , self .noise );
223
+ const k = deterministicScalar (msg_hash , self .secret_key .bytes , self .noise );
218
224
219
225
const p = try Curve .basePoint .mul (k .toBytes (.big ), .big );
220
226
const xs = p .affineCoordinates ().x .toBytes (.big );
@@ -228,6 +234,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
228
234
229
235
return Signature { .r = r .toBytes (.big ), .s = s .toBytes (.big ) };
230
236
}
237
+
238
+ /// Compute a signature over the entire message.
239
+ pub fn finalize (self : * Signer ) (IdentityElementError || NonCanonicalError )! Signature {
240
+ var h_slice : [Hash .digest_length ]u8 = undefined ;
241
+ self .h .final (& h_slice );
242
+ return self .finalizePrehashed (h_slice );
243
+ }
231
244
};
232
245
233
246
/// A Verifier is used to incrementally verify a signature.
@@ -261,12 +274,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
261
274
pub const VerifyError = IdentityElementError || NonCanonicalError ||
262
275
SignatureVerificationError ;
263
276
264
- /// Verify that the signature is valid for the entire message .
265
- pub fn verify (self : * Verifier ) VerifyError ! void {
277
+ /// Verify that the signature is valid for the hash .
278
+ fn verifyPrehashed (self : * Verifier , msg_hash : [ Hash . digest_length ] u8 ) VerifyError ! void {
266
279
const ht = Curve .scalar .encoded_length ;
267
280
const h_len = @max (Hash .digest_length , ht );
268
- var h : [h_len ]u8 = [_ ]u8 {0 } ** h_len ;
269
- self .h .final (h [h_len - Hash .digest_length .. h_len ]);
281
+ var h : [h_len ]u8 = [_ ]u8 {0 } ** (h_len - Hash .digest_length ) ++ msg_hash ;
270
282
271
283
const z = reduceToScalar (ht , h [0.. ht ].* );
272
284
if (z .isZero ()) {
@@ -284,6 +296,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
284
296
return error .SignatureVerificationFailed ;
285
297
}
286
298
}
299
+
300
+ /// Verify that the signature is valid for the entire message.
301
+ pub fn verify (self : * Verifier ) VerifyError ! void {
302
+ var h_slice : [Hash .digest_length ]u8 = undefined ;
303
+ self .h .final (& h_slice );
304
+ return self .verifyPrehashed (h_slice );
305
+ }
287
306
};
288
307
289
308
/// An ECDSA key pair.
@@ -334,6 +353,14 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
334
353
return st .finalize ();
335
354
}
336
355
356
+ /// Sign a pre-hashed message using the key pair.
357
+ /// The message must have already been hashed using the scheme's hash function.
358
+ /// The noise parameter can be null for deterministic signatures, or random bytes for enhanced security against fault attacks.
359
+ pub fn signPrehashed (key_pair : KeyPair , msg_hash : [Hash .digest_length ]u8 , noise : ? [noise_length ]u8 ) (IdentityElementError || NonCanonicalError )! Signature {
360
+ var st = try key_pair .signer (noise );
361
+ return st .finalizePrehashed (msg_hash );
362
+ }
363
+
337
364
/// Create a Signer, that can be used for incremental signature verification.
338
365
pub fn signer (key_pair : KeyPair , noise : ? [noise_length ]u8 ) ! Signer {
339
366
return Signer .init (key_pair .secret_key , noise );
@@ -475,6 +502,34 @@ test "Verifying a existing signature with EcdsaP384Sha256" {
475
502
try sig .verify (& msg , kp .public_key );
476
503
}
477
504
505
+ test "Prehashed message operations" {
506
+ if (builtin .zig_backend == .stage2_c ) return error .SkipZigTest ;
507
+
508
+ const Scheme = EcdsaP256Sha256 ;
509
+ const kp = Scheme .KeyPair .generate ();
510
+ const msg = "test message for prehashed signing" ;
511
+
512
+ const Hash = crypto .hash .sha2 .Sha256 ;
513
+ var msg_hash : [Hash .digest_length ]u8 = undefined ;
514
+ Hash .hash (msg , & msg_hash , .{});
515
+
516
+ const sig = try kp .signPrehashed (msg_hash , null );
517
+ try sig .verifyPrehashed (msg_hash , kp .public_key );
518
+
519
+ var bad_hash = msg_hash ;
520
+ bad_hash [0 ] ^= 1 ;
521
+ try testing .expectError (error .SignatureVerificationFailed , sig .verifyPrehashed (bad_hash , kp .public_key ));
522
+
523
+ var noise : [Scheme .noise_length ]u8 = undefined ;
524
+ crypto .random .bytes (& noise );
525
+ const sig_with_noise = try kp .signPrehashed (msg_hash , noise );
526
+ try sig_with_noise .verifyPrehashed (msg_hash , kp .public_key );
527
+
528
+ const regular_sig = try kp .sign (msg , null );
529
+ try regular_sig .verifyPrehashed (msg_hash , kp .public_key );
530
+ try sig .verify (msg , kp .public_key );
531
+ }
532
+
478
533
const TestVector = struct {
479
534
key : []const u8 ,
480
535
msg : []const u8 ,
0 commit comments