authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2021-04-24 14:55:01+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-24 13:35:36-04:00
log7a793a9b9d38adc6800834d2dc11a1037b1aef4d
treefbeadcb17b07399c000d1723706d48ca3a6c4e26
parent29e5e98eed2161c37edbce41b96c2999eb1f3177

ed25519: use double-base multiplication for signature verification

This makes single signature verification about 60% faster. Also check that R is not the identity point.

2 files changed, 48 insertions(+), 6 deletions(-)

lib/std/crypto/25519/ed25519.zig+5-4
...@@ -129,6 +129,7 @@ pub const Ed25519 = struct {...@@ -129,6 +129,7 @@ pub const Ed25519 = struct {
129 try a.rejectIdentity();129 try a.rejectIdentity();
130 try Curve.rejectNonCanonical(r.*);130 try Curve.rejectNonCanonical(r.*);
131 const expected_r = try Curve.fromBytes(r.*);131 const expected_r = try Curve.fromBytes(r.*);
132 try expected_r.rejectIdentity();
132133
133 var h = Sha512.init(.{});134 var h = Sha512.init(.{});
134 h.update(r);135 h.update(r);
...@@ -138,8 +139,7 @@ pub const Ed25519 = struct {...@@ -138,8 +139,7 @@ pub const Ed25519 = struct {
138 h.final(&hram64);139 h.final(&hram64);
139 const hram = Curve.scalar.reduce64(hram64);140 const hram = Curve.scalar.reduce64(hram64);
140141
141 const ah = try a.neg().mulPublic(hram);142 const sb_ah = try Curve.basePoint.mulDoubleBasePublic(s.*, a.neg(), hram);
142 const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah);
143 if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {143 if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {
144 return error.SignatureVerificationFailed;144 return error.SignatureVerificationFailed;
145 } else |_| {}145 } else |_| {}
...@@ -168,6 +168,7 @@ pub const Ed25519 = struct {...@@ -168,6 +168,7 @@ pub const Ed25519 = struct {
168 try a.rejectIdentity();168 try a.rejectIdentity();
169 try Curve.rejectNonCanonical(r.*);169 try Curve.rejectNonCanonical(r.*);
170 const expected_r = try Curve.fromBytes(r.*);170 const expected_r = try Curve.fromBytes(r.*);
171 try expected_r.rejectIdentity();
171 expected_r_batch[i] = expected_r;172 expected_r_batch[i] = expected_r;
172 r_batch[i] = r.*;173 r_batch[i] = r.*;
173 s_batch[i] = s.*;174 s_batch[i] = s.*;
...@@ -324,13 +325,13 @@ test "ed25519 test vectors" {...@@ -324,13 +325,13 @@ test "ed25519 test vectors" {
324 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",325 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
325 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",326 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
326 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",327 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
327 .expected = error.SignatureVerificationFailed, // 8 - non-canonical R328 .expected = error.IdentityElement, // 8 - non-canonical R
328 },329 },
329 Vec{330 Vec{
330 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",331 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
331 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",332 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
332 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",333 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
333 .expected = null, // 9 - non-canonical R334 .expected = error.IdentityElement, // 9 - non-canonical R
334 },335 },
335 Vec{336 Vec{
336 .msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",337 .msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
lib/std/crypto/25519/edwards25519.zig+43-2
...@@ -238,6 +238,11 @@ pub const Edwards25519 = struct {...@@ -238,6 +238,11 @@ pub const Edwards25519 = struct {
238 break :pc precompute(Edwards25519.basePoint, 15);238 break :pc precompute(Edwards25519.basePoint, 15);
239 };239 };
240240
241 const basePointPc8 = comptime pc: {
242 @setEvalBranchQuota(10000);
243 break :pc precompute(Edwards25519.basePoint, 8);
244 };
245
241 /// Multiply an Edwards25519 point by a scalar without clamping it.246 /// Multiply an Edwards25519 point by a scalar without clamping it.
242 /// Return error.WeakPublicKey if the base generates a small-order group,247 /// Return error.WeakPublicKey if the base generates a small-order group,
243 /// and error.IdentityElement if the result is the identity element.248 /// and error.IdentityElement if the result is the identity element.
...@@ -262,14 +267,50 @@ pub const Edwards25519 = struct {...@@ -262,14 +267,50 @@ pub const Edwards25519 = struct {
262 }267 }
263 }268 }
264269
270 /// Double-base multiplication of public parameters - Compute (p1*s1)+(p2*s2) *IN VARIABLE TIME*
271 /// This can be used for signature verification.
272 pub fn mulDoubleBasePublic(p1: Edwards25519, s1: [32]u8, p2: Edwards25519, s2: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
273 const pc1 = if (p1.is_base) basePointPc8 else pc: {
274 const xpc = precompute(p1, 8);
275 xpc[4].rejectIdentity() catch return error.WeakPublicKey;
276 break :pc xpc;
277 };
278 const pc2 = if (p2.is_base) basePointPc8 else pc: {
279 const xpc = precompute(p2, 8);
280 xpc[4].rejectIdentity() catch return error.WeakPublicKey;
281 break :pc xpc;
282 };
283 const e1 = nonAdjacentForm(s1);
284 const e2 = nonAdjacentForm(s2);
285 var q = Edwards25519.identityElement;
286 var pos: usize = 2 * 32 - 1;
287 while (true) : (pos -= 1) {
288 const slot1 = e1[pos];
289 if (slot1 > 0) {
290 q = q.add(pc1[@intCast(usize, slot1)]);
291 } else if (slot1 < 0) {
292 q = q.sub(pc1[@intCast(usize, -slot1)]);
293 }
294 const slot2 = e2[pos];
295 if (slot2 > 0) {
296 q = q.add(pc2[@intCast(usize, slot2)]);
297 } else if (slot2 < 0) {
298 q = q.sub(pc2[@intCast(usize, -slot2)]);
299 }
300 if (pos == 0) break;
301 q = q.dbl().dbl().dbl().dbl();
302 }
303 try q.rejectIdentity();
304 return q;
305 }
306
265 /// Multiscalar multiplication *IN VARIABLE TIME* for public data307 /// Multiscalar multiplication *IN VARIABLE TIME* for public data
266 /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually308 /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually
267 pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {309 pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
268 var pcs: [count][9]Edwards25519 = undefined;310 var pcs: [count][9]Edwards25519 = undefined;
269 for (ps) |p, i| {311 for (ps) |p, i| {
270 if (p.is_base) {312 if (p.is_base) {
271 @setEvalBranchQuota(10000);313 pcs[i] = basePointPc8;
272 pcs[i] = comptime precompute(Edwards25519.basePoint, 8);
273 } else {314 } else {
274 pcs[i] = precompute(p, 8);315 pcs[i] = precompute(p, 8);
275 pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey;316 pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey;