From f3667e8a8056765c460aa3da1fd3ea655b54bf25 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 15 Oct 2020 16:39:39 +0200 Subject: [PATCH] std/crypto/25519: do cofactored ed25519 verification This is slightly slower but makes our verification function compatible with batch signatures. Which, in turn, makes blockchain people happy. And we want to make our users happy. Add convenience functions to substract edwards25519 points and to clear the cofactor. --- lib/std/crypto/25519/curve25519.zig | 5 +++++ lib/std/crypto/25519/ed25519.zig | 9 +++++---- lib/std/crypto/25519/edwards25519.zig | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index 94490f674c1db550e26471191dcde5461710a1ad..7f180eac2f78b26be4ecff5df7eac6045f57700c 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -39,6 +39,11 @@ pub const Curve25519 = struct { } } + /// Multiply a point by the cofactor + pub fn clearCofactor(p: Edwards25519) Edwards25519 { + return p.dbl().dbl().dbl(); + } + fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) !Curve25519 { var x1 = p.x; var x2 = Fe.one; diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 9993afb6d2dec072bf7df7fe654542b39e7ecd16..784ba62de09eab5a7c713158d6f3e6ea359a7224 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -97,6 +97,7 @@ pub const Ed25519 = struct { try Curve.rejectNonCanonical(public_key); const a = try Curve.fromBytes(public_key); try a.rejectIdentity(); + const expected_r = try Curve.fromBytes(r.*); var h = Sha512.init(.{}); h.update(r); @@ -106,11 +107,11 @@ pub const Ed25519 = struct { h.final(&hram64); const hram = Curve.scalar.reduce64(hram64); - const p = try a.neg().mul(hram); - const check = (try Curve.basePoint.mul(s.*)).add(p).toBytes(); - if (mem.eql(u8, &check, r) == false) { + const ah = try a.neg().mul(hram); + const sb_ah = (try Curve.basePoint.mul(s.*)).add(ah); + if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| { return error.InvalidSignature; - } + } else |_| {} } }; diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index 176e5337da0b4bc0cd91ed1762ea8a7cd1d5133e..f98774542fde6cac5b9afe92ceed55582003719f 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -73,6 +73,11 @@ pub const Edwards25519 = struct { } } + /// Multiply a point by the cofactor + pub fn clearCofactor(p: Edwards25519) Edwards25519 { + return p.dbl().dbl().dbl(); + } + /// Flip the sign of the X coordinate. pub inline fn neg(p: Edwards25519) Edwards25519 { return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() }; @@ -114,6 +119,11 @@ pub const Edwards25519 = struct { }; } + /// Substract two Edwards25519 points. + pub fn sub(p: Edwards25519, q: Edwards25519) Edwards25519 { + return p.add(q.neg()); + } + inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void { p.x.cMov(a.x, c); p.y.cMov(a.y, c); @@ -217,3 +227,17 @@ test "edwards25519 packing/unpacking" { std.testing.expectError(error.WeakPublicKey, small_p.mul(s)); } } + +test "edwards25519 point addition/substraction" { + var s1: [32]u8 = undefined; + var s2: [32]u8 = undefined; + try std.crypto.randomBytes(&s1); + try std.crypto.randomBytes(&s2); + const p = try Edwards25519.basePoint.clampedMul(s1); + const q = try Edwards25519.basePoint.clampedMul(s2); + const r = p.add(q).add(q).sub(q).sub(q); + try r.rejectIdentity(); + std.testing.expectError(error.IdentityElement, r.sub(p).rejectIdentity()); + std.testing.expectError(error.IdentityElement, p.sub(p).rejectIdentity()); + std.testing.expectError(error.IdentityElement, p.sub(q).add(q).sub(p).rejectIdentity()); +} -- 2.54.0