mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
crypto.ecdsa: add the ability to sign/verify prehashed messages (#23607)
This commit is contained in:
parent
86d3546184
commit
f01833e03e
1 changed files with 65 additions and 10 deletions
|
|
@ -106,6 +106,14 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||||
try st.verify();
|
try st.verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify the signature against a pre-hashed message and public key.
|
||||||
|
/// The message must have already been hashed using the scheme's hash function.
|
||||||
|
/// Returns SignatureVerificationError if the signature is invalid for the given message and key.
|
||||||
|
pub fn verifyPrehashed(sig: Signature, msg_hash: [Hash.digest_length]u8, public_key: PublicKey) VerifyError!void {
|
||||||
|
var st = try sig.verifier(public_key);
|
||||||
|
return st.verifyPrehashed(msg_hash);
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the raw signature (r, s) in big-endian format.
|
/// Return the raw signature (r, s) in big-endian format.
|
||||||
pub fn toBytes(sig: Signature) [encoded_length]u8 {
|
pub fn toBytes(sig: Signature) [encoded_length]u8 {
|
||||||
var bytes: [encoded_length]u8 = undefined;
|
var bytes: [encoded_length]u8 = undefined;
|
||||||
|
|
@ -203,18 +211,16 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||||
self.h.update(data);
|
self.h.update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a signature over the entire message.
|
/// Compute a signature over a hash.
|
||||||
pub fn finalize(self: *Signer) (IdentityElementError || NonCanonicalError)!Signature {
|
fn finalizePrehashed(self: *Signer, msg_hash: [Hash.digest_length]u8) (IdentityElementError || NonCanonicalError)!Signature {
|
||||||
const scalar_encoded_length = Curve.scalar.encoded_length;
|
const scalar_encoded_length = Curve.scalar.encoded_length;
|
||||||
const h_len = @max(Hash.digest_length, scalar_encoded_length);
|
const h_len = @max(Hash.digest_length, scalar_encoded_length);
|
||||||
var h: [h_len]u8 = [_]u8{0} ** h_len;
|
var h: [h_len]u8 = [_]u8{0} ** (h_len - Hash.digest_length) ++ msg_hash;
|
||||||
const h_slice = h[h_len - Hash.digest_length .. h_len];
|
|
||||||
self.h.final(h_slice);
|
|
||||||
|
|
||||||
std.debug.assert(h.len >= scalar_encoded_length);
|
std.debug.assert(h.len >= scalar_encoded_length);
|
||||||
const z = reduceToScalar(scalar_encoded_length, h[0..scalar_encoded_length].*);
|
const z = reduceToScalar(scalar_encoded_length, h[0..scalar_encoded_length].*);
|
||||||
|
|
||||||
const k = deterministicScalar(h_slice.*, self.secret_key.bytes, self.noise);
|
const k = deterministicScalar(msg_hash, self.secret_key.bytes, self.noise);
|
||||||
|
|
||||||
const p = try Curve.basePoint.mul(k.toBytes(.big), .big);
|
const p = try Curve.basePoint.mul(k.toBytes(.big), .big);
|
||||||
const xs = p.affineCoordinates().x.toBytes(.big);
|
const xs = p.affineCoordinates().x.toBytes(.big);
|
||||||
|
|
@ -228,6 +234,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||||
|
|
||||||
return Signature{ .r = r.toBytes(.big), .s = s.toBytes(.big) };
|
return Signature{ .r = r.toBytes(.big), .s = s.toBytes(.big) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute a signature over the entire message.
|
||||||
|
pub fn finalize(self: *Signer) (IdentityElementError || NonCanonicalError)!Signature {
|
||||||
|
var h_slice: [Hash.digest_length]u8 = undefined;
|
||||||
|
self.h.final(&h_slice);
|
||||||
|
return self.finalizePrehashed(h_slice);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A Verifier is used to incrementally verify a signature.
|
/// A Verifier is used to incrementally verify a signature.
|
||||||
|
|
@ -261,12 +274,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||||
pub const VerifyError = IdentityElementError || NonCanonicalError ||
|
pub const VerifyError = IdentityElementError || NonCanonicalError ||
|
||||||
SignatureVerificationError;
|
SignatureVerificationError;
|
||||||
|
|
||||||
/// Verify that the signature is valid for the entire message.
|
/// Verify that the signature is valid for the hash.
|
||||||
pub fn verify(self: *Verifier) VerifyError!void {
|
fn verifyPrehashed(self: *Verifier, msg_hash: [Hash.digest_length]u8) VerifyError!void {
|
||||||
const ht = Curve.scalar.encoded_length;
|
const ht = Curve.scalar.encoded_length;
|
||||||
const h_len = @max(Hash.digest_length, ht);
|
const h_len = @max(Hash.digest_length, ht);
|
||||||
var h: [h_len]u8 = [_]u8{0} ** h_len;
|
var h: [h_len]u8 = [_]u8{0} ** (h_len - Hash.digest_length) ++ msg_hash;
|
||||||
self.h.final(h[h_len - Hash.digest_length .. h_len]);
|
|
||||||
|
|
||||||
const z = reduceToScalar(ht, h[0..ht].*);
|
const z = reduceToScalar(ht, h[0..ht].*);
|
||||||
if (z.isZero()) {
|
if (z.isZero()) {
|
||||||
|
|
@ -284,6 +296,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||||
return error.SignatureVerificationFailed;
|
return error.SignatureVerificationFailed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify that the signature is valid for the entire message.
|
||||||
|
pub fn verify(self: *Verifier) VerifyError!void {
|
||||||
|
var h_slice: [Hash.digest_length]u8 = undefined;
|
||||||
|
self.h.final(&h_slice);
|
||||||
|
return self.verifyPrehashed(h_slice);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An ECDSA key pair.
|
/// An ECDSA key pair.
|
||||||
|
|
@ -334,6 +353,14 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
||||||
return st.finalize();
|
return st.finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign a pre-hashed message using the key pair.
|
||||||
|
/// The message must have already been hashed using the scheme's hash function.
|
||||||
|
/// The noise parameter can be null for deterministic signatures, or random bytes for enhanced security against fault attacks.
|
||||||
|
pub fn signPrehashed(key_pair: KeyPair, msg_hash: [Hash.digest_length]u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError)!Signature {
|
||||||
|
var st = try key_pair.signer(noise);
|
||||||
|
return st.finalizePrehashed(msg_hash);
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a Signer, that can be used for incremental signature verification.
|
/// Create a Signer, that can be used for incremental signature verification.
|
||||||
pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) !Signer {
|
pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) !Signer {
|
||||||
return Signer.init(key_pair.secret_key, noise);
|
return Signer.init(key_pair.secret_key, noise);
|
||||||
|
|
@ -475,6 +502,34 @@ test "Verifying a existing signature with EcdsaP384Sha256" {
|
||||||
try sig.verify(&msg, kp.public_key);
|
try sig.verify(&msg, kp.public_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Prehashed message operations" {
|
||||||
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||||
|
|
||||||
|
const Scheme = EcdsaP256Sha256;
|
||||||
|
const kp = Scheme.KeyPair.generate();
|
||||||
|
const msg = "test message for prehashed signing";
|
||||||
|
|
||||||
|
const Hash = crypto.hash.sha2.Sha256;
|
||||||
|
var msg_hash: [Hash.digest_length]u8 = undefined;
|
||||||
|
Hash.hash(msg, &msg_hash, .{});
|
||||||
|
|
||||||
|
const sig = try kp.signPrehashed(msg_hash, null);
|
||||||
|
try sig.verifyPrehashed(msg_hash, kp.public_key);
|
||||||
|
|
||||||
|
var bad_hash = msg_hash;
|
||||||
|
bad_hash[0] ^= 1;
|
||||||
|
try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key));
|
||||||
|
|
||||||
|
var noise: [Scheme.noise_length]u8 = undefined;
|
||||||
|
crypto.random.bytes(&noise);
|
||||||
|
const sig_with_noise = try kp.signPrehashed(msg_hash, noise);
|
||||||
|
try sig_with_noise.verifyPrehashed(msg_hash, kp.public_key);
|
||||||
|
|
||||||
|
const regular_sig = try kp.sign(msg, null);
|
||||||
|
try regular_sig.verifyPrehashed(msg_hash, kp.public_key);
|
||||||
|
try sig.verify(msg, kp.public_key);
|
||||||
|
}
|
||||||
|
|
||||||
const TestVector = struct {
|
const TestVector = struct {
|
||||||
key: []const u8,
|
key: []const u8,
|
||||||
msg: []const u8,
|
msg: []const u8,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue