authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-11-14 23:54:50+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-17 17:07:32-08:00
log9c2b014ea82fdfbb4b46ce789898623be450cca5
treeff302856c34987cb8833d6324b76bbe5e1e039dd
parent0d9c474ecfaf58698774022211d06da97d2fd648

std/crypto: use NAF for multi-scalar edwards25519 multiplication

Transforming scalars to non-adjacent form shrinks the number of precomputations down to 8, while still processing 4 bits at a time. However, real-world benchmarks show that the transform is only really useful with large precomputation tables and for batch signature verification. So, do it for batch verification only.

1 files changed, 85 insertions(+), 36 deletions(-)

lib/std/crypto/25519/edwards25519.zig+85-36
......@@ -144,98 +144,147 @@ pub const Edwards25519 = struct {
144144 p.t.cMov(a.t, c);
145145 }
146146
147 inline fn pcSelect(pc: [16]Edwards25519, b: u8) Edwards25519 {
147 inline fn pcSelect(comptime n: usize, pc: [n]Edwards25519, b: u8) Edwards25519 {
148148 var t = Edwards25519.identityElement;
149 comptime var i: u8 = 0;
150 inline while (i < 16) : (i += 1) {
149 comptime var i: u8 = 1;
150 inline while (i < pc.len) : (i += 1) {
151151 t.cMov(pc[i], ((@as(usize, b ^ i) -% 1) >> 8) & 1);
152152 }
153153 return t;
154154 }
155155
156 fn pcMul(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 {
156 fn nonAdjacentForm(s: [32]u8) [2 * 32]i8 {
157 var e: [2 * 32]i8 = undefined;
158 for (s) |x, i| {
159 e[i * 2 + 0] = @as(i8, @truncate(u4, x));
160 e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4));
161 }
162 // Now, e[0..63] is between 0 and 15, e[63] is between 0 and 7
163 var carry: i8 = 0;
164 for (e[0..63]) |*x| {
165 x.* += carry;
166 carry = (x.* + 8) >> 4;
167 x.* -= carry * 16;
168 }
169 e[63] += carry;
170 // Now, e[*] is between -8 and 8, including e[63]
171 return e;
172 }
173
174 // Scalar multiplication with a 4-bit window and the first 8 multiples.
175 // This requires the scalar to be converted to non-adjacent form.
176 // Based on real-world benchmarks, we only use this for multi-scalar multiplication.
177 // NAF could be useful to half the size of precomputation tables, but we intentionally
178 // avoid these to keep the standard library lightweight.
179 fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 {
180 std.debug.assert(vartime);
181 const e = nonAdjacentForm(s);
182 var q = Edwards25519.identityElement;
183 var pos: usize = 2 * 32 - 1;
184 while (true) : (pos -= 1) {
185 const slot = e[pos];
186 if (slot > 0) {
187 q = q.add(pc[@intCast(usize, slot)]);
188 } else if (slot < 0) {
189 q = q.sub(pc[@intCast(usize, -slot)]);
190 }
191 if (pos == 0) break;
192 q = q.dbl().dbl().dbl().dbl();
193 }
194 try q.rejectIdentity();
195 return q;
196 }
197
198 // Scalar multiplication with a 4-bit window and the first 15 multiples.
199 fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 {
157200 var q = Edwards25519.identityElement;
158201 var pos: usize = 252;
159202 while (true) : (pos -= 4) {
160 q = q.dbl().dbl().dbl().dbl();
161 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf;
203 const slot = @truncate(u4, (s[pos >> 3] >> @truncate(u3, pos)));
162204 if (vartime) {
163 if (bit != 0) {
164 q = q.add(pc[bit]);
205 if (slot != 0) {
206 q = q.add(pc[slot]);
165207 }
166208 } else {
167 q = q.add(pcSelect(pc, bit));
209 q = q.add(pcSelect(16, pc, slot));
168210 }
169211 if (pos == 0) break;
212 q = q.dbl().dbl().dbl().dbl();
170213 }
171214 try q.rejectIdentity();
172215 return q;
173216 }
174217
175 fn precompute(p: Edwards25519) [16]Edwards25519 {
176 var pc: [16]Edwards25519 = undefined;
218 fn precompute(p: Edwards25519, comptime count: usize) [1 + count]Edwards25519 {
219 var pc: [1 + count]Edwards25519 = undefined;
177220 pc[0] = Edwards25519.identityElement;
178221 pc[1] = p;
179222 var i: usize = 2;
180 while (i < 16) : (i += 1) {
223 while (i <= count) : (i += 1) {
181224 pc[i] = pc[i - 1].add(p);
182225 }
183226 return pc;
184227 }
185228
229 const basePointPc = comptime pc: {
230 @setEvalBranchQuota(10000);
231 break :pc precompute(Edwards25519.basePoint, 15);
232 };
233
186234 /// Multiply an Edwards25519 point by a scalar without clamping it.
187235 /// Return error.WeakPublicKey if the resulting point is
188236 /// the identity element.
189237 pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 {
190 var pc: [16]Edwards25519 = undefined;
191 if (p.is_base) {
192 @setEvalBranchQuota(10000);
193 pc = comptime precompute(Edwards25519.basePoint);
194 } else {
195 pc = precompute(p);
196 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
197 }
198 return pcMul(pc, s, false);
238 const pc = if (p.is_base) basePointPc else pc: {
239 const xpc = precompute(p, 15);
240 xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
241 break :pc xpc;
242 };
243 return pcMul16(pc, s, false);
199244 }
200245
201246 /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
202247 /// This can be used for signature verification.
203248 pub fn mulPublic(p: Edwards25519, s: [32]u8) !Edwards25519 {
204 var pc: [16]Edwards25519 = undefined;
205249 if (p.is_base) {
206 @setEvalBranchQuota(10000);
207 pc = comptime precompute(Edwards25519.basePoint);
250 return pcMul16(basePointPc, s, true);
208251 } else {
209 pc = precompute(p);
252 const pc = precompute(p, 8);
210253 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
254 return pcMul(pc, s, true);
211255 }
212 return pcMul(pc, s, true);
213256 }
214257
215258 /// Multiscalar multiplication *IN VARIABLE TIME* for public data
216259 /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually
217260 pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) !Edwards25519 {
218 var pcs: [count][16]Edwards25519 = undefined;
261 var pcs: [count][9]Edwards25519 = undefined;
219262 for (ps) |p, i| {
220263 if (p.is_base) {
221264 @setEvalBranchQuota(10000);
222 pcs[i] = comptime precompute(Edwards25519.basePoint);
265 pcs[i] = comptime precompute(Edwards25519.basePoint, 8);
223266 } else {
224 pcs[i] = precompute(p);
267 pcs[i] = precompute(p, 8);
225268 pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey;
226269 }
227270 }
271 var es: [count][2 * 32]i8 = undefined;
272 for (ss) |s, i| {
273 es[i] = nonAdjacentForm(s);
274 }
228275 var q = Edwards25519.identityElement;
229 var pos: usize = 252;
230 while (true) : (pos -= 4) {
231 q = q.dbl().dbl().dbl().dbl();
232 for (ss) |s, i| {
233 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf;
234 if (bit != 0) {
235 q = q.add(pcs[i][bit]);
276 var pos: usize = 2 * 32 - 1;
277 while (true) : (pos -= 1) {
278 for (es) |e, i| {
279 const slot = e[pos];
280 if (slot > 0) {
281 q = q.add(pcs[i][@intCast(usize, slot)]);
282 } else if (slot < 0) {
283 q = q.sub(pcs[i][@intCast(usize, -slot)]);
236284 }
237285 }
238286 if (pos == 0) break;
287 q = q.dbl().dbl().dbl().dbl();
239288 }
240289 try q.rejectIdentity();
241290 return q;