| ... | ... | @@ -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, |