Created
July 14, 2023 09:13
Revisions
-
jedisct1 created this gist
Jul 14, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,51 @@ 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]; } };