authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-11-07 12:29:28+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-17 17:06:38-08:00
log0d9c474ecfaf58698774022211d06da97d2fd648
treedf48fb7b794168c4b2b216ee4d481009cc2a7ba8
parentfa27420b72b81126ae7880f2da41218bdf9941e9

std/crypto: implement the Hash-To-Curve standard for Edwards25519

https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve This is quite an important feature to have since many other standards being worked on depend on this operation. Brings a couple useful arithmetic operations on field elements by the way. This PR also adds comments to the functions we expose in 25519/field so that they can appear in the generated documentation.

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

lib/std/crypto/25519/edwards25519.zig+163
...@@ -4,7 +4,9 @@...@@ -4,7 +4,9 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std");6const std = @import("std");
7const debug = std.debug;
7const fmt = std.fmt;8const fmt = std.fmt;
9const mem = std.mem;
810
9/// Group operations over Edwards25519.11/// Group operations over Edwards25519.
10pub const Edwards25519 = struct {12pub const Edwards25519 = struct {
...@@ -250,8 +252,150 @@ pub const Edwards25519 = struct {...@@ -250,8 +252,150 @@ pub const Edwards25519 = struct {
250 scalar.clamp(&t);252 scalar.clamp(&t);
251 return mul(p, t);253 return mul(p, t);
252 }254 }
255
256 // montgomery -- recover y = sqrt(x^3 + A*x^2 + x)
257 fn xmontToYmont(x: Fe) !Fe {
258 var x2 = x.sq();
259 const x3 = x.mul(x2);
260 x2 = x2.mul32(Fe.edwards25519a_32);
261 return x.add(x2).add(x3).sqrt();
262 }
263
264 // montgomery affine coordinates to edwards extended coordinates
265 fn montToEd(x: Fe, y: Fe) Edwards25519 {
266 const x_plus_one = x.add(Fe.one);
267 const x_minus_one = x.sub(Fe.one);
268 const x_plus_one_y_inv = x_plus_one.mul(y).invert(); // 1/((x+1)*y)
269
270 // xed = sqrt(-A-2)*x/y
271 const xed = x.mul(Fe.edwards25519sqrtam2).mul(x_plus_one_y_inv).mul(x_plus_one);
272
273 // yed = (x-1)/(x+1) or 1 if the denominator is 0
274 var yed = x_plus_one_y_inv.mul(y).mul(x_minus_one);
275 yed.cMov(Fe.one, @boolToInt(x_plus_one_y_inv.isZero()));
276
277 return Edwards25519{
278 .x = xed,
279 .y = yed,
280 .z = Fe.one,
281 .t = xed.mul(yed),
282 };
283 }
284
285 /// Elligator2 map - Returns Montgomery affine coordinates
286 pub fn elligator2(r: Fe) struct { x: Fe, y: Fe, not_square: bool } {
287 const rr2 = r.sq2().add(Fe.one).invert();
288 var x = rr2.mul32(Fe.edwards25519a_32).neg(); // x=x1
289 var x2 = x.sq();
290 const x3 = x2.mul(x);
291 x2 = x2.mul32(Fe.edwards25519a_32); // x2 = A*x1^2
292 const gx1 = x3.add(x).add(x2); // gx1 = x1^3 + A*x1^2 + x1
293 const not_square = !gx1.isSquare();
294
295 // gx1 not a square => x = -x1-A
296 x.cMov(x.neg(), @boolToInt(not_square));
297 x2 = Fe.zero;
298 x2.cMov(Fe.edwards25519a, @boolToInt(not_square));
299 x = x.sub(x2);
300
301 // We have y = sqrt(gx1) or sqrt(gx2) with gx2 = gx1*(A+x1)/(-x1)
302 // but it is about as fast to just recompute y from the curve equation.
303 const y = xmontToYmont(x) catch unreachable;
304 return .{ .x = x, .y = y, .not_square = not_square };
305 }
306
307 /// Map a 64-bit hash into an Edwards25519 point
308 pub fn fromHash(h: [64]u8) Edwards25519 {
309 const fe_f = Fe.fromBytes64(h);
310 var elr = elligator2(fe_f);
311
312 const y_sign = elr.not_square;
313 const y_neg = elr.y.neg();
314 elr.y.cMov(y_neg, @boolToInt(elr.y.isNegative()) ^ @boolToInt(y_sign));
315 return montToEd(elr.x, elr.y).clearCofactor();
316 }
317
318 fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 {
319 debug.assert(n <= 2);
320 const H = std.crypto.hash.sha2.Sha512;
321 const h_l: usize = 48;
322 var xctx = ctx;
323 var hctx: [H.digest_length]u8 = undefined;
324 if (ctx.len > 0xff) {
325 var st = H.init(.{});
326 st.update("H2C-OVERSIZE-DST-");
327 st.update(ctx);
328 st.final(&hctx);
329 xctx = hctx[0..];
330 }
331 const empty_block = [_]u8{0} ** H.block_length;
332 var t = [3]u8{ 0, n * h_l, 0 };
333 var xctx_len_u8 = [1]u8{@intCast(u8, xctx.len)};
334 var st = H.init(.{});
335 st.update(empty_block[0..]);
336 st.update(s);
337 st.update(t[0..]);
338 st.update(xctx);
339 st.update(xctx_len_u8[0..]);
340 var u_0: [H.digest_length]u8 = undefined;
341 st.final(&u_0);
342 var u: [n * H.digest_length]u8 = undefined;
343 var i: usize = 0;
344 while (i < n * H.digest_length) : (i += H.digest_length) {
345 mem.copy(u8, u[i..][0..H.digest_length], u_0[0..]);
346 var j: usize = 0;
347 while (i > 0 and j < H.digest_length) : (j += 1) {
348 u[i + j] ^= u[i + j - H.digest_length];
349 }
350 t[2] += 1;
351 st = H.init(.{});
352 st.update(u[i..][0..H.digest_length]);
353 st.update(t[2..3]);
354 st.update(xctx);
355 st.update(xctx_len_u8[0..]);
356 st.final(u[i..][0..H.digest_length]);
357 }
358 var px: [n]Edwards25519 = undefined;
359 i = 0;
360 while (i < n) : (i += 1) {
361 mem.set(u8, u_0[0 .. H.digest_length - h_l], 0);
362 mem.copy(u8, u_0[H.digest_length - h_l ..][0..h_l], u[i * h_l ..][0..h_l]);
363 px[i] = fromHash(u_0);
364 }
365 return px;
366 }
367
368 /// Hash a context `ctx` and a string `s` into an Edwards25519 point
369 ///
370 /// This function implements the edwards25519_XMD:SHA-512_ELL2_RO_ and edwards25519_XMD:SHA-512_ELL2_NU_
371 /// methods from the "Hashing to Elliptic Curves" standard document.
372 ///
373 /// Although not strictly required by the standard, it is recommended to avoid NUL characters in
374 /// the context in order to be compatible with other implementations.
375 pub fn fromString(comptime random_oracle: bool, ctx: []const u8, s: []const u8) Edwards25519 {
376 if (random_oracle) {
377 const px = stringToPoints(2, ctx, s);
378 return px[0].add(px[1]);
379 } else {
380 return stringToPoints(1, ctx, s)[0];
381 }
382 }
383
384 /// Map a 32 bit uniform bit string into an edwards25519 point
385 pub fn fromUniform(r: [32]u8) Edwards25519 {
386 var s = r;
387 const x_sign = s[31] >> 7;
388 s[31] &= 0x7f;
389 const elr = elligator2(Fe.fromBytes(s));
390 var p = montToEd(elr.x, elr.y);
391 const p_neg = p.neg();
392 p.cMov(p_neg, @boolToInt(p.x.isNegative()) ^ x_sign);
393 return p.clearCofactor();
394 }
253};395};
254396
397const htest = @import("../test.zig");
398
255test "edwards25519 packing/unpacking" {399test "edwards25519 packing/unpacking" {
256 const s = [_]u8{170} ++ [_]u8{0} ** 31;400 const s = [_]u8{170} ++ [_]u8{0} ** 31;
257 var b = Edwards25519.basePoint;401 var b = Edwards25519.basePoint;
...@@ -301,3 +445,22 @@ test "edwards25519 point addition/substraction" {...@@ -301,3 +445,22 @@ test "edwards25519 point addition/substraction" {
301 std.testing.expectError(error.IdentityElement, p.sub(p).rejectIdentity());445 std.testing.expectError(error.IdentityElement, p.sub(p).rejectIdentity());
302 std.testing.expectError(error.IdentityElement, p.sub(q).add(q).sub(p).rejectIdentity());446 std.testing.expectError(error.IdentityElement, p.sub(q).add(q).sub(p).rejectIdentity());
303}447}
448
449test "edwards25519 uniform-to-point" {
450 var r = [32]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
451 var p = Edwards25519.fromUniform(r);
452 htest.assertEqual("0691eee3cf70a0056df6bfa03120635636581b5c4ea571dfc680f78c7e0b4137", p.toBytes()[0..]);
453
454 r[31] = 0xff;
455 p = Edwards25519.fromUniform(r);
456 htest.assertEqual("f70718e68ef42d90ca1d936bb2d7e159be6c01d8095d39bd70487c82fe5c973a", p.toBytes()[0..]);
457}
458
459// Test vectors from draft-irtf-cfrg-hash-to-curve-10
460test "edwards25519 hash-to-curve operation" {
461 var p = Edwards25519.fromString(true, "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_", "abc");
462 htest.assertEqual("31558a26887f23fb8218f143e69d5f0af2e7831130bd5b432ef23883b895831a", p.toBytes()[0..]);
463
464 p = Edwards25519.fromString(false, "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_", "abc");
465 htest.assertEqual("42fa27c8f5a1ae0aa38bb59d5938e5145622ba5dedd11d11736fa2f9502d73e7", p.toBytes()[0..]);
466}
lib/std/crypto/25519/field.zig+105-6
...@@ -12,26 +12,46 @@ pub const Fe = struct {...@@ -12,26 +12,46 @@ pub const Fe = struct {
1212
13 const MASK51: u64 = 0x7ffffffffffff;13 const MASK51: u64 = 0x7ffffffffffff;
1414
15 /// 0
15 pub const zero = Fe{ .limbs = .{ 0, 0, 0, 0, 0 } };16 pub const zero = Fe{ .limbs = .{ 0, 0, 0, 0, 0 } };
1617
18 /// 1
17 pub const one = Fe{ .limbs = .{ 1, 0, 0, 0, 0 } };19 pub const one = Fe{ .limbs = .{ 1, 0, 0, 0, 0 } };
1820
19 pub const sqrtm1 = Fe{ .limbs = .{ 1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133 } }; // sqrt(-1)21 /// sqrt(-1)
22 pub const sqrtm1 = Fe{ .limbs = .{ 1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133 } };
2023
24 /// The Curve25519 base point
21 pub const curve25519BasePoint = Fe{ .limbs = .{ 9, 0, 0, 0, 0 } };25 pub const curve25519BasePoint = Fe{ .limbs = .{ 9, 0, 0, 0, 0 } };
2226
23 pub const edwards25519d = Fe{ .limbs = .{ 929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575 } }; // 3709570593466943934313808350875456518954211387984321901638878553308594028355527 /// Edwards25519 d = 37095705934669439343138083508754565189542113879843219016388785533085940283555
28 pub const edwards25519d = Fe{ .limbs = .{ 929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575 } };
2429
25 pub const edwards25519d2 = Fe{ .limbs = .{ 1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903 } }; // 2d30 /// Edwards25519 2d
31 pub const edwards25519d2 = Fe{ .limbs = .{ 1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903 } };
2632
27 pub const edwards25519sqrtamd = Fe{ .limbs = .{ 278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447 } }; // 1/sqrt(a-d)33 /// Edwards25519 1/sqrt(a-d)
34 pub const edwards25519sqrtamd = Fe{ .limbs = .{ 278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447 } };
2835
29 pub const edwards25519eonemsqd = Fe{ .limbs = .{ 1136626929484150, 1998550399581263, 496427632559748, 118527312129759, 45110755273534 } }; // 1-d^236 /// Edwards25519 1-d^2
37 pub const edwards25519eonemsqd = Fe{ .limbs = .{ 1136626929484150, 1998550399581263, 496427632559748, 118527312129759, 45110755273534 } };
3038
31 pub const edwards25519sqdmone = Fe{ .limbs = .{ 1507062230895904, 1572317787530805, 683053064812840, 317374165784489, 1572899562415810 } }; // (d-1)^239 /// Edwards25519 (d-1)^2
40 pub const edwards25519sqdmone = Fe{ .limbs = .{ 1507062230895904, 1572317787530805, 683053064812840, 317374165784489, 1572899562415810 } };
3241
42 /// Edwards25519 sqrt(ad-1) with a = -1 (mod p)
33 pub const edwards25519sqrtadm1 = Fe{ .limbs = .{ 2241493124984347, 425987919032274, 2207028919301688, 1220490630685848, 974799131293748 } };43 pub const edwards25519sqrtadm1 = Fe{ .limbs = .{ 2241493124984347, 425987919032274, 2207028919301688, 1220490630685848, 974799131293748 } };
3444
45 /// Edwards25519 A, as a single limb
46 pub const edwards25519a_32: u32 = 486662;
47
48 /// Edwards25519 A
49 pub const edwards25519a = Fe{ .limbs = .{ @as(u64, edwards25519a_32), 0, 0, 0, 0 } };
50
51 /// Edwards25519 sqrt(A-2)
52 pub const edwards25519sqrtam2 = Fe{ .limbs = .{ 1693982333959686, 608509411481997, 2235573344831311, 947681270984193, 266558006233600 } };
53
54 /// Return true if the field element is zero
35 pub inline fn isZero(fe: Fe) bool {55 pub inline fn isZero(fe: Fe) bool {
36 var reduced = fe;56 var reduced = fe;
37 reduced.reduce();57 reduced.reduce();
...@@ -39,10 +59,12 @@ pub const Fe = struct {...@@ -39,10 +59,12 @@ pub const Fe = struct {
39 return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0;59 return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0;
40 }60 }
4161
62 /// Return true if both field elements are equivalent
42 pub inline fn equivalent(a: Fe, b: Fe) bool {63 pub inline fn equivalent(a: Fe, b: Fe) bool {
43 return a.sub(b).isZero();64 return a.sub(b).isZero();
44 }65 }
4566
67 /// Unpack a field element
46 pub fn fromBytes(s: [32]u8) Fe {68 pub fn fromBytes(s: [32]u8) Fe {
47 var fe: Fe = undefined;69 var fe: Fe = undefined;
48 fe.limbs[0] = readIntLittle(u64, s[0..8]) & MASK51;70 fe.limbs[0] = readIntLittle(u64, s[0..8]) & MASK51;
...@@ -54,6 +76,7 @@ pub const Fe = struct {...@@ -54,6 +76,7 @@ pub const Fe = struct {
54 return fe;76 return fe;
55 }77 }
5678
79 /// Pack a field element
57 pub fn toBytes(fe: Fe) [32]u8 {80 pub fn toBytes(fe: Fe) [32]u8 {
58 var reduced = fe;81 var reduced = fe;
59 reduced.reduce();82 reduced.reduce();
...@@ -66,6 +89,29 @@ pub const Fe = struct {...@@ -66,6 +89,29 @@ pub const Fe = struct {
66 return s;89 return s;
67 }90 }
6891
92 /// Map a 64-bit big endian string into a field element
93 pub fn fromBytes64(s: [64]u8) Fe {
94 var fl: [32]u8 = undefined;
95 var gl: [32]u8 = undefined;
96 var i: usize = 0;
97 while (i < 32) : (i += 1) {
98 fl[i] = s[63 - i];
99 gl[i] = s[31 - i];
100 }
101 fl[31] &= 0x7f;
102 gl[31] &= 0x7f;
103 var fe_f = fromBytes(fl);
104 const fe_g = fromBytes(gl);
105 fe_f.limbs[0] += (s[32] >> 7) * 19;
106 i = 0;
107 while (i < 5) : (i += 1) {
108 fe_f.limbs[i] += 38 * fe_g.limbs[i];
109 }
110 fe_f.reduce();
111 return fe_f;
112 }
113
114 /// Reject non-canonical encodings of an element, possibly ignoring the top bit
69 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void {115 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void {
70 var c: u16 = (s[31] & 0x7f) ^ 0x7f;116 var c: u16 = (s[31] & 0x7f) ^ 0x7f;
71 comptime var i = 30;117 comptime var i = 30;
...@@ -80,6 +126,7 @@ pub const Fe = struct {...@@ -80,6 +126,7 @@ pub const Fe = struct {
80 }126 }
81 }127 }
82128
129 /// Reduce a field element mod 2^255-19
83 fn reduce(fe: *Fe) void {130 fn reduce(fe: *Fe) void {
84 comptime var i = 0;131 comptime var i = 0;
85 comptime var j = 0;132 comptime var j = 0;
...@@ -116,6 +163,7 @@ pub const Fe = struct {...@@ -116,6 +163,7 @@ pub const Fe = struct {
116 limbs[4] &= MASK51;163 limbs[4] &= MASK51;
117 }164 }
118165
166 /// Add a field element
119 pub inline fn add(a: Fe, b: Fe) Fe {167 pub inline fn add(a: Fe, b: Fe) Fe {
120 var fe: Fe = undefined;168 var fe: Fe = undefined;
121 comptime var i = 0;169 comptime var i = 0;
...@@ -125,6 +173,7 @@ pub const Fe = struct {...@@ -125,6 +173,7 @@ pub const Fe = struct {
125 return fe;173 return fe;
126 }174 }
127175
176 /// Substract a field elememnt
128 pub inline fn sub(a: Fe, b: Fe) Fe {177 pub inline fn sub(a: Fe, b: Fe) Fe {
129 var fe = b;178 var fe = b;
130 comptime var i = 0;179 comptime var i = 0;
...@@ -143,14 +192,17 @@ pub const Fe = struct {...@@ -143,14 +192,17 @@ pub const Fe = struct {
143 return fe;192 return fe;
144 }193 }
145194
195 /// Negate a field element
146 pub inline fn neg(a: Fe) Fe {196 pub inline fn neg(a: Fe) Fe {
147 return zero.sub(a);197 return zero.sub(a);
148 }198 }
149199
200 /// Return true if a field element is negative
150 pub inline fn isNegative(a: Fe) bool {201 pub inline fn isNegative(a: Fe) bool {
151 return (a.toBytes()[0] & 1) != 0;202 return (a.toBytes()[0] & 1) != 0;
152 }203 }
153204
205 /// Conditonally replace a field element with `a` if `c` is positive
154 pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void {206 pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void {
155 const mask: u64 = 0 -% c;207 const mask: u64 = 0 -% c;
156 var x = fe.*;208 var x = fe.*;
...@@ -168,6 +220,7 @@ pub const Fe = struct {...@@ -168,6 +220,7 @@ pub const Fe = struct {
168 }220 }
169 }221 }
170222
223 /// Conditionally swap two pairs of field elements if `c` is positive
171 pub fn cSwap2(a0: *Fe, b0: *Fe, a1: *Fe, b1: *Fe, c: u64) void {224 pub fn cSwap2(a0: *Fe, b0: *Fe, a1: *Fe, b1: *Fe, c: u64) void {
172 const mask: u64 = 0 -% c;225 const mask: u64 = 0 -% c;
173 var x0 = a0.*;226 var x0 = a0.*;
...@@ -211,6 +264,7 @@ pub const Fe = struct {...@@ -211,6 +264,7 @@ pub const Fe = struct {
211 return .{ .limbs = rs };264 return .{ .limbs = rs };
212 }265 }
213266
267 /// Multiply two field elements
214 pub inline fn mul(a: Fe, b: Fe) Fe {268 pub inline fn mul(a: Fe, b: Fe) Fe {
215 var ax: [5]u128 = undefined;269 var ax: [5]u128 = undefined;
216 var bx: [5]u128 = undefined;270 var bx: [5]u128 = undefined;
...@@ -262,14 +316,17 @@ pub const Fe = struct {...@@ -262,14 +316,17 @@ pub const Fe = struct {
262 return _carry128(&r);316 return _carry128(&r);
263 }317 }
264318
319 /// Square a field element
265 pub inline fn sq(a: Fe) Fe {320 pub inline fn sq(a: Fe) Fe {
266 return _sq(a, false);321 return _sq(a, false);
267 }322 }
268323
324 /// Square and double a field element
269 pub inline fn sq2(a: Fe) Fe {325 pub inline fn sq2(a: Fe) Fe {
270 return _sq(a, true);326 return _sq(a, true);
271 }327 }
272328
329 /// Multiply a field element with a small (32-bit) integer
273 pub inline fn mul32(a: Fe, comptime n: u32) Fe {330 pub inline fn mul32(a: Fe, comptime n: u32) Fe {
274 const sn = @intCast(u128, n);331 const sn = @intCast(u128, n);
275 var fe: Fe = undefined;332 var fe: Fe = undefined;
...@@ -284,6 +341,7 @@ pub const Fe = struct {...@@ -284,6 +341,7 @@ pub const Fe = struct {
284 return fe;341 return fe;
285 }342 }
286343
344 /// Square a field element `n` times
287 inline fn sqn(a: Fe, comptime n: comptime_int) Fe {345 inline fn sqn(a: Fe, comptime n: comptime_int) Fe {
288 var i: usize = 0;346 var i: usize = 0;
289 var fe = a;347 var fe = a;
...@@ -293,6 +351,7 @@ pub const Fe = struct {...@@ -293,6 +351,7 @@ pub const Fe = struct {
293 return fe;351 return fe;
294 }352 }
295353
354 /// Compute the inverse of a field element
296 pub fn invert(a: Fe) Fe {355 pub fn invert(a: Fe) Fe {
297 var t0 = a.sq();356 var t0 = a.sq();
298 var t1 = t0.sqn(2).mul(a);357 var t1 = t0.sqn(2).mul(a);
...@@ -306,6 +365,8 @@ pub const Fe = struct {...@@ -306,6 +365,8 @@ pub const Fe = struct {
306 return t1.mul(t2.mul(t2.sqn(100)).sqn(50)).sqn(5).mul(t0);365 return t1.mul(t2.mul(t2.sqn(100)).sqn(50)).sqn(5).mul(t0);
307 }366 }
308367
368 /// Return a^((p-5)/8) = a^(2^252-3)
369 /// Used to compute square roots since we have p=5 (mod 8); see Cohen and Frey.
309 pub fn pow2523(a: Fe) Fe {370 pub fn pow2523(a: Fe) Fe {
310 var t0 = a.mul(a.sq());371 var t0 = a.mul(a.sq());
311 var t1 = t0.mul(t0.sqn(2)).sq().mul(a);372 var t1 = t0.mul(t0.sqn(2)).sq().mul(a);
...@@ -317,9 +378,47 @@ pub const Fe = struct {...@@ -317,9 +378,47 @@ pub const Fe = struct {
317 return t1.sqn(120).mul(t1).sqn(10).mul(t0).sqn(2).mul(a);378 return t1.sqn(120).mul(t1).sqn(10).mul(t0).sqn(2).mul(a);
318 }379 }
319380
381 /// Return the absolute value of a field element
320 pub fn abs(a: Fe) Fe {382 pub fn abs(a: Fe) Fe {
321 var r = a;383 var r = a;
322 r.cMov(a.neg(), @boolToInt(a.isNegative()));384 r.cMov(a.neg(), @boolToInt(a.isNegative()));
323 return r;385 return r;
324 }386 }
387
388 /// Return true if the field element is a square
389 pub fn isSquare(a: Fe) bool {
390 // Compute the Jacobi symbol x^((p-1)/2)
391 const _11 = a.mul(a.sq());
392 const _1111 = _11.mul(_11.sq().sq());
393 const _11111111 = _1111.mul(_1111.sq().sq().sq().sq());
394 var t = _11111111.sqn(2).mul(_11);
395 const u = t;
396 t = t.sqn(10).mul(u).sqn(10).mul(u);
397 t = t.sqn(30).mul(t);
398 t = t.sqn(60).mul(t);
399 t = t.sqn(120).mul(t).sqn(10).mul(u).sqn(3).mul(_11).sq();
400 return @bitCast(bool, @truncate(u1, ~(t.toBytes()[1] & 1)));
401 }
402
403 fn uncheckedSqrt(x2: Fe) Fe {
404 var e = x2.pow2523();
405 const p_root = e.mul(x2); // positive root
406 const m_root = p_root.mul(Fe.sqrtm1); // negative root
407 const m_root2 = m_root.sq();
408 e = x2.sub(m_root2);
409 var x = p_root;
410 x.cMov(m_root, @boolToInt(e.isZero()));
411 return x;
412 }
413
414 /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square
415 pub fn sqrt(x2: Fe) !Fe {
416 var x2_copy = x2;
417 const x = x2.uncheckedSqrt();
418 const check = x.sq().sub(x2_copy);
419 if (check.isZero()) {
420 return x;
421 }
422 return error.NotSquare;
423 }
325};424};