Skip to content

Instantly share code, notes, and snippets.

@jedisct1
Created July 14, 2023 09:13

Revisions

  1. jedisct1 created this gist Jul 14, 2023.
    51 changes: 51 additions & 0 deletions foo.patch
    Original 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];
    }
    };