authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-23 00:58:53-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-23 00:58:53-04:00
log3af9025a1d4f99c0c36f605c4aa0ea7b52a97aca
treea0966a00bf966a248d221edde694f0d7040f19d0
parent392e6da8a30b75308dd52467a18ec1021a261478
parent047599928a8176ae32d0850638d67d812edd3508
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6719 from jedisct1/ed25519-batch

std/crypto/25519: add support for batch Ed25519 signature verification

4 files changed, 1054 insertions(+), 116 deletions(-)

lib/std/crypto/25519/ed25519.zig+199-2
......@@ -97,6 +97,7 @@ pub const Ed25519 = struct {
9797 try Curve.rejectNonCanonical(public_key);
9898 const a = try Curve.fromBytes(public_key);
9999 try a.rejectIdentity();
100 try Curve.rejectNonCanonical(r.*);
100101 const expected_r = try Curve.fromBytes(r.*);
101102
102103 var h = Sha512.init(.{});
......@@ -107,12 +108,79 @@ pub const Ed25519 = struct {
107108 h.final(&hram64);
108109 const hram = Curve.scalar.reduce64(hram64);
109110
110 const ah = try a.neg().mul(hram);
111 const sb_ah = (try Curve.basePoint.mul(s.*)).add(ah);
111 const ah = try a.neg().mulPublic(hram);
112 const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah);
112113 if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| {
113114 return error.InvalidSignature;
114115 } else |_| {}
115116 }
117
118 /// A (signature, message, public_key) tuple for batch verification
119 pub const BatchElement = struct {
120 sig: [signature_length]u8,
121 msg: []const u8,
122 public_key: [public_length]u8,
123 };
124
125 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
126 pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) !void {
127 var r_batch: [count][32]u8 = undefined;
128 var s_batch: [count][32]u8 = undefined;
129 var a_batch: [count]Curve = undefined;
130 var expected_r_batch: [count]Curve = undefined;
131
132 for (signature_batch) |signature, i| {
133 const r = signature.sig[0..32];
134 const s = signature.sig[32..64];
135 try Curve.scalar.rejectNonCanonical(s.*);
136 try Curve.rejectNonCanonical(signature.public_key);
137 const a = try Curve.fromBytes(signature.public_key);
138 try a.rejectIdentity();
139 try Curve.rejectNonCanonical(r.*);
140 const expected_r = try Curve.fromBytes(r.*);
141 expected_r_batch[i] = expected_r;
142 r_batch[i] = r.*;
143 s_batch[i] = s.*;
144 a_batch[i] = a;
145 }
146
147 var hram_batch: [count]Curve.scalar.CompressedScalar = undefined;
148 for (signature_batch) |signature, i| {
149 var h = Sha512.init(.{});
150 h.update(&r_batch[i]);
151 h.update(&signature.public_key);
152 h.update(signature.msg);
153 var hram64: [Sha512.digest_length]u8 = undefined;
154 h.final(&hram64);
155 hram_batch[i] = Curve.scalar.reduce64(hram64);
156 }
157
158 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
159 for (z_batch) |*z| {
160 try std.crypto.randomBytes(z[0..16]);
161 mem.set(u8, z[16..], 0);
162 }
163
164 var zs_sum = Curve.scalar.zero;
165 for (z_batch) |z, i| {
166 const zs = Curve.scalar.mul(z, s_batch[i]);
167 zs_sum = Curve.scalar.add(zs_sum, zs);
168 }
169 zs_sum = Curve.scalar.mul8(zs_sum);
170
171 var zhs: [count]Curve.scalar.CompressedScalar = undefined;
172 for (z_batch) |z, i| {
173 zhs[i] = Curve.scalar.mul(z, hram_batch[i]);
174 }
175
176 const zr = (try Curve.mulMulti(count, expected_r_batch, z_batch)).clearCofactor();
177 const zah = (try Curve.mulMulti(count, a_batch, zhs)).clearCofactor();
178
179 const zsb = try Curve.basePoint.mulPublic(zs_sum);
180 if (zr.add(zah).sub(zsb).rejectIdentity()) |_| {
181 return error.InvalidSignature;
182 } else |_| {}
183 }
116184};
117185
118186test "ed25519 key pair creation" {
......@@ -138,3 +206,132 @@ test "ed25519 signature" {
138206 try Ed25519.verify(sig, "test", public_key);
139207 std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key));
140208}
209
210test "ed25519 batch verification" {
211 var i: usize = 0;
212 while (i < 100) : (i += 1) {
213 var seed: [32]u8 = undefined;
214 try std.crypto.randomBytes(&seed);
215 const key_pair = try Ed25519.createKeyPair(seed);
216 var msg1: [32]u8 = undefined;
217 var msg2: [32]u8 = undefined;
218 try std.crypto.randomBytes(&msg1);
219 try std.crypto.randomBytes(&msg2);
220 const sig1 = try Ed25519.sign(&msg1, key_pair, null);
221 const sig2 = try Ed25519.sign(&msg2, key_pair, null);
222 const public_key = Ed25519.publicKey(key_pair);
223 var signature_batch = [_]Ed25519.BatchElement{
224 Ed25519.BatchElement{
225 .sig = sig1,
226 .msg = &msg1,
227 .public_key = public_key,
228 },
229 Ed25519.BatchElement{
230 .sig = sig2,
231 .msg = &msg2,
232 .public_key = public_key,
233 },
234 };
235 try Ed25519.verifyBatch(2, signature_batch);
236
237 signature_batch[1].sig = sig1;
238 std.testing.expectError(error.InvalidSignature, Ed25519.verifyBatch(signature_batch.len, signature_batch));
239 }
240}
241
242test "ed25519 test vectors" {
243 const Vec = struct {
244 msg_hex: *const [64:0]u8,
245 public_key_hex: *const [64:0]u8,
246 sig_hex: *const [128:0]u8,
247 expected: ?anyerror,
248 };
249
250 const entries = [_]Vec{
251 Vec{
252 .msg_hex = "8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6",
253 .public_key_hex = "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
254 .sig_hex = "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
255 .expected = error.WeakPublicKey, // 0
256 },
257 Vec{
258 .msg_hex = "9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
259 .public_key_hex = "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
260 .sig_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
261 .expected = error.WeakPublicKey, // 1
262 },
263 Vec{
264 .msg_hex = "aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab",
265 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
266 .sig_hex = "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e",
267 .expected = null, // 2 - small order R is acceptable
268 },
269 Vec{
270 .msg_hex = "9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
271 .public_key_hex = "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
272 .sig_hex = "9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009",
273 .expected = null, // 3 - mixed orders
274 },
275 Vec{
276 .msg_hex = "e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
277 .public_key_hex = "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
278 .sig_hex = "160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09",
279 .expected = null, // 4 - cofactored verification
280 },
281 Vec{
282 .msg_hex = "e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
283 .public_key_hex = "cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
284 .sig_hex = "21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405",
285 .expected = null, // 5 - cofactored verification
286 },
287 Vec{
288 .msg_hex = "85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
289 .public_key_hex = "442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
290 .sig_hex = "e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514",
291 .expected = error.NonCanonical, // 6 - S > L
292 },
293 Vec{
294 .msg_hex = "85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
295 .public_key_hex = "442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
296 .sig_hex = "8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a4734e74f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c2",
297 .expected = error.NonCanonical, // 7 - S >> L
298 },
299 Vec{
300 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
301 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
302 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
303 .expected = error.InvalidSignature, // 8 - non-canonical R
304 },
305 Vec{
306 .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
307 .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
308 .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
309 .expected = null, // 9 - non-canonical R
310 },
311 Vec{
312 .msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
313 .public_key_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
314 .sig_hex = "a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
315 .expected = error.IdentityElement, // 10 - small-order A
316 },
317 Vec{
318 .msg_hex = "39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f",
319 .public_key_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
320 .sig_hex = "a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
321 .expected = error.IdentityElement, // 11 - small-order A
322 },
323 };
324 for (entries) |entry, i| {
325 var msg: [entry.msg_hex.len / 2]u8 = undefined;
326 try fmt.hexToBytes(&msg, entry.msg_hex);
327 var public_key: [32]u8 = undefined;
328 try fmt.hexToBytes(&public_key, entry.public_key_hex);
329 var sig: [64]u8 = undefined;
330 try fmt.hexToBytes(&sig, entry.sig_hex);
331 if (entry.expected) |error_type| {
332 std.testing.expectError(error_type, Ed25519.verify(sig, &msg, public_key));
333 } else {
334 try Ed25519.verify(sig, &msg, public_key);
335 }
336 }
337}
lib/std/crypto/25519/edwards25519.zig+61-3
......@@ -64,6 +64,15 @@ pub const Edwards25519 = struct {
6464 .is_base = true,
6565 };
6666
67 /// The edwards25519 neutral element.
68 pub const neutralElement = Edwards25519{
69 .x = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
70 .y = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
71 .z = Fe{ .limbs = .{ 1507481815385608, 2223447444246085, 1083941587175919, 2059929906842505, 1581435440146976 } },
72 .t = Fe{ .limbs = .{ 2251799813685229, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247 } },
73 .is_base = false,
74 };
75
6776 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
6877
6978 /// Reject the neutral element.
......@@ -140,13 +149,19 @@ pub const Edwards25519 = struct {
140149 return t;
141150 }
142151
143 fn pcMul(pc: [16]Edwards25519, s: [32]u8) !Edwards25519 {
152 fn pcMul(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 {
144153 var q = Edwards25519.identityElement;
145154 var pos: usize = 252;
146155 while (true) : (pos -= 4) {
147156 q = q.dbl().dbl().dbl().dbl();
148157 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf;
149 q = q.add(pcSelect(pc, bit));
158 if (vartime) {
159 if (bit != 0) {
160 q = q.add(pc[bit]);
161 }
162 } else {
163 q = q.add(pcSelect(pc, bit));
164 }
150165 if (pos == 0) break;
151166 }
152167 try q.rejectIdentity();
......@@ -176,7 +191,50 @@ pub const Edwards25519 = struct {
176191 pc = precompute(p);
177192 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
178193 }
179 return pcMul(pc, s);
194 return pcMul(pc, s, false);
195 }
196
197 /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
198 /// This can be used for signature verification.
199 pub fn mulPublic(p: Edwards25519, s: [32]u8) !Edwards25519 {
200 var pc: [16]Edwards25519 = undefined;
201 if (p.is_base) {
202 @setEvalBranchQuota(10000);
203 pc = comptime precompute(Edwards25519.basePoint);
204 } else {
205 pc = precompute(p);
206 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
207 }
208 return pcMul(pc, s, true);
209 }
210
211 /// Multiscalar multiplication *IN VARIABLE TIME* for public data
212 /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually
213 pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) !Edwards25519 {
214 var pcs: [count][16]Edwards25519 = undefined;
215 for (ps) |p, i| {
216 if (p.is_base) {
217 @setEvalBranchQuota(10000);
218 pcs[i] = comptime precompute(Edwards25519.basePoint);
219 } else {
220 pcs[i] = precompute(p);
221 pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey;
222 }
223 }
224 var q = Edwards25519.identityElement;
225 var pos: usize = 252;
226 while (true) : (pos -= 4) {
227 q = q.dbl().dbl().dbl().dbl();
228 for (ss) |s, i| {
229 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf;
230 if (bit != 0) {
231 q = q.add(pcs[i][bit]);
232 }
233 }
234 if (pos == 0) break;
235 }
236 try q.rejectIdentity();
237 return q;
180238 }
181239
182240 /// Multiply an Edwards25519 point by a scalar after "clamping" it.
lib/std/crypto/25519/scalar.zig+719-109
......@@ -6,115 +6,16 @@
66const std = @import("std");
77const mem = std.mem;
88
9const field_size = [32]u8{
9/// 2^252 + 27742317777372353535851937790883648493
10pub const field_size = [32]u8{
1011 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 2^252+27742317777372353535851937790883648493
1112};
1213
13const ScalarExpanded = struct {
14 limbs: [64]i64 = [_]i64{0} ** 64,
14/// A compressed scalar
15pub const CompressedScalar = [32]u8;
1516
16 fn fromBytes(s: [32]u8) ScalarExpanded {
17 var limbs: [64]i64 = undefined;
18 for (s) |x, idx| {
19 limbs[idx] = @as(i64, x);
20 }
21 mem.set(i64, limbs[32..], 0);
22 return .{ .limbs = limbs };
23 }
24
25 fn fromBytes64(s: [64]u8) ScalarExpanded {
26 var limbs: [64]i64 = undefined;
27 for (s) |x, idx| {
28 limbs[idx] = @as(i64, x);
29 }
30 return .{ .limbs = limbs };
31 }
32
33 fn reduce(e: *ScalarExpanded) void {
34 const limbs = &e.limbs;
35 var carry: i64 = undefined;
36 var i: usize = 63;
37 while (i >= 32) : (i -= 1) {
38 carry = 0;
39 const k = i - 12;
40 const xi = limbs[i];
41 var j = i - 32;
42 while (j < k) : (j += 1) {
43 const xj = limbs[j] + carry - 16 * xi * @as(i64, field_size[j - (i - 32)]);
44 carry = (xj + 128) >> 8;
45 limbs[j] = xj - carry * 256;
46 }
47 limbs[k] += carry;
48 limbs[i] = 0;
49 }
50 carry = 0;
51 comptime var j: usize = 0;
52 inline while (j < 32) : (j += 1) {
53 const xi = limbs[j] + carry - (limbs[31] >> 4) * @as(i64, field_size[j]);
54 carry = xi >> 8;
55 limbs[j] = xi & 255;
56 }
57 j = 0;
58 inline while (j < 32) : (j += 1) {
59 limbs[j] -= carry * @as(i64, field_size[j]);
60 }
61 j = 0;
62 inline while (j < 32) : (j += 1) {
63 limbs[j + 1] += limbs[j] >> 8;
64 }
65 }
66
67 fn toBytes(e: *ScalarExpanded) [32]u8 {
68 e.reduce();
69 var r: [32]u8 = undefined;
70 var i: usize = 0;
71 while (i < 32) : (i += 1) {
72 r[i] = @intCast(u8, e.limbs[i]);
73 }
74 return r;
75 }
76
77 fn add(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
78 var r = ScalarExpanded{};
79 comptime var i = 0;
80 inline while (i < 64) : (i += 1) {
81 r.limbs[i] = a.limbs[i] + b.limbs[i];
82 }
83 return r;
84 }
85
86 fn mul(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
87 var r = ScalarExpanded{};
88 var i: usize = 0;
89 while (i < 32) : (i += 1) {
90 const ai = a.limbs[i];
91 comptime var j = 0;
92 inline while (j < 32) : (j += 1) {
93 r.limbs[i + j] += ai * b.limbs[j];
94 }
95 }
96 r.reduce();
97 return r;
98 }
99
100 fn sq(a: ScalarExpanded) ScalarExpanded {
101 return a.mul(a);
102 }
103
104 fn mulAdd(a: ScalarExpanded, b: ScalarExpanded, c: ScalarExpanded) ScalarExpanded {
105 var r: ScalarExpanded = .{ .limbs = c.limbs };
106 var i: usize = 0;
107 while (i < 32) : (i += 1) {
108 const ai = a.limbs[i];
109 comptime var j = 0;
110 inline while (j < 32) : (j += 1) {
111 r.limbs[i + j] += ai * b.limbs[j];
112 }
113 }
114 r.reduce();
115 return r;
116 }
117};
17/// Zero
18pub const zero = [_]u8{0} ** 32;
11819
11920/// Reject a scalar whose encoding is not canonical.
12021pub fn rejectNonCanonical(s: [32]u8) !void {
......@@ -135,12 +36,12 @@ pub fn rejectNonCanonical(s: [32]u8) !void {
13536
13637/// Reduce a scalar to the field size.
13738pub fn reduce(s: [32]u8) [32]u8 {
138 return ScalarExpanded.fromBytes(s).toBytes();
39 return Scalar.fromBytes(s).toBytes();
13940}
14041
14142/// Reduce a 64-bytes scalar to the field size.
14243pub fn reduce64(s: [64]u8) [32]u8 {
143 return ScalarExpanded.fromBytes64(s).toBytes();
44 return ScalarDouble.fromBytes64(s).toBytes();
14445}
14546
14647/// Perform the X25519 "clamping" operation.
......@@ -150,14 +51,723 @@ pub inline fn clamp(s: *[32]u8) void {
15051 s[31] = (s[31] & 127) | 64;
15152}
15253
54/// Return a*b (mod L)
55pub fn mul(a: [32]u8, b: [32]u8) [32]u8 {
56 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).toBytes();
57}
58
15359/// Return a*b+c (mod L)
15460pub fn mulAdd(a: [32]u8, b: [32]u8, c: [32]u8) [32]u8 {
155 return ScalarExpanded.fromBytes(a).mulAdd(ScalarExpanded.fromBytes(b), ScalarExpanded.fromBytes(c)).toBytes();
61 return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).add(Scalar.fromBytes(c)).toBytes();
62}
63
64/// Return a*8 (mod L)
65pub fn mul8(s: [32]u8) [32]u8 {
66 var x = Scalar.fromBytes(s);
67 x = x.add(x);
68 x = x.add(x);
69 x = x.add(x);
70 return x.toBytes();
71}
72
73/// Return a+b (mod L)
74pub fn add(a: [32]u8, b: [32]u8) [32]u8 {
75 return Scalar.fromBytes(a).add(Scalar.fromBytes(b)).toBytes();
76}
77
78/// Return -s (mod L)
79pub fn neg(s: [32]u8) [32]u8 {
80 const fs: [64]u8 = field_size ++ [_]u8{0} ** 32;
81 var sx: [64]u8 = undefined;
82 mem.copy(u8, sx[0..32], s[0..]);
83 mem.set(u8, sx[32..], 0);
84 var carry: u32 = 0;
85 var i: usize = 0;
86 while (i < 64) : (i += 1) {
87 carry = @as(u32, fs[i]) -% sx[i] -% @as(u32, carry);
88 sx[i] = @truncate(u8, carry);
89 carry = (carry >> 8) & 1;
90 }
91 return reduce64(sx);
92}
93
94/// Return (a-b) (mod L)
95pub fn sub(a: [32]u8, b: [32]u8) [32]u8 {
96 return add(a, neg(b));
15697}
15798
99/// A scalar in unpacked reprentation
100pub const Scalar = struct {
101 const Limbs = [5]u64;
102 limbs: Limbs = undefined,
103
104 /// Unpack a 32-byte representation of a scalar
105 pub fn fromBytes(bytes: [32]u8) Scalar {
106 return ScalarDouble.fromBytes32(bytes).reduce(5);
107 }
108
109 /// Pack a scalar into bytes
110 pub fn toBytes(expanded: *const Scalar) [32]u8 {
111 var bytes: [32]u8 = undefined;
112 var i: usize = 0;
113 while (i < 4) : (i += 1) {
114 mem.writeIntLittle(u64, bytes[i * 7 ..][0..8], expanded.limbs[i]);
115 }
116 mem.writeIntLittle(u32, bytes[i * 7 ..][0..4], @intCast(u32, expanded.limbs[i]));
117 return bytes;
118 }
119
120 /// Return x+y (mod l)
121 pub fn add(x: Scalar, y: Scalar) Scalar {
122 const carry0 = (x.limbs[0] + y.limbs[0]) >> 56;
123 const t0 = (x.limbs[0] + y.limbs[0]) & 0xffffffffffffff;
124 const t00 = t0;
125 const c0 = carry0;
126 const carry1 = (x.limbs[1] + y.limbs[1] + c0) >> 56;
127 const t1 = (x.limbs[1] + y.limbs[1] + c0) & 0xffffffffffffff;
128 const t10 = t1;
129 const c1 = carry1;
130 const carry2 = (x.limbs[2] + y.limbs[2] + c1) >> 56;
131 const t2 = (x.limbs[2] + y.limbs[2] + c1) & 0xffffffffffffff;
132 const t20 = t2;
133 const c2 = carry2;
134 const carry = (x.limbs[3] + y.limbs[3] + c2) >> 56;
135 const t3 = (x.limbs[3] + y.limbs[3] + c2) & 0xffffffffffffff;
136 const t30 = t3;
137 const c3 = carry;
138 const t4 = x.limbs[4] + y.limbs[4] + c3;
139
140 const y01: u64 = 5175514460705773;
141 const y11: u64 = 70332060721272408;
142 const y21: u64 = 5342;
143 const y31: u64 = 0;
144 const y41: u64 = 268435456;
145
146 const b5 = (t00 -% y01) >> 63;
147 const t5 = ((b5 << 56) + t00) -% y01;
148 const b0 = b5;
149 const t01 = t5;
150 const b6 = (t10 -% (y11 + b0)) >> 63;
151 const t6 = ((b6 << 56) + t10) -% (y11 + b0);
152 const b1 = b6;
153 const t11 = t6;
154 const b7 = (t20 -% (y21 + b1)) >> 63;
155 const t7 = ((b7 << 56) + t20) -% (y21 + b1);
156 const b2 = b7;
157 const t21 = t7;
158 const b8 = (t30 -% (y31 + b2)) >> 63;
159 const t8 = ((b8 << 56) + t30) -% (y31 + b2);
160 const b3 = b8;
161 const t31 = t8;
162 const b = (t4 -% (y41 + b3)) >> 63;
163 const t = ((b << 56) + t4) -% (y41 + b3);
164 const b4 = b;
165 const t41 = t;
166
167 const mask = (b4 -% 1);
168 const z00 = t00 ^ (mask & (t00 ^ t01));
169 const z10 = t10 ^ (mask & (t10 ^ t11));
170 const z20 = t20 ^ (mask & (t20 ^ t21));
171 const z30 = t30 ^ (mask & (t30 ^ t31));
172 const z40 = t4 ^ (mask & (t4 ^ t41));
173
174 return Scalar{ .limbs = .{ z00, z10, z20, z30, z40 } };
175 }
176
177 /// Return x*r (mod l)
178 pub fn mul(x: Scalar, y: Scalar) Scalar {
179 const xy000 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[0]);
180 const xy010 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[1]);
181 const xy020 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[2]);
182 const xy030 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[3]);
183 const xy040 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[4]);
184 const xy100 = @as(u128, x.limbs[1]) * @as(u128, y.limbs[0]);
185 const xy110 = @as(u128, x.limbs[1]) * @as(u128, y.limbs[1]);
186 const xy120 = @as(u128, x.limbs[1]) * @as(u128, y.limbs[2]);
187 const xy130 = @as(u128, x.limbs[1]) * @as(u128, y.limbs[3]);
188 const xy140 = @as(u128, x.limbs[1]) * @as(u128, y.limbs[4]);
189 const xy200 = @as(u128, x.limbs[2]) * @as(u128, y.limbs[0]);
190 const xy210 = @as(u128, x.limbs[2]) * @as(u128, y.limbs[1]);
191 const xy220 = @as(u128, x.limbs[2]) * @as(u128, y.limbs[2]);
192 const xy230 = @as(u128, x.limbs[2]) * @as(u128, y.limbs[3]);
193 const xy240 = @as(u128, x.limbs[2]) * @as(u128, y.limbs[4]);
194 const xy300 = @as(u128, x.limbs[3]) * @as(u128, y.limbs[0]);
195 const xy310 = @as(u128, x.limbs[3]) * @as(u128, y.limbs[1]);
196 const xy320 = @as(u128, x.limbs[3]) * @as(u128, y.limbs[2]);
197 const xy330 = @as(u128, x.limbs[3]) * @as(u128, y.limbs[3]);
198 const xy340 = @as(u128, x.limbs[3]) * @as(u128, y.limbs[4]);
199 const xy400 = @as(u128, x.limbs[4]) * @as(u128, y.limbs[0]);
200 const xy410 = @as(u128, x.limbs[4]) * @as(u128, y.limbs[1]);
201 const xy420 = @as(u128, x.limbs[4]) * @as(u128, y.limbs[2]);
202 const xy430 = @as(u128, x.limbs[4]) * @as(u128, y.limbs[3]);
203 const xy440 = @as(u128, x.limbs[4]) * @as(u128, y.limbs[4]);
204 const z00 = xy000;
205 const z10 = xy010 + xy100;
206 const z20 = xy020 + xy110 + xy200;
207 const z30 = xy030 + xy120 + xy210 + xy300;
208 const z40 = xy040 + xy130 + xy220 + xy310 + xy400;
209 const z50 = xy140 + xy230 + xy320 + xy410;
210 const z60 = xy240 + xy330 + xy420;
211 const z70 = xy340 + xy430;
212 const z80 = xy440;
213
214 const carry0 = z00 >> 56;
215 const t10 = @truncate(u64, z00) & 0xffffffffffffff;
216 const c00 = carry0;
217 const t00 = t10;
218 const carry1 = (z10 + c00) >> 56;
219 const t11 = @truncate(u64, (z10 + c00)) & 0xffffffffffffff;
220 const c10 = carry1;
221 const t12 = t11;
222 const carry2 = (z20 + c10) >> 56;
223 const t13 = @truncate(u64, (z20 + c10)) & 0xffffffffffffff;
224 const c20 = carry2;
225 const t20 = t13;
226 const carry3 = (z30 + c20) >> 56;
227 const t14 = @truncate(u64, (z30 + c20)) & 0xffffffffffffff;
228 const c30 = carry3;
229 const t30 = t14;
230 const carry4 = (z40 + c30) >> 56;
231 const t15 = @truncate(u64, (z40 + c30)) & 0xffffffffffffff;
232 const c40 = carry4;
233 const t40 = t15;
234 const carry5 = (z50 + c40) >> 56;
235 const t16 = @truncate(u64, (z50 + c40)) & 0xffffffffffffff;
236 const c50 = carry5;
237 const t50 = t16;
238 const carry6 = (z60 + c50) >> 56;
239 const t17 = @truncate(u64, (z60 + c50)) & 0xffffffffffffff;
240 const c60 = carry6;
241 const t60 = t17;
242 const carry7 = (z70 + c60) >> 56;
243 const t18 = @truncate(u64, (z70 + c60)) & 0xffffffffffffff;
244 const c70 = carry7;
245 const t70 = t18;
246 const carry8 = (z80 + c70) >> 56;
247 const t19 = @truncate(u64, (z80 + c70)) & 0xffffffffffffff;
248 const c80 = carry8;
249 const t80 = t19;
250 const t90 = (@truncate(u64, c80));
251 const r0 = t00;
252 const r1 = t12;
253 const r2 = t20;
254 const r3 = t30;
255 const r4 = t40;
256 const r5 = t50;
257 const r6 = t60;
258 const r7 = t70;
259 const r8 = t80;
260 const r9 = t90;
261
262 const m0: u64 = 5175514460705773;
263 const m1: u64 = 70332060721272408;
264 const m2: u64 = 5342;
265 const m3: u64 = 0;
266 const m4: u64 = 268435456;
267 const mu0: u64 = 44162584779952923;
268 const mu1: u64 = 9390964836247533;
269 const mu2: u64 = 72057594036560134;
270 const mu3: u64 = 72057594037927935;
271 const mu4: u64 = 68719476735;
272
273 const y_ = (r5 & 0xffffff) << 32;
274 const x_ = r4 >> 24;
275 const z01 = (x_ | y_);
276 const y_0 = (r6 & 0xffffff) << 32;
277 const x_0 = r5 >> 24;
278 const z11 = (x_0 | y_0);
279 const y_1 = (r7 & 0xffffff) << 32;
280 const x_1 = r6 >> 24;
281 const z21 = (x_1 | y_1);
282 const y_2 = (r8 & 0xffffff) << 32;
283 const x_2 = r7 >> 24;
284 const z31 = (x_2 | y_2);
285 const y_3 = (r9 & 0xffffff) << 32;
286 const x_3 = r8 >> 24;
287 const z41 = (x_3 | y_3);
288 const q0 = z01;
289 const q1 = z11;
290 const q2 = z21;
291 const q3 = z31;
292 const q4 = z41;
293 const xy001 = @as(u128, q0) * @as(u128, mu0);
294 const xy011 = @as(u128, q0) * @as(u128, mu1);
295 const xy021 = @as(u128, q0) * @as(u128, mu2);
296 const xy031 = @as(u128, q0) * @as(u128, mu3);
297 const xy041 = @as(u128, q0) * @as(u128, mu4);
298 const xy101 = @as(u128, q1) * @as(u128, mu0);
299 const xy111 = @as(u128, q1) * @as(u128, mu1);
300 const xy121 = @as(u128, q1) * @as(u128, mu2);
301 const xy131 = @as(u128, q1) * @as(u128, mu3);
302 const xy14 = @as(u128, q1) * @as(u128, mu4);
303 const xy201 = @as(u128, q2) * @as(u128, mu0);
304 const xy211 = @as(u128, q2) * @as(u128, mu1);
305 const xy221 = @as(u128, q2) * @as(u128, mu2);
306 const xy23 = @as(u128, q2) * @as(u128, mu3);
307 const xy24 = @as(u128, q2) * @as(u128, mu4);
308 const xy301 = @as(u128, q3) * @as(u128, mu0);
309 const xy311 = @as(u128, q3) * @as(u128, mu1);
310 const xy32 = @as(u128, q3) * @as(u128, mu2);
311 const xy33 = @as(u128, q3) * @as(u128, mu3);
312 const xy34 = @as(u128, q3) * @as(u128, mu4);
313 const xy401 = @as(u128, q4) * @as(u128, mu0);
314 const xy41 = @as(u128, q4) * @as(u128, mu1);
315 const xy42 = @as(u128, q4) * @as(u128, mu2);
316 const xy43 = @as(u128, q4) * @as(u128, mu3);
317 const xy44 = @as(u128, q4) * @as(u128, mu4);
318 const z02 = xy001;
319 const z12 = xy011 + xy101;
320 const z22 = xy021 + xy111 + xy201;
321 const z32 = xy031 + xy121 + xy211 + xy301;
322 const z42 = xy041 + xy131 + xy221 + xy311 + xy401;
323 const z5 = xy14 + xy23 + xy32 + xy41;
324 const z6 = xy24 + xy33 + xy42;
325 const z7 = xy34 + xy43;
326 const z8 = xy44;
327
328 const carry9 = z02 >> 56;
329 const c01 = carry9;
330 const carry10 = (z12 + c01) >> 56;
331 const t21 = @truncate(u64, z12 + c01) & 0xffffffffffffff;
332 const c11 = carry10;
333 const carry11 = (z22 + c11) >> 56;
334 const t22 = @truncate(u64, z22 + c11) & 0xffffffffffffff;
335 const c21 = carry11;
336 const carry12 = (z32 + c21) >> 56;
337 const t23 = @truncate(u64, z32 + c21) & 0xffffffffffffff;
338 const c31 = carry12;
339 const carry13 = (z42 + c31) >> 56;
340 const t24 = @truncate(u64, z42 + c31) & 0xffffffffffffff;
341 const c41 = carry13;
342 const t41 = t24;
343 const carry14 = (z5 + c41) >> 56;
344 const t25 = @truncate(u64, z5 + c41) & 0xffffffffffffff;
345 const c5 = carry14;
346 const t5 = t25;
347 const carry15 = (z6 + c5) >> 56;
348 const t26 = @truncate(u64, z6 + c5) & 0xffffffffffffff;
349 const c6 = carry15;
350 const t6 = t26;
351 const carry16 = (z7 + c6) >> 56;
352 const t27 = @truncate(u64, z7 + c6) & 0xffffffffffffff;
353 const c7 = carry16;
354 const t7 = t27;
355 const carry17 = (z8 + c7) >> 56;
356 const t28 = @truncate(u64, z8 + c7) & 0xffffffffffffff;
357 const c8 = carry17;
358 const t8 = t28;
359 const t9 = @truncate(u64, c8);
360
361 const qmu4_ = t41;
362 const qmu5_ = t5;
363 const qmu6_ = t6;
364 const qmu7_ = t7;
365 const qmu8_ = t8;
366 const qmu9_ = t9;
367 const y_4 = (qmu5_ & 0xffffffffff) << 16;
368 const x_4 = qmu4_ >> 40;
369 const z03 = (x_4 | y_4);
370 const y_5 = (qmu6_ & 0xffffffffff) << 16;
371 const x_5 = qmu5_ >> 40;
372 const z13 = (x_5 | y_5);
373 const y_6 = (qmu7_ & 0xffffffffff) << 16;
374 const x_6 = qmu6_ >> 40;
375 const z23 = (x_6 | y_6);
376 const y_7 = (qmu8_ & 0xffffffffff) << 16;
377 const x_7 = qmu7_ >> 40;
378 const z33 = (x_7 | y_7);
379 const y_8 = (qmu9_ & 0xffffffffff) << 16;
380 const x_8 = qmu8_ >> 40;
381 const z43 = (x_8 | y_8);
382 const qdiv0 = z03;
383 const qdiv1 = z13;
384 const qdiv2 = z23;
385 const qdiv3 = z33;
386 const qdiv4 = z43;
387 const r01 = r0;
388 const r11 = r1;
389 const r21 = r2;
390 const r31 = r3;
391 const r41 = (r4 & 0xffffffffff);
392
393 const xy00 = @as(u128, qdiv0) * @as(u128, m0);
394 const xy01 = @as(u128, qdiv0) * @as(u128, m1);
395 const xy02 = @as(u128, qdiv0) * @as(u128, m2);
396 const xy03 = @as(u128, qdiv0) * @as(u128, m3);
397 const xy04 = @as(u128, qdiv0) * @as(u128, m4);
398 const xy10 = @as(u128, qdiv1) * @as(u128, m0);
399 const xy11 = @as(u128, qdiv1) * @as(u128, m1);
400 const xy12 = @as(u128, qdiv1) * @as(u128, m2);
401 const xy13 = @as(u128, qdiv1) * @as(u128, m3);
402 const xy20 = @as(u128, qdiv2) * @as(u128, m0);
403 const xy21 = @as(u128, qdiv2) * @as(u128, m1);
404 const xy22 = @as(u128, qdiv2) * @as(u128, m2);
405 const xy30 = @as(u128, qdiv3) * @as(u128, m0);
406 const xy31 = @as(u128, qdiv3) * @as(u128, m1);
407 const xy40 = @as(u128, qdiv4) * @as(u128, m0);
408 const carry18 = xy00 >> 56;
409 const t29 = @truncate(u64, xy00) & 0xffffffffffffff;
410 const c0 = carry18;
411 const t01 = t29;
412 const carry19 = (xy01 + xy10 + c0) >> 56;
413 const t31 = @truncate(u64, xy01 + xy10 + c0) & 0xffffffffffffff;
414 const c12 = carry19;
415 const t110 = t31;
416 const carry20 = (xy02 + xy11 + xy20 + c12) >> 56;
417 const t32 = @truncate(u64, xy02 + xy11 + xy20 + c12) & 0xffffffffffffff;
418 const c22 = carry20;
419 const t210 = t32;
420 const carry = (xy03 + xy12 + xy21 + xy30 + c22) >> 56;
421 const t33 = @truncate(u64, xy03 + xy12 + xy21 + xy30 + c22) & 0xffffffffffffff;
422 const c32 = carry;
423 const t34 = t33;
424 const t42 = @truncate(u64, xy04 + xy13 + xy22 + xy31 + xy40 + c32) & 0xffffffffff;
425
426 const qmul0 = t01;
427 const qmul1 = t110;
428 const qmul2 = t210;
429 const qmul3 = t34;
430 const qmul4 = t42;
431 const b5 = (r01 -% qmul0) >> 63;
432 const t35 = ((b5 << 56) + r01) -% qmul0;
433 const c1 = b5;
434 const t02 = t35;
435 const b6 = (r11 -% (qmul1 + c1)) >> 63;
436 const t36 = ((b6 << 56) + r11) -% (qmul1 + c1);
437 const c2 = b6;
438 const t111 = t36;
439 const b7 = (r21 -% (qmul2 + c2)) >> 63;
440 const t37 = ((b7 << 56) + r21) -% (qmul2 + c2);
441 const c3 = b7;
442 const t211 = t37;
443 const b8 = (r31 -% (qmul3 + c3)) >> 63;
444 const t38 = ((b8 << 56) + r31) -% (qmul3 + c3);
445 const c4 = b8;
446 const t39 = t38;
447 const b9 = (r41 -% (qmul4 + c4)) >> 63;
448 const t43 = ((b9 << 40) + r41) -% (qmul4 + c4);
449 const t44 = t43;
450 const s0 = t02;
451 const s1 = t111;
452 const s2 = t211;
453 const s3 = t39;
454 const s4 = t44;
455
456 const y01: u64 = 5175514460705773;
457 const y11: u64 = 70332060721272408;
458 const y21: u64 = 5342;
459 const y31: u64 = 0;
460 const y41: u64 = 268435456;
461
462 const b10 = (s0 -% y01) >> 63;
463 const t45 = ((b10 << 56) + s0) -% y01;
464 const b0 = b10;
465 const t0 = t45;
466 const b11 = (s1 -% (y11 + b0)) >> 63;
467 const t46 = ((b11 << 56) + s1) -% (y11 + b0);
468 const b1 = b11;
469 const t1 = t46;
470 const b12 = (s2 -% (y21 + b1)) >> 63;
471 const t47 = ((b12 << 56) + s2) -% (y21 + b1);
472 const b2 = b12;
473 const t2 = t47;
474 const b13 = (s3 -% (y31 + b2)) >> 63;
475 const t48 = ((b13 << 56) + s3) -% (y31 + b2);
476 const b3 = b13;
477 const t3 = t48;
478 const b = (s4 -% (y41 + b3)) >> 63;
479 const t = ((b << 56) + s4) -% (y41 + b3);
480 const b4 = b;
481 const t4 = t;
482 const mask = (b4 -% @intCast(u64, ((1))));
483 const z04 = s0 ^ (mask & (s0 ^ t0));
484 const z14 = s1 ^ (mask & (s1 ^ t1));
485 const z24 = s2 ^ (mask & (s2 ^ t2));
486 const z34 = s3 ^ (mask & (s3 ^ t3));
487 const z44 = s4 ^ (mask & (s4 ^ t4));
488
489 return Scalar{ .limbs = .{ z04, z14, z24, z34, z44 } };
490 }
491};
492
493const ScalarDouble = struct {
494 const Limbs = [10]u64;
495 limbs: Limbs = undefined,
496
497 fn fromBytes64(bytes: [64]u8) ScalarDouble {
498 var limbs: Limbs = undefined;
499 var i: usize = 0;
500 while (i < 9) : (i += 1) {
501 limbs[i] = mem.readIntLittle(u64, bytes[i * 7 ..][0..8]) & 0xffffffffffffff;
502 }
503 limbs[i] = @as(u64, bytes[i * 7]);
504 return ScalarDouble{ .limbs = limbs };
505 }
506
507 fn fromBytes32(bytes: [32]u8) ScalarDouble {
508 var limbs: Limbs = undefined;
509 var i: usize = 0;
510 while (i < 4) : (i += 1) {
511 limbs[i] = mem.readIntLittle(u64, bytes[i * 7 ..][0..8]) & 0xffffffffffffff;
512 }
513 limbs[i] = @as(u64, mem.readIntLittle(u32, bytes[i * 7 ..][0..4]));
514 mem.set(u64, limbs[5..], 0);
515 return ScalarDouble{ .limbs = limbs };
516 }
517
518 fn toBytes(expanded_double: *ScalarDouble) [32]u8 {
519 return expanded_double.reduce(10).toBytes();
520 }
521
522 /// Barrett reduction
523 fn reduce(expanded: *ScalarDouble, comptime limbs_count: usize) Scalar {
524 const t = expanded.limbs;
525 const t0 = if (limbs_count <= 0) 0 else t[0];
526 const t1 = if (limbs_count <= 1) 0 else t[1];
527 const t2 = if (limbs_count <= 2) 0 else t[2];
528 const t3 = if (limbs_count <= 3) 0 else t[3];
529 const t4 = if (limbs_count <= 4) 0 else t[4];
530 const t5 = if (limbs_count <= 5) 0 else t[5];
531 const t6 = if (limbs_count <= 6) 0 else t[6];
532 const t7 = if (limbs_count <= 7) 0 else t[7];
533 const t8 = if (limbs_count <= 8) 0 else t[8];
534 const t9 = if (limbs_count <= 9) 0 else t[9];
535
536 const m0: u64 = 5175514460705773;
537 const m1: u64 = 70332060721272408;
538 const m2: u64 = 5342;
539 const m3: u64 = 0;
540 const m4: u64 = 268435456;
541 const mu0: u64 = 44162584779952923;
542 const mu1: u64 = 9390964836247533;
543 const mu2: u64 = 72057594036560134;
544 const mu3: u64 = 0xffffffffffffff;
545 const mu4: u64 = 68719476735;
546
547 const y_ = (t5 & 0xffffff) << 32;
548 const x_ = t4 >> 24;
549 const z00 = x_ | y_;
550 const y_0 = (t6 & 0xffffff) << 32;
551 const x_0 = t5 >> 24;
552 const z10 = x_0 | y_0;
553 const y_1 = (t7 & 0xffffff) << 32;
554 const x_1 = t6 >> 24;
555 const z20 = x_1 | y_1;
556 const y_2 = (t8 & 0xffffff) << 32;
557 const x_2 = t7 >> 24;
558 const z30 = x_2 | y_2;
559 const y_3 = (t9 & 0xffffff) << 32;
560 const x_3 = t8 >> 24;
561 const z40 = x_3 | y_3;
562 const q0 = z00;
563 const q1 = z10;
564 const q2 = z20;
565 const q3 = z30;
566 const q4 = z40;
567
568 const xy000 = @as(u128, q0) * @as(u128, mu0);
569 const xy010 = @as(u128, q0) * @as(u128, mu1);
570 const xy020 = @as(u128, q0) * @as(u128, mu2);
571 const xy030 = @as(u128, q0) * @as(u128, mu3);
572 const xy040 = @as(u128, q0) * @as(u128, mu4);
573 const xy100 = @as(u128, q1) * @as(u128, mu0);
574 const xy110 = @as(u128, q1) * @as(u128, mu1);
575 const xy120 = @as(u128, q1) * @as(u128, mu2);
576 const xy130 = @as(u128, q1) * @as(u128, mu3);
577 const xy14 = @as(u128, q1) * @as(u128, mu4);
578 const xy200 = @as(u128, q2) * @as(u128, mu0);
579 const xy210 = @as(u128, q2) * @as(u128, mu1);
580 const xy220 = @as(u128, q2) * @as(u128, mu2);
581 const xy23 = @as(u128, q2) * @as(u128, mu3);
582 const xy24 = @as(u128, q2) * @as(u128, mu4);
583 const xy300 = @as(u128, q3) * @as(u128, mu0);
584 const xy310 = @as(u128, q3) * @as(u128, mu1);
585 const xy32 = @as(u128, q3) * @as(u128, mu2);
586 const xy33 = @as(u128, q3) * @as(u128, mu3);
587 const xy34 = @as(u128, q3) * @as(u128, mu4);
588 const xy400 = @as(u128, q4) * @as(u128, mu0);
589 const xy41 = @as(u128, q4) * @as(u128, mu1);
590 const xy42 = @as(u128, q4) * @as(u128, mu2);
591 const xy43 = @as(u128, q4) * @as(u128, mu3);
592 const xy44 = @as(u128, q4) * @as(u128, mu4);
593 const z01 = xy000;
594 const z11 = xy010 + xy100;
595 const z21 = xy020 + xy110 + xy200;
596 const z31 = xy030 + xy120 + xy210 + xy300;
597 const z41 = xy040 + xy130 + xy220 + xy310 + xy400;
598 const z5 = xy14 + xy23 + xy32 + xy41;
599 const z6 = xy24 + xy33 + xy42;
600 const z7 = xy34 + xy43;
601 const z8 = xy44;
602
603 const carry0 = z01 >> 56;
604 const c00 = carry0;
605 const carry1 = (z11 + c00) >> 56;
606 const t100 = @as(u64, @truncate(u64, z11 + c00)) & 0xffffffffffffff;
607 const c10 = carry1;
608 const carry2 = (z21 + c10) >> 56;
609 const t101 = @as(u64, @truncate(u64, z21 + c10)) & 0xffffffffffffff;
610 const c20 = carry2;
611 const carry3 = (z31 + c20) >> 56;
612 const t102 = @as(u64, @truncate(u64, z31 + c20)) & 0xffffffffffffff;
613 const c30 = carry3;
614 const carry4 = (z41 + c30) >> 56;
615 const t103 = @as(u64, @truncate(u64, z41 + c30)) & 0xffffffffffffff;
616 const c40 = carry4;
617 const t410 = t103;
618 const carry5 = (z5 + c40) >> 56;
619 const t104 = @as(u64, @truncate(u64, z5 + c40)) & 0xffffffffffffff;
620 const c5 = carry5;
621 const t51 = t104;
622 const carry6 = (z6 + c5) >> 56;
623 const t105 = @as(u64, @truncate(u64, z6 + c5)) & 0xffffffffffffff;
624 const c6 = carry6;
625 const t61 = t105;
626 const carry7 = (z7 + c6) >> 56;
627 const t106 = @as(u64, @truncate(u64, z7 + c6)) & 0xffffffffffffff;
628 const c7 = carry7;
629 const t71 = t106;
630 const carry8 = (z8 + c7) >> 56;
631 const t107 = @as(u64, @truncate(u64, z8 + c7)) & 0xffffffffffffff;
632 const c8 = carry8;
633 const t81 = t107;
634 const t91 = @as(u64, @truncate(u64, c8));
635
636 const qmu4_ = t410;
637 const qmu5_ = t51;
638 const qmu6_ = t61;
639 const qmu7_ = t71;
640 const qmu8_ = t81;
641 const qmu9_ = t91;
642 const y_4 = (qmu5_ & 0xffffffffff) << 16;
643 const x_4 = qmu4_ >> 40;
644 const z02 = x_4 | y_4;
645 const y_5 = (qmu6_ & 0xffffffffff) << 16;
646 const x_5 = qmu5_ >> 40;
647 const z12 = x_5 | y_5;
648 const y_6 = (qmu7_ & 0xffffffffff) << 16;
649 const x_6 = qmu6_ >> 40;
650 const z22 = x_6 | y_6;
651 const y_7 = (qmu8_ & 0xffffffffff) << 16;
652 const x_7 = qmu7_ >> 40;
653 const z32 = x_7 | y_7;
654 const y_8 = (qmu9_ & 0xffffffffff) << 16;
655 const x_8 = qmu8_ >> 40;
656 const z42 = x_8 | y_8;
657 const qdiv0 = z02;
658 const qdiv1 = z12;
659 const qdiv2 = z22;
660 const qdiv3 = z32;
661 const qdiv4 = z42;
662 const r0 = t0;
663 const r1 = t1;
664 const r2 = t2;
665 const r3 = t3;
666 const r4 = t4 & 0xffffffffff;
667
668 const xy00 = @as(u128, qdiv0) * @as(u128, m0);
669 const xy01 = @as(u128, qdiv0) * @as(u128, m1);
670 const xy02 = @as(u128, qdiv0) * @as(u128, m2);
671 const xy03 = @as(u128, qdiv0) * @as(u128, m3);
672 const xy04 = @as(u128, qdiv0) * @as(u128, m4);
673 const xy10 = @as(u128, qdiv1) * @as(u128, m0);
674 const xy11 = @as(u128, qdiv1) * @as(u128, m1);
675 const xy12 = @as(u128, qdiv1) * @as(u128, m2);
676 const xy13 = @as(u128, qdiv1) * @as(u128, m3);
677 const xy20 = @as(u128, qdiv2) * @as(u128, m0);
678 const xy21 = @as(u128, qdiv2) * @as(u128, m1);
679 const xy22 = @as(u128, qdiv2) * @as(u128, m2);
680 const xy30 = @as(u128, qdiv3) * @as(u128, m0);
681 const xy31 = @as(u128, qdiv3) * @as(u128, m1);
682 const xy40 = @as(u128, qdiv4) * @as(u128, m0);
683 const carry9 = xy00 >> 56;
684 const t108 = @truncate(u64, xy00) & 0xffffffffffffff;
685 const c0 = carry9;
686 const t010 = t108;
687 const carry10 = (xy01 + xy10 + c0) >> 56;
688 const t109 = @truncate(u64, xy01 + xy10 + c0) & 0xffffffffffffff;
689 const c11 = carry10;
690 const t110 = t109;
691 const carry11 = (xy02 + xy11 + xy20 + c11) >> 56;
692 const t1010 = @truncate(u64, xy02 + xy11 + xy20 + c11) & 0xffffffffffffff;
693 const c21 = carry11;
694 const t210 = t1010;
695 const carry = (xy03 + xy12 + xy21 + xy30 + c21) >> 56;
696 const t1011 = @truncate(u64, xy03 + xy12 + xy21 + xy30 + c21) & 0xffffffffffffff;
697 const c31 = carry;
698 const t310 = t1011;
699 const t411 = @truncate(u64, xy04 + xy13 + xy22 + xy31 + xy40 + c31) & 0xffffffffff;
700
701 const qmul0 = t010;
702 const qmul1 = t110;
703 const qmul2 = t210;
704 const qmul3 = t310;
705 const qmul4 = t411;
706 const b5 = (r0 -% qmul0) >> 63;
707 const t1012 = ((b5 << 56) + r0) -% qmul0;
708 const c1 = b5;
709 const t011 = t1012;
710 const b6 = (r1 -% (qmul1 + c1)) >> 63;
711 const t1013 = ((b6 << 56) + r1) -% (qmul1 + c1);
712 const c2 = b6;
713 const t111 = t1013;
714 const b7 = (r2 -% (qmul2 + c2)) >> 63;
715 const t1014 = ((b7 << 56) + r2) -% (qmul2 + c2);
716 const c3 = b7;
717 const t211 = t1014;
718 const b8 = (r3 -% (qmul3 + c3)) >> 63;
719 const t1015 = ((b8 << 56) + r3) -% (qmul3 + c3);
720 const c4 = b8;
721 const t311 = t1015;
722 const b9 = (r4 -% (qmul4 + c4)) >> 63;
723 const t1016 = ((b9 << 40) + r4) -% (qmul4 + c4);
724 const t412 = t1016;
725 const s0 = t011;
726 const s1 = t111;
727 const s2 = t211;
728 const s3 = t311;
729 const s4 = t412;
730
731 const y0: u64 = 5175514460705773;
732 const y1: u64 = 70332060721272408;
733 const y2: u64 = 5342;
734 const y3: u64 = 0;
735 const y4: u64 = 268435456;
736
737 const b10 = (s0 -% y0) >> 63;
738 const t1017 = ((b10 << 56) + s0) -% y0;
739 const b0 = b10;
740 const t01 = t1017;
741 const b11 = (s1 -% (y1 + b0)) >> 63;
742 const t1018 = ((b11 << 56) + s1) -% (y1 + b0);
743 const b1 = b11;
744 const t11 = t1018;
745 const b12 = (s2 -% (y2 + b1)) >> 63;
746 const t1019 = ((b12 << 56) + s2) -% (y2 + b1);
747 const b2 = b12;
748 const t21 = t1019;
749 const b13 = (s3 -% (y3 + b2)) >> 63;
750 const t1020 = ((b13 << 56) + s3) -% (y3 + b2);
751 const b3 = b13;
752 const t31 = t1020;
753 const b = (s4 -% (y4 + b3)) >> 63;
754 const t10 = ((b << 56) + s4) -% (y4 + b3);
755 const b4 = b;
756 const t41 = t10;
757 const mask = b4 -% @as(u64, @as(u64, 1));
758 const z03 = s0 ^ (mask & (s0 ^ t01));
759 const z13 = s1 ^ (mask & (s1 ^ t11));
760 const z23 = s2 ^ (mask & (s2 ^ t21));
761 const z33 = s3 ^ (mask & (s3 ^ t31));
762 const z43 = s4 ^ (mask & (s4 ^ t41));
763
764 return Scalar{ .limbs = .{ z03, z13, z23, z33, z43 } };
765 }
766};
767
158768test "scalar25519" {
159769 const bytes: [32]u8 = .{ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 255 };
160 var x = ScalarExpanded.fromBytes(bytes);
770 var x = Scalar.fromBytes(bytes);
161771 var y = x.toBytes();
162772 try rejectNonCanonical(y);
163773 var buf: [128]u8 = undefined;
lib/std/crypto/benchmark.zig+75-2
......@@ -134,8 +134,8 @@ pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count
134134 {
135135 var i: usize = 0;
136136 while (i < signatures_count) : (i += 1) {
137 const s = try Signature.sign(&msg, key_pair, null);
138 mem.doNotOptimizeAway(&s);
137 const sig = try Signature.sign(&msg, key_pair, null);
138 mem.doNotOptimizeAway(&sig);
139139 }
140140 }
141141 const end = timer.read();
......@@ -146,6 +146,65 @@ pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count
146146 return throughput;
147147}
148148
149const signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
150
151pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
152 var seed: [Signature.seed_length]u8 = undefined;
153 prng.random.bytes(seed[0..]);
154 const msg = [_]u8{0} ** 64;
155 const key_pair = try Signature.createKeyPair(seed);
156 const public_key = Signature.publicKey(key_pair);
157 const sig = try Signature.sign(&msg, key_pair, null);
158
159 var timer = try Timer.start();
160 const start = timer.lap();
161 {
162 var i: usize = 0;
163 while (i < signatures_count) : (i += 1) {
164 try Signature.verify(sig, &msg, public_key);
165 mem.doNotOptimizeAway(&sig);
166 }
167 }
168 const end = timer.read();
169
170 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
171 const throughput = @floatToInt(u64, signatures_count / elapsed_s);
172
173 return throughput;
174}
175
176const batch_signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
177
178pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
179 var seed: [Signature.seed_length]u8 = undefined;
180 prng.random.bytes(seed[0..]);
181 const msg = [_]u8{0} ** 64;
182 const key_pair = try Signature.createKeyPair(seed);
183 const public_key = Signature.publicKey(key_pair);
184 const sig = try Signature.sign(&msg, key_pair, null);
185
186 var batch: [64]Signature.BatchElement = undefined;
187 for (batch) |*element| {
188 element.* = Signature.BatchElement{ .sig = sig, .msg = &msg, .public_key = public_key };
189 }
190
191 var timer = try Timer.start();
192 const start = timer.lap();
193 {
194 var i: usize = 0;
195 while (i < signatures_count) : (i += 1) {
196 try Signature.verifyBatch(batch.len, batch);
197 mem.doNotOptimizeAway(&sig);
198 }
199 }
200 const end = timer.read();
201
202 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
203 const throughput = batch.len * @floatToInt(u64, signatures_count / elapsed_s);
204
205 return throughput;
206}
207
149208const aeads = [_]Crypto{
150209 Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
151210 Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
......@@ -326,6 +385,20 @@ pub fn main() !void {
326385 }
327386 }
328387
388 inline for (signature_verifications) |E| {
389 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
390 const throughput = try benchmarkSignatureVerification(E.ty, mode(1000));
391 try stdout.print("{:>17}: {:10} verifications/s\n", .{ E.name, throughput });
392 }
393 }
394
395 inline for (batch_signature_verifications) |E| {
396 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
397 const throughput = try benchmarkBatchSignatureVerification(E.ty, mode(1000));
398 try stdout.print("{:>17}: {:10} verifications/s (batch)\n", .{ E.name, throughput });
399 }
400 }
401
329402 inline for (aeads) |E| {
330403 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
331404 const throughput = try benchmarkAead(E.ty, mode(128 * MiB));