Commit f3667e8a80

Frank Denis <github@pureftpd.org>
2020-10-15 16:39:39
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.
1 parent ab585c6
Changed files (3)
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;
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 |_| {}
     }
 };
 
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());
+}