| ... | ... | @@ -0,0 +1,667 @@ |
| 1 | // Translated from monocypher which is licensed under CC-0/BSD-3. |
| 2 | // |
| 3 | // https://monocypher.org/ |
| 4 | |
| 5 | const std = @import("../index.zig"); |
| 6 | const builtin = @import("builtin"); |
| 7 | |
| 8 | const Endian = builtin.Endian; |
| 9 | const readInt = std.mem.readInt; |
| 10 | const writeInt = std.mem.writeInt; |
| 11 | |
| 12 | // Constant time compare to zero. |
| 13 | fn zerocmp(comptime T: type, a: []const T) bool { |
| 14 | var s: T = 0; |
| 15 | for (a) |b| { |
| 16 | s |= b; |
| 17 | } |
| 18 | return s == 0; |
| 19 | } |
| 20 | |
| 21 | //////////////////////////////////// |
| 22 | /// Arithmetic modulo 2^255 - 19 /// |
| 23 | //////////////////////////////////// |
| 24 | // Taken from Supercop's ref10 implementation. |
| 25 | // A bit bigger than TweetNaCl, over 4 times faster. |
| 26 | |
| 27 | // field element |
| 28 | const Fe = struct { |
| 29 | b: [10]i32, |
| 30 | |
| 31 | fn secure_zero(self: *Fe) void { |
| 32 | std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]); |
| 33 | } |
| 34 | }; |
| 35 | |
| 36 | fn fe_0(h: *Fe) void { |
| 37 | for (h.b) |*e| { |
| 38 | e.* = 0; |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | fn fe_1(h: *Fe) void { |
| 43 | for (h.b[1..]) |*e| { |
| 44 | e.* = 0; |
| 45 | } |
| 46 | h.b[0] = 1; |
| 47 | } |
| 48 | |
| 49 | fn fe_copy(h: *Fe, f: *const Fe) void { |
| 50 | for (h.b) |_, i| { |
| 51 | h.b[i] = f.b[i]; |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | fn fe_neg(h: *Fe, f: *const Fe) void { |
| 56 | for (h.b) |_, i| { |
| 57 | h.b[i] = -f.b[i]; |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | fn fe_add(h: *Fe, f: *const Fe, g: *const Fe) void { |
| 62 | for (h.b) |_, i| { |
| 63 | h.b[i] = f.b[i] + g.b[i]; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | fn fe_sub(h: *Fe, f: *const Fe, g: *const Fe) void { |
| 68 | for (h.b) |_, i| { |
| 69 | h.b[i] = f.b[i] - g.b[i]; |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | fn fe_cswap(f: *Fe, g: *Fe, b: i32) void { |
| 74 | for (f.b) |_, i| { |
| 75 | const x = (f.b[i] ^ g.b[i]) & -b; |
| 76 | f.b[i] ^= x; |
| 77 | g.b[i] ^= x; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | fn fe_ccopy(f: *Fe, g: *const Fe, b: i32) void { |
| 82 | for (f.b) |_, i| { |
| 83 | const x = (f.b[i] ^ g.b[i]) & -b; |
| 84 | f.b[i] ^= x; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | inline fn carryround(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void { |
| 89 | const j = (i + 1) % 10; |
| 90 | |
| 91 | c[i] = (t[i] + (i64(1) << shift)) >> (shift + 1); |
| 92 | t[j] += c[i] * mult; |
| 93 | t[i] -= c[i] * (i64(1) << (shift + 1)); |
| 94 | } |
| 95 | |
| 96 | fn feCarry1(h: *Fe, t: []i64) void { |
| 97 | var c: [10]i64 = undefined; |
| 98 | |
| 99 | var sc = c[0..]; |
| 100 | var st = t[0..]; |
| 101 | |
| 102 | carryround(sc, st, 9, 24, 19); |
| 103 | carryround(sc, st, 1, 24, 1); |
| 104 | carryround(sc, st, 3, 24, 1); |
| 105 | carryround(sc, st, 5, 24, 1); |
| 106 | carryround(sc, st, 7, 24, 1); |
| 107 | carryround(sc, st, 0, 25, 1); |
| 108 | carryround(sc, st, 2, 25, 1); |
| 109 | carryround(sc, st, 4, 25, 1); |
| 110 | carryround(sc, st, 6, 25, 1); |
| 111 | carryround(sc, st, 8, 25, 1); |
| 112 | |
| 113 | for (h.b) |_, i| { |
| 114 | h.b[i] = @intCast(i32, t[i]); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | fn feCarry2(h: *Fe, t: []i64) void { |
| 119 | var c: [10]i64 = undefined; |
| 120 | |
| 121 | var sc = c[0..]; |
| 122 | var st = t[0..]; |
| 123 | |
| 124 | carryround(sc, st, 0, 25, 1); |
| 125 | carryround(sc, st, 4, 25, 1); |
| 126 | carryround(sc, st, 1, 24, 1); |
| 127 | carryround(sc, st, 5, 24, 1); |
| 128 | carryround(sc, st, 2, 25, 1); |
| 129 | carryround(sc, st, 6, 25, 1); |
| 130 | carryround(sc, st, 3, 24, 1); |
| 131 | carryround(sc, st, 7, 24, 1); |
| 132 | carryround(sc, st, 4, 25, 1); |
| 133 | carryround(sc, st, 8, 25, 1); |
| 134 | carryround(sc, st, 9, 24, 19); |
| 135 | carryround(sc, st, 0, 25, 1); |
| 136 | |
| 137 | for (h.b) |_, i| { |
| 138 | h.b[i] = @intCast(i32, t[i]); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // TODO: Use readInt(u24) but double check alignment since currently it produces different values. |
| 143 | fn load24_le(s: []const u8) u32 { |
| 144 | return s[0] | (u32(s[1]) << 8) | (u32(s[2]) << 16); |
| 145 | } |
| 146 | |
| 147 | fn fe_frombytes(h: *Fe, s: [32]u8) void { |
| 148 | var t: [10]i64 = undefined; |
| 149 | |
| 150 | t[0] = readInt(s[0..4], u32, Endian.Little); |
| 151 | t[1] = load24_le(s[4..7]) << 6; |
| 152 | t[2] = load24_le(s[7..10]) << 5; |
| 153 | t[3] = load24_le(s[10..13]) << 3; |
| 154 | t[4] = load24_le(s[13..16]) << 2; |
| 155 | t[5] = readInt(s[16..20], u32, Endian.Little); |
| 156 | t[6] = load24_le(s[20..23]) << 7; |
| 157 | t[7] = load24_le(s[23..26]) << 5; |
| 158 | t[8] = load24_le(s[26..29]) << 4; |
| 159 | t[9] = (load24_le(s[29..32]) & 0x7fffff) << 2; |
| 160 | |
| 161 | feCarry1(h, t[0..]); |
| 162 | } |
| 163 | |
| 164 | fn fe_mul_small(h: *Fe, f: *const Fe, comptime g: comptime_int) void { |
| 165 | var t: [10]i64 = undefined; |
| 166 | |
| 167 | for (t[0..]) |_, i| { |
| 168 | t[i] = i64(f.b[i]) * g; |
| 169 | } |
| 170 | |
| 171 | feCarry1(h, t[0..]); |
| 172 | } |
| 173 | |
| 174 | fn fe_mul121666(h: *Fe, f: *const Fe) void { |
| 175 | fe_mul_small(h, f, 121666); |
| 176 | } |
| 177 | |
| 178 | fn fe_mul(h: *Fe, f1: *const Fe, g1: *const Fe) void { |
| 179 | const f = f1.b; |
| 180 | const g = g1.b; |
| 181 | |
| 182 | var F: [10]i32 = undefined; |
| 183 | var G: [10]i32 = undefined; |
| 184 | |
| 185 | F[1] = f[1] * 2; |
| 186 | F[3] = f[3] * 2; |
| 187 | F[5] = f[5] * 2; |
| 188 | F[7] = f[7] * 2; |
| 189 | F[9] = f[9] * 2; |
| 190 | |
| 191 | G[1] = g[1] * 19; |
| 192 | G[2] = g[2] * 19; |
| 193 | G[3] = g[3] * 19; |
| 194 | G[4] = g[4] * 19; |
| 195 | G[5] = g[5] * 19; |
| 196 | G[6] = g[6] * 19; |
| 197 | G[7] = g[7] * 19; |
| 198 | G[8] = g[8] * 19; |
| 199 | G[9] = g[9] * 19; |
| 200 | |
| 201 | // t's become h |
| 202 | var t: [10]i64 = undefined; |
| 203 | |
| 204 | t[0] = f[0] * i64(g[0]) + F[1] * i64(G[9]) + f[2] * i64(G[8]) + F[3] * i64(G[7]) + f[4] * i64(G[6]) + F[5] * i64(G[5]) + f[6] * i64(G[4]) + F[7] * i64(G[3]) + f[8] * i64(G[2]) + F[9] * i64(G[1]); |
| 205 | t[1] = f[0] * i64(g[1]) + f[1] * i64(g[0]) + f[2] * i64(G[9]) + f[3] * i64(G[8]) + f[4] * i64(G[7]) + f[5] * i64(G[6]) + f[6] * i64(G[5]) + f[7] * i64(G[4]) + f[8] * i64(G[3]) + f[9] * i64(G[2]); |
| 206 | t[2] = f[0] * i64(g[2]) + F[1] * i64(g[1]) + f[2] * i64(g[0]) + F[3] * i64(G[9]) + f[4] * i64(G[8]) + F[5] * i64(G[7]) + f[6] * i64(G[6]) + F[7] * i64(G[5]) + f[8] * i64(G[4]) + F[9] * i64(G[3]); |
| 207 | t[3] = f[0] * i64(g[3]) + f[1] * i64(g[2]) + f[2] * i64(g[1]) + f[3] * i64(g[0]) + f[4] * i64(G[9]) + f[5] * i64(G[8]) + f[6] * i64(G[7]) + f[7] * i64(G[6]) + f[8] * i64(G[5]) + f[9] * i64(G[4]); |
| 208 | t[4] = f[0] * i64(g[4]) + F[1] * i64(g[3]) + f[2] * i64(g[2]) + F[3] * i64(g[1]) + f[4] * i64(g[0]) + F[5] * i64(G[9]) + f[6] * i64(G[8]) + F[7] * i64(G[7]) + f[8] * i64(G[6]) + F[9] * i64(G[5]); |
| 209 | t[5] = f[0] * i64(g[5]) + f[1] * i64(g[4]) + f[2] * i64(g[3]) + f[3] * i64(g[2]) + f[4] * i64(g[1]) + f[5] * i64(g[0]) + f[6] * i64(G[9]) + f[7] * i64(G[8]) + f[8] * i64(G[7]) + f[9] * i64(G[6]); |
| 210 | t[6] = f[0] * i64(g[6]) + F[1] * i64(g[5]) + f[2] * i64(g[4]) + F[3] * i64(g[3]) + f[4] * i64(g[2]) + F[5] * i64(g[1]) + f[6] * i64(g[0]) + F[7] * i64(G[9]) + f[8] * i64(G[8]) + F[9] * i64(G[7]); |
| 211 | t[7] = f[0] * i64(g[7]) + f[1] * i64(g[6]) + f[2] * i64(g[5]) + f[3] * i64(g[4]) + f[4] * i64(g[3]) + f[5] * i64(g[2]) + f[6] * i64(g[1]) + f[7] * i64(g[0]) + f[8] * i64(G[9]) + f[9] * i64(G[8]); |
| 212 | t[8] = f[0] * i64(g[8]) + F[1] * i64(g[7]) + f[2] * i64(g[6]) + F[3] * i64(g[5]) + f[4] * i64(g[4]) + F[5] * i64(g[3]) + f[6] * i64(g[2]) + F[7] * i64(g[1]) + f[8] * i64(g[0]) + F[9] * i64(G[9]); |
| 213 | t[9] = f[0] * i64(g[9]) + f[1] * i64(g[8]) + f[2] * i64(g[7]) + f[3] * i64(g[6]) + f[4] * i64(g[5]) + f[5] * i64(g[4]) + f[6] * i64(g[3]) + f[7] * i64(g[2]) + f[8] * i64(g[1]) + f[9] * i64(g[0]); |
| 214 | |
| 215 | feCarry2(h, t[0..]); |
| 216 | } |
| 217 | |
| 218 | // we could use fe_mul() for this, but this is significantly faster |
| 219 | fn fe_sq(h: *Fe, fz: *const Fe) void { |
| 220 | const f0 = fz.b[0]; |
| 221 | const f1 = fz.b[1]; |
| 222 | const f2 = fz.b[2]; |
| 223 | const f3 = fz.b[3]; |
| 224 | const f4 = fz.b[4]; |
| 225 | const f5 = fz.b[5]; |
| 226 | const f6 = fz.b[6]; |
| 227 | const f7 = fz.b[7]; |
| 228 | const f8 = fz.b[8]; |
| 229 | const f9 = fz.b[9]; |
| 230 | |
| 231 | const f0_2 = f0 * 2; |
| 232 | const f1_2 = f1 * 2; |
| 233 | const f2_2 = f2 * 2; |
| 234 | const f3_2 = f3 * 2; |
| 235 | const f4_2 = f4 * 2; |
| 236 | const f5_2 = f5 * 2; |
| 237 | const f6_2 = f6 * 2; |
| 238 | const f7_2 = f7 * 2; |
| 239 | const f5_38 = f5 * 38; |
| 240 | const f6_19 = f6 * 19; |
| 241 | const f7_38 = f7 * 38; |
| 242 | const f8_19 = f8 * 19; |
| 243 | const f9_38 = f9 * 38; |
| 244 | |
| 245 | var t: [10]i64 = undefined; |
| 246 | |
| 247 | t[0] = f0 * i64(f0) + f1_2 * i64(f9_38) + f2_2 * i64(f8_19) + f3_2 * i64(f7_38) + f4_2 * i64(f6_19) + f5 * i64(f5_38); |
| 248 | t[1] = f0_2 * i64(f1) + f2 * i64(f9_38) + f3_2 * i64(f8_19) + f4 * i64(f7_38) + f5_2 * i64(f6_19); |
| 249 | t[2] = f0_2 * i64(f2) + f1_2 * i64(f1) + f3_2 * i64(f9_38) + f4_2 * i64(f8_19) + f5_2 * i64(f7_38) + f6 * i64(f6_19); |
| 250 | t[3] = f0_2 * i64(f3) + f1_2 * i64(f2) + f4 * i64(f9_38) + f5_2 * i64(f8_19) + f6 * i64(f7_38); |
| 251 | t[4] = f0_2 * i64(f4) + f1_2 * i64(f3_2) + f2 * i64(f2) + f5_2 * i64(f9_38) + f6_2 * i64(f8_19) + f7 * i64(f7_38); |
| 252 | t[5] = f0_2 * i64(f5) + f1_2 * i64(f4) + f2_2 * i64(f3) + f6 * i64(f9_38) + f7_2 * i64(f8_19); |
| 253 | t[6] = f0_2 * i64(f6) + f1_2 * i64(f5_2) + f2_2 * i64(f4) + f3_2 * i64(f3) + f7_2 * i64(f9_38) + f8 * i64(f8_19); |
| 254 | t[7] = f0_2 * i64(f7) + f1_2 * i64(f6) + f2_2 * i64(f5) + f3_2 * i64(f4) + f8 * i64(f9_38); |
| 255 | t[8] = f0_2 * i64(f8) + f1_2 * i64(f7_2) + f2_2 * i64(f6) + f3_2 * i64(f5_2) + f4 * i64(f4) + f9 * i64(f9_38); |
| 256 | t[9] = f0_2 * i64(f9) + f1_2 * i64(f8) + f2_2 * i64(f7) + f3_2 * i64(f6) + f4 * i64(f5_2); |
| 257 | |
| 258 | feCarry2(h, t[0..]); |
| 259 | } |
| 260 | |
| 261 | fn fe_sq2(h: *Fe, f: *const Fe) void { |
| 262 | fe_sq(h, f); |
| 263 | fe_mul_small(h, h, 2); |
| 264 | } |
| 265 | |
| 266 | // This could be simplified, but it would be slower |
| 267 | fn fe_invert(out: *Fe, z: *const Fe) void { |
| 268 | var i: usize = undefined; |
| 269 | |
| 270 | var t: [4]Fe = undefined; |
| 271 | var t0 = &t[0]; |
| 272 | var t1 = &t[1]; |
| 273 | var t2 = &t[2]; |
| 274 | var t3 = &t[3]; |
| 275 | |
| 276 | fe_sq(t0, z); |
| 277 | fe_sq(t1, t0); |
| 278 | fe_sq(t1, t1); |
| 279 | fe_mul(t1, z, t1); |
| 280 | fe_mul(t0, t0, t1); |
| 281 | |
| 282 | fe_sq(t2, t0); |
| 283 | fe_mul(t1, t1, t2); |
| 284 | |
| 285 | fe_sq(t2, t1); |
| 286 | i = 1; |
| 287 | while (i < 5) : (i += 1) fe_sq(t2, t2); |
| 288 | fe_mul(t1, t2, t1); |
| 289 | |
| 290 | fe_sq(t2, t1); |
| 291 | i = 1; |
| 292 | while (i < 10) : (i += 1) fe_sq(t2, t2); |
| 293 | fe_mul(t2, t2, t1); |
| 294 | |
| 295 | fe_sq(t3, t2); |
| 296 | i = 1; |
| 297 | while (i < 20) : (i += 1) fe_sq(t3, t3); |
| 298 | fe_mul(t2, t3, t2); |
| 299 | |
| 300 | fe_sq(t2, t2); |
| 301 | i = 1; |
| 302 | while (i < 10) : (i += 1) fe_sq(t2, t2); |
| 303 | fe_mul(t1, t2, t1); |
| 304 | |
| 305 | fe_sq(t2, t1); |
| 306 | i = 1; |
| 307 | while (i < 50) : (i += 1) fe_sq(t2, t2); |
| 308 | fe_mul(t2, t2, t1); |
| 309 | |
| 310 | fe_sq(t3, t2); |
| 311 | i = 1; |
| 312 | while (i < 100) : (i += 1) fe_sq(t3, t3); |
| 313 | fe_mul(t2, t3, t2); |
| 314 | |
| 315 | fe_sq(t2, t2); |
| 316 | i = 1; |
| 317 | while (i < 50) : (i += 1) fe_sq(t2, t2); |
| 318 | fe_mul(t1, t2, t1); |
| 319 | |
| 320 | fe_sq(t1, t1); |
| 321 | i = 1; |
| 322 | while (i < 5) : (i += 1) fe_sq(t1, t1); |
| 323 | fe_mul(out, t1, t0); |
| 324 | |
| 325 | t0.secure_zero(); |
| 326 | t1.secure_zero(); |
| 327 | t2.secure_zero(); |
| 328 | t3.secure_zero(); |
| 329 | } |
| 330 | |
| 331 | // This could be simplified, but it would be slower |
| 332 | fn fe_pow22523(out: *Fe, z: *const Fe) void { |
| 333 | var i: usize = undefined; |
| 334 | |
| 335 | var t: [3]Fe = undefined; |
| 336 | var t0 = &t[0]; |
| 337 | var t1 = &t[1]; |
| 338 | var t2 = &t[2]; |
| 339 | |
| 340 | fe_sq(t0, z); |
| 341 | fe_sq(t1, t0); |
| 342 | fe_sq(t1, t1); |
| 343 | fe_mul(t1, z, t1); |
| 344 | fe_mul(t0, t0, t1); |
| 345 | |
| 346 | fe_sq(t0, t0); |
| 347 | fe_mul(t0, t1, t0); |
| 348 | |
| 349 | fe_sq(t1, t0); |
| 350 | i = 1; |
| 351 | while (i < 5) : (i += 1) fe_sq(t1, t1); |
| 352 | fe_mul(t0, t1, t0); |
| 353 | |
| 354 | fe_sq(t1, t0); |
| 355 | i = 1; |
| 356 | while (i < 10) : (i += 1) fe_sq(t1, t1); |
| 357 | fe_mul(t1, t1, t0); |
| 358 | |
| 359 | fe_sq(t2, t1); |
| 360 | i = 1; |
| 361 | while (i < 20) : (i += 1) fe_sq(t2, t2); |
| 362 | fe_mul(t1, t2, t1); |
| 363 | |
| 364 | fe_sq(t1, t1); |
| 365 | i = 1; |
| 366 | while (i < 10) : (i += 1) fe_sq(t1, t1); |
| 367 | fe_mul(t0, t1, t0); |
| 368 | |
| 369 | fe_sq(t1, t0); |
| 370 | i = 1; |
| 371 | while (i < 50) : (i += 1) fe_sq(t1, t1); |
| 372 | fe_mul(t1, t1, t0); |
| 373 | |
| 374 | fe_sq(t2, t1); |
| 375 | i = 1; |
| 376 | while (i < 100) : (i += 1) fe_sq(t2, t2); |
| 377 | fe_mul(t1, t2, t1); |
| 378 | |
| 379 | fe_sq(t1, t1); |
| 380 | i = 1; |
| 381 | while (i < 50) : (i += 1) fe_sq(t1, t1); |
| 382 | fe_mul(t0, t1, t0); |
| 383 | |
| 384 | fe_sq(t0, t0); |
| 385 | i = 1; |
| 386 | while (i < 2) : (i += 1) fe_sq(t0, t0); |
| 387 | fe_mul(out, t0, z); |
| 388 | |
| 389 | t0.secure_zero(); |
| 390 | t1.secure_zero(); |
| 391 | t2.secure_zero(); |
| 392 | } |
| 393 | |
| 394 | inline fn tobytesround(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void { |
| 395 | c[i] = t[i] >> shift; |
| 396 | if (i + 1 < 10) { |
| 397 | t[i + 1] += c[i]; |
| 398 | } |
| 399 | t[i] -= c[i] * (i32(1) << shift); |
| 400 | } |
| 401 | |
| 402 | fn fe_tobytes(s: []u8, h: *const Fe) void { |
| 403 | std.debug.assert(s.len >= 32); |
| 404 | |
| 405 | var t: [10]i64 = undefined; |
| 406 | for (h.b[0..]) |_, i| { |
| 407 | t[i] = h.b[i]; |
| 408 | } |
| 409 | |
| 410 | var q = (19 * t[9] + ((i32(1) << 24))) >> 25; |
| 411 | { |
| 412 | var i: usize = 0; |
| 413 | while (i < 5) : (i += 1) { |
| 414 | q += t[2 * i]; |
| 415 | q >>= 26; |
| 416 | q += t[2 * i + 1]; |
| 417 | q >>= 25; |
| 418 | } |
| 419 | } |
| 420 | t[0] += 19 * q; |
| 421 | |
| 422 | var c: [10]i64 = undefined; |
| 423 | |
| 424 | var st = t[0..]; |
| 425 | var sc = c[0..]; |
| 426 | |
| 427 | tobytesround(sc, st, 0, 26); |
| 428 | tobytesround(sc, st, 1, 25); |
| 429 | tobytesround(sc, st, 2, 26); |
| 430 | tobytesround(sc, st, 3, 25); |
| 431 | tobytesround(sc, st, 4, 26); |
| 432 | tobytesround(sc, st, 5, 25); |
| 433 | tobytesround(sc, st, 6, 26); |
| 434 | tobytesround(sc, st, 7, 25); |
| 435 | tobytesround(sc, st, 8, 26); |
| 436 | tobytesround(sc, st, 9, 25); |
| 437 | |
| 438 | var ut: [10]u32 = undefined; |
| 439 | for (ut[0..]) |_, i| { |
| 440 | ut[i] = @bitCast(u32, @intCast(i32, t[i])); |
| 441 | } |
| 442 | |
| 443 | writeInt(s[0..], (ut[0] >> 0) | (ut[1] << 26), Endian.Little); |
| 444 | writeInt(s[4..], (ut[1] >> 6) | (ut[2] << 19), Endian.Little); |
| 445 | writeInt(s[8..], (ut[2] >> 13) | (ut[3] << 13), Endian.Little); |
| 446 | writeInt(s[12..], (ut[3] >> 19) | (ut[4] << 6), Endian.Little); |
| 447 | writeInt(s[16..], (ut[5] >> 0) | (ut[6] << 25), Endian.Little); |
| 448 | writeInt(s[20..], (ut[6] >> 7) | (ut[7] << 19), Endian.Little); |
| 449 | writeInt(s[24..], (ut[7] >> 13) | (ut[8] << 12), Endian.Little); |
| 450 | writeInt(s[28..], (ut[8] >> 20) | (ut[9] << 6), Endian.Little); |
| 451 | |
| 452 | std.mem.secureZero(i64, t[0..]); |
| 453 | } |
| 454 | |
| 455 | // Parity check. Returns 0 if even, 1 if odd |
| 456 | fn fe_isnegative(f: *const Fe) bool { |
| 457 | var s: [32]u8 = undefined; |
| 458 | fe_tobytes(s[0..], f); |
| 459 | const isneg = s[0] & 1; |
| 460 | s.secure_zero(); |
| 461 | return isneg; |
| 462 | } |
| 463 | |
| 464 | fn fe_isnonzero(f: *const Fe) bool { |
| 465 | var s: [32]u8 = undefined; |
| 466 | fe_tobytes(s[0..], f); |
| 467 | const isnonzero = zerocmp(u8, s[0..]); |
| 468 | s.secure_zero(); |
| 469 | return isneg; |
| 470 | } |
| 471 | |
| 472 | /////////////// |
| 473 | /// X-25519 /// Taken from Supercop's ref10 implementation. |
| 474 | /////////////// |
| 475 | fn trim_scalar(s: []u8) void { |
| 476 | s[0] &= 248; |
| 477 | s[31] &= 127; |
| 478 | s[31] |= 64; |
| 479 | } |
| 480 | |
| 481 | fn scalar_bit(s: []const u8, i: usize) i32 { |
| 482 | return (s[i >> 3] >> @intCast(u3, i & 7)) & 1; |
| 483 | } |
| 484 | |
| 485 | pub fn crypto_x25519(raw_shared_secret: []u8, your_secret_key: [32]u8, their_public_key: [32]u8) bool { |
| 486 | std.debug.assert(raw_shared_secret.len >= 32); |
| 487 | |
| 488 | var storage: [7]Fe = undefined; |
| 489 | |
| 490 | var x1 = &storage[0]; |
| 491 | var x2 = &storage[1]; |
| 492 | var z2 = &storage[2]; |
| 493 | var x3 = &storage[3]; |
| 494 | var z3 = &storage[4]; |
| 495 | var t0 = &storage[5]; |
| 496 | var t1 = &storage[6]; |
| 497 | |
| 498 | // computes the scalar product |
| 499 | fe_frombytes(x1, their_public_key); |
| 500 | |
| 501 | // restrict the possible scalar values |
| 502 | var e: [32]u8 = undefined; |
| 503 | for (e[0..]) |_, i| { |
| 504 | e[i] = your_secret_key[i]; |
| 505 | } |
| 506 | trim_scalar(e[0..]); |
| 507 | |
| 508 | // computes the actual scalar product (the result is in x2 and z2) |
| 509 | |
| 510 | // Montgomery ladder |
| 511 | // In projective coordinates, to avoid divisons: x = X / Z |
| 512 | // We don't care about the y coordinate, it's only 1 bit of information |
| 513 | fe_1(x2); |
| 514 | fe_0(z2); // "zero" point |
| 515 | fe_copy(x3, x1); |
| 516 | fe_1(z3); |
| 517 | |
| 518 | var swap: i32 = 0; |
| 519 | var pos: isize = 254; |
| 520 | while (pos >= 0) : (pos -= 1) { |
| 521 | // constant time conditional swap before ladder step |
| 522 | const b = scalar_bit(e, @intCast(usize, pos)); |
| 523 | swap ^= b; // xor trick avoids swapping at the end of the loop |
| 524 | fe_cswap(x2, x3, swap); |
| 525 | fe_cswap(z2, z3, swap); |
| 526 | swap = b; // anticipates one last swap after the loop |
| 527 | |
| 528 | // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3) |
| 529 | // with differential addition |
| 530 | fe_sub(t0, x3, z3); |
| 531 | fe_sub(t1, x2, z2); |
| 532 | fe_add(x2, x2, z2); |
| 533 | fe_add(z2, x3, z3); |
| 534 | fe_mul(z3, t0, x2); |
| 535 | fe_mul(z2, z2, t1); |
| 536 | fe_sq(t0, t1); |
| 537 | fe_sq(t1, x2); |
| 538 | fe_add(x3, z3, z2); |
| 539 | fe_sub(z2, z3, z2); |
| 540 | fe_mul(x2, t1, t0); |
| 541 | fe_sub(t1, t1, t0); |
| 542 | fe_sq(z2, z2); |
| 543 | fe_mul121666(z3, t1); |
| 544 | fe_sq(x3, x3); |
| 545 | fe_add(t0, t0, z3); |
| 546 | fe_mul(z3, x1, z2); |
| 547 | fe_mul(z2, t1, t0); |
| 548 | } |
| 549 | |
| 550 | // last swap is necessary to compensate for the xor trick |
| 551 | // Note: after this swap, P3 == P2 + P1. |
| 552 | fe_cswap(x2, x3, swap); |
| 553 | fe_cswap(z2, z3, swap); |
| 554 | |
| 555 | // normalises the coordinates: x == X / Z |
| 556 | fe_invert(z2, z2); |
| 557 | fe_mul(x2, x2, z2); |
| 558 | fe_tobytes(raw_shared_secret, x2); |
| 559 | |
| 560 | x1.secure_zero(); |
| 561 | x2.secure_zero(); |
| 562 | x3.secure_zero(); |
| 563 | t0.secure_zero(); |
| 564 | t1.secure_zero(); |
| 565 | z2.secure_zero(); |
| 566 | z3.secure_zero(); |
| 567 | std.mem.secureZero(u8, e[0..]); |
| 568 | |
| 569 | // Returns false if the output is all zero |
| 570 | // (happens with some malicious public keys) |
| 571 | return !zerocmp(u8, raw_shared_secret); |
| 572 | } |
| 573 | |
| 574 | pub fn crypto_x25519_public_key(public_key: []u8, secret_key: [32]u8) void { |
| 575 | var base_point = []u8{9} ++ []u8{0} ** 31; |
| 576 | crypto_x25519(public_key, secret_key, base_point); |
| 577 | } |
| 578 | |
| 579 | test "x25519 rfc7748 vector1" { |
| 580 | const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4"; |
| 581 | const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c"; |
| 582 | |
| 583 | const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52"; |
| 584 | |
| 585 | var output: [32]u8 = undefined; |
| 586 | |
| 587 | std.debug.assert(crypto_x25519(output[0..], secret_key, public_key)); |
| 588 | std.debug.assert(std.mem.eql(u8, output, expected_output)); |
| 589 | } |
| 590 | |
| 591 | test "x25519 rfc7748 vector2" { |
| 592 | const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d"; |
| 593 | const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93"; |
| 594 | |
| 595 | const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57"; |
| 596 | |
| 597 | var output: [32]u8 = undefined; |
| 598 | |
| 599 | std.debug.assert(crypto_x25519(output[0..], secret_key, public_key)); |
| 600 | std.debug.assert(std.mem.eql(u8, output, expected_output)); |
| 601 | } |
| 602 | |
| 603 | test "x25519 rfc7748 one iteration" { |
| 604 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; |
| 605 | const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79"; |
| 606 | |
| 607 | var k: [32]u8 = initial_value; |
| 608 | var u: [32]u8 = initial_value; |
| 609 | |
| 610 | var i: usize = 0; |
| 611 | while (i < 1) : (i += 1) { |
| 612 | var output: [32]u8 = undefined; |
| 613 | std.debug.assert(crypto_x25519(output[0..], k, u)); |
| 614 | |
| 615 | std.mem.copy(u8, u[0..], k[0..]); |
| 616 | std.mem.copy(u8, k[0..], output[0..]); |
| 617 | } |
| 618 | |
| 619 | std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); |
| 620 | } |
| 621 | |
| 622 | test "x25519 rfc7748 1,000 iterations" { |
| 623 | // These iteration tests are slow so we always skip them. Results have been verified. |
| 624 | if (true) { |
| 625 | return error.SkipZigTest; |
| 626 | } |
| 627 | |
| 628 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; |
| 629 | const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51"; |
| 630 | |
| 631 | var k: [32]u8 = initial_value; |
| 632 | var u: [32]u8 = initial_value; |
| 633 | |
| 634 | var i: usize = 0; |
| 635 | while (i < 1000) : (i += 1) { |
| 636 | var output: [32]u8 = undefined; |
| 637 | std.debug.assert(crypto_x25519(output[0..], k, u)); |
| 638 | |
| 639 | std.mem.copy(u8, u[0..], k[0..]); |
| 640 | std.mem.copy(u8, k[0..], output[0..]); |
| 641 | } |
| 642 | |
| 643 | std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); |
| 644 | } |
| 645 | |
| 646 | test "x25519 rfc7748 1,000,000 iterations" { |
| 647 | if (true) { |
| 648 | return error.SkipZigTest; |
| 649 | } |
| 650 | |
| 651 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; |
| 652 | const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24"; |
| 653 | |
| 654 | var k: [32]u8 = initial_value; |
| 655 | var u: [32]u8 = initial_value; |
| 656 | |
| 657 | var i: usize = 0; |
| 658 | while (i < 1000000) : (i += 1) { |
| 659 | var output: [32]u8 = undefined; |
| 660 | std.debug.assert(crypto_x25519(output[0..], k, u)); |
| 661 | |
| 662 | std.mem.copy(u8, u[0..], k[0..]); |
| 663 | std.mem.copy(u8, k[0..], output[0..]); |
| 664 | } |
| 665 | |
| 666 | std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); |
| 667 | } |