Skip to content

Instantly share code, notes, and snippets.

@jedisct1
Created July 14, 2023 09:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jedisct1/40e4d4d82af831bc13ffa4cee792856f to your computer and use it in GitHub Desktop.
Save jedisct1/40e4d4d82af831bc13ffa4cee792856f to your computer and use it in GitHub Desktop.
diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig
index 1a5335b07..b78cf6f6e 100644
--- a/lib/std/crypto/ecdsa.zig
+++ b/lib/std/crypto/ecdsa.zig
@@ -196,8 +196,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
self.h.update(data);
}
- /// Compute a signature over the entire message.
- pub fn finalize(self: *Signer) (IdentityElementError || NonCanonicalError)!Signature {
+ /// Recovery ID.
+ pub const RecoveryId = u2;
+
+ /// Compute a signature over the entire message, returning the signature as well as a recovery ID that can be used to recover the public key.
+ pub fn finalizeForPublicKeyRecovery(self: *Signer) (IdentityElementError || NonCanonicalError)!struct { Signature, RecoveryId } {
const scalar_encoded_length = Curve.scalar.encoded_length;
const h_len = @max(Hash.digest_length, scalar_encoded_length);
var h: [h_len]u8 = [_]u8{0} ** h_len;
@@ -210,7 +213,8 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
const k = deterministicScalar(h_slice.*, self.secret_key.bytes, self.noise);
const p = try Curve.basePoint.mul(k.toBytes(.Big), .Big);
- const xs = p.affineCoordinates().x.toBytes(.Big);
+ const p_affine = p.affineCoordinates();
+ const xs = p_affine.x.toBytes(.Big);
const r = reduceToScalar(Curve.Fe.encoded_length, xs);
if (r.isZero()) return error.IdentityElement;
@@ -219,7 +223,21 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
const s = k_inv.mul(zrs);
if (s.isZero()) return error.IdentityElement;
- return Signature{ .r = r.toBytes(.Big), .s = s.toBytes(.Big) };
+ const r_bytes = r.toBytes(.Big);
+
+ const x_is_canonical = crypto.utils.timingSafeEql(@TypeOf(xs), xs, r_bytes);
+ const y_is_odd = p_affine.y.isOdd();
+ const sig = Signature{ .r = r_bytes, .s = s.toBytes(.Big) };
+ var recovery_id = @as(RecoveryId, @as(u1, @bitCast(x_is_canonical)) ^ 1) << 1;
+ recovery_id |= @as(u1, @bitCast(y_is_odd));
+
+ return .{ sig, recovery_id };
+ }
+
+ /// Compute a signature over the entire message.
+ pub fn finalize(self: *Signer) (IdentityElementError || NonCanonicalError)!Signature {
+ const ret = try self.finalizeForPublicKeyRecovery();
+ return ret[0];
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment