authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-08-14 14:06:18+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-16 22:35:27-07:00
log3f0d80f25eccd12759bad21fb8429e646eff070b
treecbb337e93ba6cb04c7ee26008a99f3c0b502e9e7
parentf46e375bbe0ac0893717bd477eab78f51863e277

Improve curve25519-based crypto

This is a rewrite of the x25519 code, that generalizes support for common primitives based on the same finite field. - Low-level operations can now be performed over the curve25519 and edwards25519 curves, as well as the ristretto255 group. - Ed25519 signatures have been implemented. - X25519 is now about twice as fast. - mem.timingSafeEqual() has been added for constant-time comparison. Domains have been clearly separated, making it easier to later add platform-specific implementations.

10 files changed, 1357 insertions(+), 677 deletions(-)

lib/std/crypto.zig+13-2
......@@ -34,12 +34,17 @@ pub const chaCha20IETF = import_chaCha20.chaCha20IETF;
3434pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
3535
3636pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
37pub const X25519 = @import("crypto/x25519.zig").X25519;
3837
3938const import_aes = @import("crypto/aes.zig");
4039pub const AES128 = import_aes.AES128;
4140pub const AES256 = import_aes.AES256;
4241
42pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
43pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519;
44pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519;
45pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
46pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255;
47
4348const std = @import("std.zig");
4449pub const randomBytes = std.os.getrandom;
4550
......@@ -55,7 +60,13 @@ test "crypto" {
5560 _ = @import("crypto/sha1.zig");
5661 _ = @import("crypto/sha2.zig");
5762 _ = @import("crypto/sha3.zig");
58 _ = @import("crypto/x25519.zig");
63 _ = @import("crypto/25519/curve25519.zig");
64 _ = @import("crypto/25519/ed25519.zig");
65 _ = @import("crypto/25519/edwards25519.zig");
66 _ = @import("crypto/25519/field25519.zig");
67 _ = @import("crypto/25519/scalar25519.zig");
68 _ = @import("crypto/25519/x25519.zig");
69 _ = @import("crypto/25519/ristretto255.zig");
5970}
6071
6172test "issue #4532: no index out of bounds" {
lib/std/crypto/25519/curve25519.zig created+155
......@@ -0,0 +1,155 @@
1const std = @import("std");
2
3/// Group operations over Curve25519.
4pub const Curve25519 = struct {
5 /// The underlying prime field.
6 pub const Fe = @import("field25519.zig").Fe;
7 /// Field arithmetic mod the order of the main subgroup.
8 pub const scalar = @import("scalar25519.zig");
9
10 x: Fe,
11
12 /// Decode a Curve25519 point from its compressed (X) coordinates.
13 pub inline fn fromBytes(s: [32]u8) Curve25519 {
14 return .{ .x = Fe.fromBytes(s) };
15 }
16
17 /// Encode a Curve25519 point.
18 pub inline fn toBytes(p: Curve25519) [32]u8 {
19 return p.x.toBytes();
20 }
21
22 /// Return the Curve25519 base point.
23 pub inline fn basePoint() Curve25519 {
24 return .{ .x = Fe.curve25519BasePoint() };
25 }
26
27 /// Check that the encoding of a Curve25519 point is canonical.
28 pub fn rejectNonCanonical(s: [32]u8) !void {
29 return Fe.rejectNonCanonical(s, false);
30 }
31
32 /// Reject the neutral element.
33 pub fn rejectIdentity(p: Curve25519) !void {
34 if (p.x.isZero()) {
35 return error.IdentityElement;
36 }
37 }
38
39 fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) !Curve25519 {
40 var x1 = p.x;
41 var x2 = Fe.one();
42 var z2 = Fe.zero();
43 var x3 = x1;
44 var z3 = Fe.one();
45 var swap: u8 = 0;
46 var pos: usize = bits - 1;
47 while (true) {
48 const b = (s[pos / 8] >> @intCast(u3, pos & 7)) & 1;
49 swap ^= b;
50 Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
51 swap = b;
52 var tmp0 = x3.sub(z3);
53 var tmp1 = x2.sub(z2);
54 x2 = x2.add(z2);
55 z2 = x3.add(z3);
56 z3 = tmp0.mul(x2);
57 z2 = z2.mul(tmp1);
58 tmp0 = tmp1.sq();
59 tmp1 = x2.sq();
60 x3 = z3.add(z2);
61 z2 = z3.sub(z2);
62 x2 = tmp1.mul(tmp0);
63 tmp1 = tmp1.sub(tmp0);
64 z2 = z2.sq();
65 z3 = tmp1.mul32(121666);
66 x3 = x3.sq();
67 tmp0 = tmp0.add(z3);
68 z3 = x1.mul(z2);
69 z2 = tmp1.mul(tmp0);
70 if (pos == 0) break;
71 pos -= 1;
72 }
73 Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
74 z2 = z2.invert();
75 x2 = x2.mul(z2);
76 if (x2.isZero()) {
77 return error.IdentityElement;
78 }
79 return @as(Curve25519, .{ .x = x2 });
80 }
81
82 /// Multiply a Curve25519 point by a scalar after "clamping" it.
83 /// Clamping forces the scalar to be a multiple of the cofactor in
84 /// order to prevent small subgroups attacks. This is the standard
85 /// way to use Curve25519 for a DH operation.
86 /// Return error.IdentityElement if the resulting point is
87 /// the identity element.
88 pub fn clampedMul(p: Curve25519, s: [32]u8) !Curve25519 {
89 var t: [32]u8 = s;
90 scalar.clamp(&t);
91 return ladder(p, t, 255);
92 }
93
94 /// Multiply a Curve25519 point by a scalar without clamping it.
95 /// Return error.IdentityElement if the resulting point is
96 /// the identity element or error.WeakPublicKey if the public
97 /// key is a low-order point.
98 pub fn mul(p: Curve25519, s: [32]u8) !Curve25519 {
99 const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
100 _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;
101 return ladder(p, s, 256);
102 }
103};
104
105test "curve25519" {
106 var s = [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, 8 };
107 const p = try Curve25519.basePoint().clampedMul(s);
108 try p.rejectIdentity();
109 var buf: [128]u8 = undefined;
110 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
111 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{p.toBytes()}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145");
112 const q = try p.clampedMul(s);
113 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{q.toBytes()}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537");
114
115 try Curve25519.rejectNonCanonical(s);
116 s[31] |= 0x80;
117 std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s));
118}
119
120test "curve25519 small order check" {
121 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;
122 const small_order_ss: [7][32]u8 = .{
123 .{
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4)
125 },
126 .{
127 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1)
128 },
129 .{
130 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00, // 325606250916557431795983626356110631294008115727848805560023387167927233504 (order 8) */
131 },
132 .{
133 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57, // 39382357235489614581723060781553021112529911719440698176882885853963445705823 (order 8)
134 },
135 .{
136 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2)
137 },
138 .{
139 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4)
140 },
141 .{
142 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1)
143 },
144 };
145 for (small_order_ss) |small_order_s| {
146 std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s));
147 var extra = small_order_s;
148 extra[31] ^= 0x80;
149 std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(extra).mul(s));
150 var valid = small_order_s;
151 valid[31] = 0x40;
152 s[0] = 0;
153 std.testing.expectError(error.IdentityElement, Curve25519.fromBytes(valid).mul(s));
154 }
155}
lib/std/crypto/25519/ed25519.zig created+129
......@@ -0,0 +1,129 @@
1const std = @import("std");
2const fmt = std.fmt;
3const mem = std.mem;
4const Sha512 = std.crypto.Sha512;
5
6/// Ed25519 (EdDSA) signatures.
7pub const Ed25519 = struct {
8 /// The underlying elliptic curve.
9 pub const Curve = @import("edwards25519.zig").Edwards25519;
10 /// Length (in bytes) of a seed required to create a key pair.
11 pub const seed_length = 32;
12 /// Length (in bytes) of a compressed key pair.
13 pub const keypair_length = 64;
14 /// Length (in bytes) of a compressed public key.
15 pub const public_length = 32;
16 /// Length (in bytes) of a signature.
17 pub const signature_length = 64;
18 /// Length (in bytes) of optional random bytes, for non-deterministic signatures.
19 pub const noise_length = 32;
20
21 /// Derive a key pair from a secret seed.
22 pub fn createKeyPair(seed: [seed_length]u8) ![keypair_length]u8 {
23 var az: [Sha512.digest_length]u8 = undefined;
24 var h = Sha512.init();
25 h.update(&seed);
26 h.final(&az);
27 const p = try Curve.basePoint().clampedMul(az[0..32].*);
28 var keypair: [keypair_length]u8 = undefined;
29 mem.copy(u8, &keypair, &seed);
30 mem.copy(u8, keypair[seed_length..], &p.toBytes());
31 return keypair;
32 }
33
34 /// Return the public key for a given key pair.
35 pub fn publicKey(key_pair: [keypair_length]u8) [public_length]u8 {
36 var public_key: [public_length]u8 = undefined;
37 mem.copy(u8, public_key[0..], key_pair[seed_length..]);
38 return public_key;
39 }
40
41 /// Sign a message using a key pair, and optional random noise.
42 /// Having noise creates non-standard, non-deterministic signatures,
43 /// but has been proven to increase resilience against fault attacks.
44 pub fn sign(msg: []const u8, key_pair: [keypair_length]u8, noise: ?[noise_length]u8) ![signature_length]u8 {
45 const public_key = key_pair[32..];
46 var az: [Sha512.digest_length]u8 = undefined;
47 var h = Sha512.init();
48 h.update(key_pair[0..seed_length]);
49 h.final(&az);
50
51 h = Sha512.init();
52 if (noise) |*z| {
53 h.update(z);
54 }
55 h.update(az[32..]);
56 h.update(msg);
57 var nonce64: [64]u8 = undefined;
58 h.final(&nonce64);
59 const nonce = Curve.scalar.reduce64(nonce64);
60 const r = try Curve.basePoint().mul(nonce);
61
62 var sig: [signature_length]u8 = undefined;
63 mem.copy(u8, sig[0..32], &r.toBytes());
64 mem.copy(u8, sig[32..], public_key);
65 h = Sha512.init();
66 h.update(&sig);
67 h.update(msg);
68 var hram64: [Sha512.digest_length]u8 = undefined;
69 h.final(&hram64);
70 const hram = Curve.scalar.reduce64(hram64);
71
72 var x = az[0..32];
73 Curve.scalar.clamp(x);
74 const s = Curve.scalar.mulAdd(hram, x.*, nonce);
75 mem.copy(u8, sig[32..], s[0..]);
76 return sig;
77 }
78
79 /// Verify an Ed25519 signature given a message and a public key.
80 /// Returns error.InvalidSignature is the signature verification failed.
81 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void {
82 const r = sig[0..32];
83 const s = sig[32..64];
84 try Curve.scalar.rejectNonCanonical(s.*);
85 try Curve.rejectNonCanonical(public_key);
86 const a = try Curve.fromBytes(public_key);
87 try a.rejectIdentity();
88
89 var h = Sha512.init();
90 h.update(r);
91 h.update(&public_key);
92 h.update(msg);
93 var hram64: [Sha512.digest_length]u8 = undefined;
94 h.final(&hram64);
95 const hram = Curve.scalar.reduce64(hram64);
96
97 const p = try a.neg().mul(hram);
98 const check = (try Curve.basePoint().mul(s.*)).add(p).toBytes();
99 if (mem.timingSafeEqual(u8, &check, r) == false) {
100 return error.InvalidSignature;
101 }
102 }
103};
104
105test "ed25519 key pair creation" {
106 var seed: [32]u8 = undefined;
107 try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
108 const key_pair = try Ed25519.createKeyPair(seed);
109 var buf: [256]u8 = undefined;
110 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
111 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{key_pair}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
112
113 const public_key = Ed25519.publicKey(key_pair);
114 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
115}
116
117test "ed25519 signature" {
118 var seed: [32]u8 = undefined;
119 try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
120 const key_pair = try Ed25519.createKeyPair(seed);
121
122 const sig = try Ed25519.sign("test", key_pair, null);
123 var buf: [128]u8 = undefined;
124 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
125 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
126 const public_key = Ed25519.publicKey(key_pair);
127 try Ed25519.verify(sig, "test", public_key);
128 std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key));
129}
lib/std/crypto/25519/edwards25519.zig created+224
......@@ -0,0 +1,224 @@
1const std = @import("std");
2const fmt = std.fmt;
3
4/// Group operations over Edwards25519.
5pub const Edwards25519 = struct {
6 /// The underlying prime field.
7 pub const Fe = @import("field25519.zig").Fe;
8 /// Field arithmetic mod the order of the main subgroup.
9 pub const scalar = @import("scalar25519.zig");
10
11 x: Fe,
12 y: Fe,
13 z: Fe,
14 t: Fe,
15
16 is_base: bool = false,
17
18 /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates.
19 pub fn fromBytes(s: [32]u8) !Edwards25519 {
20 const z = Fe.one();
21 const y = Fe.fromBytes(s);
22 var u = y.sq();
23 var v = u.mul(Fe.edwards25519d());
24 u = u.sub(z);
25 v = v.add(z);
26 const v3 = v.sq().mul(v);
27 var x = v3.sq().mul(v).mul(u).pow2523().mul(v3).mul(u);
28 const vxx = x.sq().mul(v);
29 const has_m_root = vxx.sub(u).isZero();
30 const has_p_root = vxx.add(u).isZero();
31 if ((@boolToInt(has_m_root) | @boolToInt(has_p_root)) == 0) {
32 return error.InvalidEncoding;
33 }
34 x.cMov(x.mul(Fe.sqrtm1()), 1 - @boolToInt(has_m_root));
35 x.cMov(x.neg(), @boolToInt(x.isNegative()) ^ (s[31] >> 7));
36 const t = x.mul(y);
37 return @as(Edwards25519, .{ .x = x, .y = y, .z = z, .t = t });
38 }
39
40 /// Encode an Edwards25519 point.
41 pub fn toBytes(p: Edwards25519) [32]u8 {
42 const zi = p.z.invert();
43 var s = p.y.mul(zi).toBytes();
44 s[31] ^= @as(u8, @boolToInt(p.x.mul(zi).isNegative())) << 7;
45 return s;
46 }
47
48 /// Check that the encoding of a point is canonical.
49 pub fn rejectNonCanonical(s: [32]u8) !void {
50 return Fe.rejectNonCanonical(s, true);
51 }
52
53 /// Return the Edwards25519 base point.
54 pub inline fn basePoint() Edwards25519 {
55 return .{
56 .x = Fe{ .limbs = .{ 3990542415680775, 3398198340507945, 4322667446711068, 2814063955482877, 2839572215813860 } },
57 .y = Fe{ .limbs = .{ 1801439850948184, 1351079888211148, 450359962737049, 900719925474099, 1801439850948198 } },
58 .z = Fe.one(),
59 .t = Fe{ .limbs = .{ 1841354044333475, 16398895984059, 755974180946558, 900171276175154, 1821297809914039 } },
60 .is_base = true,
61 };
62 }
63
64 inline fn identityElement() Edwards25519 {
65 return .{ .x = Fe.zero(), .y = Fe.one(), .z = Fe.one(), .t = Fe.zero() };
66 }
67
68 /// Reject the neutral element.
69 pub fn rejectIdentity(p: Edwards25519) !void {
70 if (p.x.isZero()) {
71 return error.IdentityElement;
72 }
73 }
74
75 /// Flip the sign of the X coordinate.
76 pub inline fn neg(p: Edwards25519) Edwards25519 {
77 return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() };
78 }
79
80 /// Double an Edwards25519 point.
81 pub inline fn dbl(p: Edwards25519) Edwards25519 {
82 const t0 = p.x.add(p.y).sq();
83 var x = p.x.sq();
84 var z = p.y.sq();
85 const y = z.add(x);
86 z = z.sub(x);
87 x = t0.sub(y);
88 const t = p.z.sq2().sub(z);
89 return .{
90 .x = x.mul(t),
91 .y = y.mul(z),
92 .z = z.mul(t),
93 .t = x.mul(y),
94 };
95 }
96
97 /// Add two Edwards25519 points.
98 pub inline fn add(p: Edwards25519, q: Edwards25519) Edwards25519 {
99 const a = p.y.sub(p.x).mul(q.y.sub(q.x));
100 const b = p.x.add(p.y).mul(q.x.add(q.y));
101 const c = p.t.mul(q.t).mul(Fe.edwards25519d2());
102 var d = p.z.mul(q.z);
103 d = d.add(d);
104 const x = b.sub(a);
105 const y = b.add(a);
106 const z = d.add(c);
107 const t = d.sub(c);
108 return .{
109 .x = x.mul(t),
110 .y = y.mul(z),
111 .z = z.mul(t),
112 .t = x.mul(y),
113 };
114 }
115
116 inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void {
117 p.x.cMov(a.x, c);
118 p.y.cMov(a.y, c);
119 p.z.cMov(a.z, c);
120 p.t.cMov(a.t, c);
121 }
122
123 inline fn pcSelect(pc: [16]Edwards25519, b: u8) Edwards25519 {
124 var t = Edwards25519.identityElement();
125 comptime var i: u8 = 0;
126 inline while (i < 16) : (i += 1) {
127 t.cMov(pc[i], ((@intCast(usize, (b ^ i)) -% 1) >> 8) & 1);
128 }
129 return t;
130 }
131
132 fn pcMul(pc: [16]Edwards25519, s: [32]u8) !Edwards25519 {
133 var q = Edwards25519.identityElement();
134 var pos: usize = 252;
135 while (true) {
136 q = q.dbl().dbl().dbl().dbl();
137 const b = (s[pos / 8] >> @intCast(u3, pos & 7)) & 0xf;
138 q = q.add(pcSelect(pc, b));
139 if (pos == 0) break;
140 pos -= 4;
141 }
142 try q.rejectIdentity();
143 return q;
144 }
145
146 fn precompute(p: Edwards25519) [16]Edwards25519 {
147 var pc: [16]Edwards25519 = undefined;
148 pc[0] = Edwards25519.identityElement();
149 pc[1] = p;
150 var i: usize = 2;
151 while (i < 16) : (i += 1) {
152 pc[i] = pc[i - 1].add(p);
153 }
154 return pc;
155 }
156
157 fn _mul(p: Edwards25519, s: [32]u8) !Edwards25519 {
158 var pc: [16]Edwards25519 = undefined;
159 if (p.is_base) {
160 @setEvalBranchQuota(10000);
161 pc = comptime precompute(Edwards25519.basePoint());
162 } else {
163 pc = precompute(p);
164 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
165 }
166 return pcMul(pc, s);
167 }
168
169 /// Multiply an Edwards25519 point by a scalar after "clamping" it.
170 /// Clamping forces the scalar to be a multiple of the cofactor in
171 /// order to prevent small subgroups attacks.
172 /// This is strongly recommended for DH operations.
173 /// Return error.WeakPublicKey if the resulting point is
174 /// the identity element.
175 pub fn clampedMul(p: Edwards25519, s: [32]u8) !Edwards25519 {
176 var t: [32]u8 = s;
177 scalar.clamp(&t);
178 return _mul(p, t);
179 }
180
181 /// Multiply an Edwards25519 point by a scalar without clamping it.
182 /// Return error.WeakPublicKey if the resulting point is
183 /// the identity element.
184 pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 {
185 return _mul(p, s);
186 }
187};
188
189test "edwards25519 packing/unpacking" {
190 const s = [_]u8{170} ++ [_]u8{0} ** 31;
191 var b = Edwards25519.basePoint();
192 const pk = try b.mul(s);
193 var buf: [128]u8 = undefined;
194 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
195 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{pk.toBytes()}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6");
196
197 const small_order_ss: [7][32]u8 = .{
198 .{
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 (order 4)
200 },
201 .{
202 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 (order 1)
203 },
204 .{
205 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05, // 270738550114484064931822528722565878893680426757531351946374360975030340202(order 8)
206 },
207 .{
208 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a, // 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8)
209 },
210 .{
211 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p-1 (order 2)
212 },
213 .{
214 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p (=0, order 4)
215 },
216 .{
217 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // p+1 (=1, order 1)
218 },
219 };
220 for (small_order_ss) |small_order_s| {
221 const small_p = try Edwards25519.fromBytes(small_order_s);
222 std.testing.expectError(error.WeakPublicKey, small_p.mul(s));
223 }
224}
lib/std/crypto/25519/field25519.zig created+327
......@@ -0,0 +1,327 @@
1const std = @import("std");
2const readIntLittle = std.mem.readIntLittle;
3const writeIntLittle = std.mem.writeIntLittle;
4
5pub const Fe = struct {
6 limbs: [5]u64 = undefined,
7
8 const MASK51: u64 = 0x7ffffffffffff;
9
10 pub inline fn zero() Fe {
11 return .{ .limbs = .{ 0, 0, 0, 0, 0 } };
12 }
13
14 pub inline fn one() Fe {
15 return .{ .limbs = .{ 1, 0, 0, 0, 0 } };
16 }
17
18 pub inline fn sqrtm1() Fe {
19 return .{ .limbs = .{ 1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133 } }; // sqrt(-1)
20 }
21
22 pub inline fn curve25519BasePoint() Fe {
23 return .{ .limbs = .{ 9, 0, 0, 0, 0 } };
24 }
25
26 pub inline fn edwards25519d() Fe {
27 return .{ .limbs = .{ 929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575 } }; // 37095705934669439343138083508754565189542113879843219016388785533085940283555
28 }
29
30 pub inline fn edwards25519d2() Fe {
31 return .{ .limbs = .{ 1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903 } }; // 2d
32 }
33
34 // 1/sqrt(a-d)
35 pub inline fn edwards25519sqrtamd() Fe {
36 return .{ .limbs = .{ 278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447 } };
37 }
38
39 pub inline fn isZero(fe: Fe) bool {
40 var reduced = fe;
41 reduced.reduce();
42 const limbs = reduced.limbs;
43 return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0;
44 }
45
46 pub inline fn equivalent(a: Fe, b: Fe) bool {
47 return a.sub(b).isZero();
48 }
49
50 pub fn fromBytes(s: [32]u8) Fe {
51 var fe: Fe = undefined;
52 fe.limbs[0] = readIntLittle(u64, s[0..8]) & MASK51;
53 fe.limbs[1] = (readIntLittle(u64, s[6..14]) >> 3) & MASK51;
54 fe.limbs[2] = (readIntLittle(u64, s[12..20]) >> 6) & MASK51;
55 fe.limbs[3] = (readIntLittle(u64, s[19..27]) >> 1) & MASK51;
56 fe.limbs[4] = (readIntLittle(u64, s[24..32]) >> 12) & MASK51;
57
58 return fe;
59 }
60
61 pub fn toBytes(fe: Fe) [32]u8 {
62 var reduced = fe;
63 reduced.reduce();
64 var s: [32]u8 = undefined;
65 writeIntLittle(u64, s[0..8], reduced.limbs[0] | (reduced.limbs[1] << 51));
66 writeIntLittle(u64, s[8..16], (reduced.limbs[1] >> 13) | (reduced.limbs[2] << 38));
67 writeIntLittle(u64, s[16..24], (reduced.limbs[2] >> 26) | (reduced.limbs[3] << 25));
68 writeIntLittle(u64, s[24..32], (reduced.limbs[3] >> 39) | (reduced.limbs[4] << 12));
69
70 return s;
71 }
72
73 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void {
74 var c: u16 = (s[31] & 0x7f) ^ 0x7f;
75 comptime var i = 30;
76 inline while (i > 0) : (i -= 1) {
77 c |= s[i] ^ 0xff;
78 }
79 c = (c -% 1) >> 8;
80 const d = (@intCast(u16, 0xed - 1) -% @intCast(u16, s[0])) >> 8;
81 const x = if (ignore_extra_bit) 0 else s[31] >> 7;
82 if ((((c & d) | x) & 1) != 0) {
83 return error.NonCanonical;
84 }
85 }
86
87 fn reduce(fe: *Fe) void {
88 comptime var i = 0;
89 comptime var j = 0;
90 const limbs = &fe.limbs;
91 inline while (j < 2) : (j += 1) {
92 i = 0;
93 inline while (i < 4) : (i += 1) {
94 limbs[i + 1] += limbs[i] >> 51;
95 limbs[i] &= MASK51;
96 }
97 limbs[0] += 19 * (limbs[4] >> 51);
98 limbs[4] &= MASK51;
99 }
100 limbs[0] += 19;
101 i = 0;
102 inline while (i < 4) : (i += 1) {
103 limbs[i + 1] += limbs[i] >> 51;
104 limbs[i] &= MASK51;
105 }
106 limbs[0] += 19 * (limbs[4] >> 51);
107 limbs[4] &= MASK51;
108
109 limbs[0] += 0x8000000000000 - 19;
110 limbs[1] += 0x8000000000000 - 1;
111 limbs[2] += 0x8000000000000 - 1;
112 limbs[3] += 0x8000000000000 - 1;
113 limbs[4] += 0x8000000000000 - 1;
114
115 i = 0;
116 inline while (i < 4) : (i += 1) {
117 limbs[i + 1] += limbs[i] >> 51;
118 limbs[i] &= MASK51;
119 }
120 limbs[4] &= MASK51;
121 }
122
123 pub inline fn add(a: Fe, b: Fe) Fe {
124 var fe: Fe = undefined;
125 comptime var i = 0;
126 inline while (i < 5) : (i += 1) {
127 fe.limbs[i] = a.limbs[i] + b.limbs[i];
128 }
129 return fe;
130 }
131
132 pub fn sub(a: Fe, b: Fe) Fe {
133 var fe = b;
134 comptime var i = 0;
135 inline while (i < 4) : (i += 1) {
136 fe.limbs[i + 1] += fe.limbs[i] >> 51;
137 fe.limbs[i] &= MASK51;
138 }
139 fe.limbs[0] += 19 * (fe.limbs[4] >> 51);
140 fe.limbs[4] &= MASK51;
141 fe.limbs[0] = (a.limbs[0] + 0xfffffffffffda) - fe.limbs[0];
142 fe.limbs[1] = (a.limbs[1] + 0xffffffffffffe) - fe.limbs[1];
143 fe.limbs[2] = (a.limbs[2] + 0xffffffffffffe) - fe.limbs[2];
144 fe.limbs[3] = (a.limbs[3] + 0xffffffffffffe) - fe.limbs[3];
145 fe.limbs[4] = (a.limbs[4] + 0xffffffffffffe) - fe.limbs[4];
146
147 return fe;
148 }
149
150 pub inline fn neg(a: Fe) Fe {
151 return zero().sub(a);
152 }
153
154 pub inline fn isNegative(a: Fe) bool {
155 return (a.toBytes()[0] & 1) != 0;
156 }
157
158 pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void {
159 const mask: u64 = 0 -% c;
160 var x = fe.*;
161 comptime var i = 0;
162 inline while (i < 5) : (i += 1) {
163 x.limbs[i] ^= a.limbs[i];
164 }
165 i = 0;
166 inline while (i < 5) : (i += 1) {
167 x.limbs[i] &= mask;
168 }
169 i = 0;
170 inline while (i < 5) : (i += 1) {
171 fe.limbs[i] ^= x.limbs[i];
172 }
173 }
174
175 pub fn cSwap2(a0: *Fe, b0: *Fe, a1: *Fe, b1: *Fe, c: u64) void {
176 const mask: u64 = 0 -% c;
177 var x0 = a0.*;
178 var x1 = a1.*;
179 comptime var i = 0;
180 inline while (i < 5) : (i += 1) {
181 x0.limbs[i] ^= b0.limbs[i];
182 x1.limbs[i] ^= b1.limbs[i];
183 }
184 i = 0;
185 inline while (i < 5) : (i += 1) {
186 x0.limbs[i] &= mask;
187 x1.limbs[i] &= mask;
188 }
189 i = 0;
190 inline while (i < 5) : (i += 1) {
191 a0.limbs[i] ^= x0.limbs[i];
192 b0.limbs[i] ^= x0.limbs[i];
193 a1.limbs[i] ^= x1.limbs[i];
194 b1.limbs[i] ^= x1.limbs[i];
195 }
196 }
197
198 inline fn _carry128(r: *[5]u128) Fe {
199 var rs: [5]u64 = undefined;
200 comptime var i = 0;
201 inline while (i < 4) : (i += 1) {
202 rs[i] = @truncate(u64, r[i]) & MASK51;
203 r[i + 1] += @intCast(u64, r[i] >> 51);
204 }
205 rs[4] = @truncate(u64, r[4]) & MASK51;
206 var carry = @intCast(u64, r[4] >> 51);
207 rs[0] += 19 * carry;
208 carry = rs[0] >> 51;
209 rs[0] &= MASK51;
210 rs[1] += carry;
211 carry = rs[1] >> 51;
212 rs[1] &= MASK51;
213 rs[2] += carry;
214
215 return .{ .limbs = rs };
216 }
217
218 pub fn mul(a: Fe, b: Fe) Fe {
219 var ax: [5]u128 = undefined;
220 var bx: [5]u128 = undefined;
221 var a19: [5]u128 = undefined;
222 var r: [5]u128 = undefined;
223 comptime var i = 0;
224 inline while (i < 5) : (i += 1) {
225 ax[i] = @intCast(u128, a.limbs[i]);
226 bx[i] = @intCast(u128, b.limbs[i]);
227 }
228 i = 1;
229 inline while (i < 5) : (i += 1) {
230 a19[i] = 19 * ax[i];
231 }
232 r[0] = ax[0] * bx[0] + a19[1] * bx[4] + a19[2] * bx[3] + a19[3] * bx[2] + a19[4] * bx[1];
233 r[1] = ax[0] * bx[1] + ax[1] * bx[0] + a19[2] * bx[4] + a19[3] * bx[3] + a19[4] * bx[2];
234 r[2] = ax[0] * bx[2] + ax[1] * bx[1] + ax[2] * bx[0] + a19[3] * bx[4] + a19[4] * bx[3];
235 r[3] = ax[0] * bx[3] + ax[1] * bx[2] + ax[2] * bx[1] + ax[3] * bx[0] + a19[4] * bx[4];
236 r[4] = ax[0] * bx[4] + ax[1] * bx[3] + ax[2] * bx[2] + ax[3] * bx[1] + ax[4] * bx[0];
237
238 return _carry128(&r);
239 }
240
241 fn _sq(a: Fe, double: comptime bool) Fe {
242 var ax: [5]u128 = undefined;
243 var r: [5]u128 = undefined;
244 comptime var i = 0;
245 inline while (i < 5) : (i += 1) {
246 ax[i] = @intCast(u128, a.limbs[i]);
247 }
248 const a0_2 = 2 * ax[0];
249 const a1_2 = 2 * ax[1];
250 const a1_38 = 38 * ax[1];
251 const a2_38 = 38 * ax[2];
252 const a3_38 = 38 * ax[3];
253 const a3_19 = 19 * ax[3];
254 const a4_19 = 19 * ax[4];
255 r[0] = ax[0] * ax[0] + a1_38 * ax[4] + a2_38 * ax[3];
256 r[1] = a0_2 * ax[1] + a2_38 * ax[4] + a3_19 * ax[3];
257 r[2] = a0_2 * ax[2] + ax[1] * ax[1] + a3_38 * ax[4];
258 r[3] = a0_2 * ax[3] + a1_2 * ax[2] + a4_19 * ax[4];
259 r[4] = a0_2 * ax[4] + a1_2 * ax[3] + ax[2] * ax[2];
260 if (double) {
261 i = 0;
262 inline while (i < 5) : (i += 1) {
263 r[i] *= 2;
264 }
265 }
266 return _carry128(&r);
267 }
268
269 pub inline fn sq(a: Fe) Fe {
270 return _sq(a, false);
271 }
272
273 pub inline fn sq2(a: Fe) Fe {
274 return _sq(a, true);
275 }
276
277 pub inline fn mul32(a: Fe, comptime n: u32) Fe {
278 const sn = @intCast(u128, n);
279 var fe: Fe = undefined;
280 var x: u128 = 0;
281 comptime var i = 0;
282 inline while (i < 5) : (i += 1) {
283 x = a.limbs[i] * sn + (x >> 51);
284 fe.limbs[i] = @truncate(u64, x) & MASK51;
285 }
286 fe.limbs[0] += @intCast(u64, x >> 51) * 19;
287
288 return fe;
289 }
290
291 inline fn sqn(a: Fe, comptime n: comptime_int) Fe {
292 var i: usize = 0;
293 var fe = a;
294 while (i < n) : (i += 1) {
295 fe = fe.sq();
296 }
297 return fe;
298 }
299
300 pub fn invert(a: Fe) Fe {
301 var t0 = a.sq();
302 var t1 = t0.sqn(2).mul(a);
303 t0 = t0.mul(t1);
304 t1 = t1.mul(t0.sq());
305 t1 = t1.mul(t1.sqn(5));
306 var t2 = t1.sqn(10).mul(t1);
307 t2 = t2.mul(t2.sqn(20)).sqn(10);
308 t1 = t1.mul(t2);
309 t2 = t1.sqn(50).mul(t1);
310 return t1.mul(t2.mul(t2.sqn(100)).sqn(50)).sqn(5).mul(t0);
311 }
312
313 pub fn pow2523(a: Fe) Fe {
314 var c = a;
315 var i: usize = 0;
316 while (i < 249) : (i += 1) {
317 c = c.sq().mul(a);
318 }
319 return c.sq().sq().mul(a);
320 }
321
322 pub fn abs(a: Fe) Fe {
323 var r = a;
324 r.cMov(a.neg(), @boolToInt(a.isNegative()));
325 return r;
326 }
327};
lib/std/crypto/25519/ristretto255.zig created+153
......@@ -0,0 +1,153 @@
1const std = @import("std");
2const fmt = std.fmt;
3
4/// Group operations over Edwards25519.
5pub const Ristretto255 = struct {
6 /// The underlying elliptic curve.
7 pub const Curve = @import("edwards25519.zig").Edwards25519;
8 /// The underlying prime field.
9 pub const Fe = Curve.Fe;
10 /// Field arithmetic mod the order of the main subgroup.
11 pub const scalar = Curve.scalar;
12
13 p: Curve = undefined,
14
15 fn sqrtRatioM1(u: Fe, v: Fe) !Fe {
16 const v3 = v.sq().mul(v); // v3 = v^3
17 var x = v3.sq().mul(u).mul(v). // x = uv^7
18 pow2523().mul(v3).mul(u); // x = uv^3(uv^7)^((q-5)/8)
19 const vxx = x.sq().mul(v); // vx^2
20 const m_root_check = vxx.sub(u); // vx^2-u
21 const p_root_check = vxx.add(u); // vx^2+u
22 const f_root_check = u.mul(Fe.sqrtm1()).add(vxx); // vx^2+u*sqrt(-1)
23 const has_m_root = m_root_check.isZero();
24 const has_p_root = p_root_check.isZero();
25 const has_f_root = f_root_check.isZero();
26 const x_sqrtm1 = x.mul(Fe.sqrtm1()); // x*sqrt(-1)
27 x.cMov(x_sqrtm1, @boolToInt(has_p_root) | @boolToInt(has_f_root));
28 x = x.abs();
29 if ((@boolToInt(has_m_root) | @boolToInt(has_p_root)) == 0) {
30 return error.NoRoot;
31 }
32 return x;
33 }
34
35 fn rejectNonCanonical(s: [32]u8) !void {
36 if ((s[0] & 1) != 0) {
37 return error.NonCanonical;
38 }
39 try Fe.rejectNonCanonical(s, false);
40 }
41
42 /// Reject the neutral element.
43 pub inline fn rejectIdentity(p: Ristretto255) !void {
44 return p.p.rejectIdentity();
45 }
46
47 /// Return the base point (Ristretto is a curve in desguise).
48 pub inline fn basePoint() Ristretto255 {
49 return .{ .p = Curve.basePoint() };
50 }
51
52 /// Decode a Ristretto255 representative.
53 pub fn fromBytes(s: [32]u8) !Ristretto255 {
54 try rejectNonCanonical(s);
55 const s_ = Fe.fromBytes(s);
56 const ss = s_.sq(); // s^2
57 const u1_ = Fe.one().sub(ss); // (1-s^2)
58 const u1u1 = u1_.sq(); // (1-s^2)^2
59 const u2_ = Fe.one().add(ss); // (1+s^2)
60 const u2u2 = u2_.sq(); // (1+s^2)^2
61 const v = Fe.edwards25519d().mul(u1u1).neg().sub(u2u2); // -(d*u1^2)-u2^2
62 const v_u2u2 = v.mul(u2u2); // v*u2^2
63 const inv_sqrt = sqrtRatioM1(Fe.one(), v_u2u2) catch |e| {
64 return error.InvalidEncoding;
65 };
66 var x = inv_sqrt.mul(u2_);
67 const y = inv_sqrt.mul(x).mul(v).mul(u1_);
68 x = x.mul(s_);
69 x = x.add(x).abs();
70 const t = x.mul(y);
71 if ((@boolToInt(t.isNegative()) | @boolToInt(y.isZero())) != 0) {
72 return error.InvalidEncoding;
73 }
74 const p: Curve = .{
75 .x = x,
76 .y = y,
77 .z = Fe.one(),
78 .t = t,
79 };
80 return @as(Ristretto255, .{ .p = p });
81 }
82
83 /// Encode to a Ristretto255 representative.
84 pub fn toBytes(e: Ristretto255) [32]u8 {
85 const p = &e.p;
86 var u1_ = p.z.add(p.y); // Z+Y
87 const zmy = p.z.sub(p.y); // Z-Y
88 u1_ = u1_.mul(zmy); // (Z+Y)*(Z-Y)
89 const u2_ = p.x.mul(p.y); // X*Y
90
91 const u1_u2u2 = u2_.sq().mul(u1_); // u1*u2^2
92
93 const inv_sqrt = sqrtRatioM1(Fe.one(), u1_u2u2) catch unreachable;
94 const den1 = inv_sqrt.mul(u1_);
95 const den2 = inv_sqrt.mul(u2_);
96 const z_inv = den1.mul(den2).mul(p.t); // den1*den2*T
97
98 const ix = p.x.mul(Fe.sqrtm1()); // X*sqrt(-1)
99 const iy = p.y.mul(Fe.sqrtm1()); // Y*sqrt(-1)
100 const eden = den1.mul(Fe.edwards25519sqrtamd()); // den1/sqrt(a-d)
101
102 const t_z_inv = p.t.mul(z_inv); // T*z_inv
103 const rotate = @boolToInt(t_z_inv.isNegative());
104
105 var x = p.x;
106 var y = p.y;
107 var den_inv = den2;
108
109 x.cMov(iy, rotate);
110 y.cMov(ix, rotate);
111 den_inv.cMov(eden, rotate);
112
113 const x_z_inv = x.mul(z_inv);
114 const yneg = y.neg();
115 y.cMov(yneg, @boolToInt(x_z_inv.isNegative()));
116
117 return p.z.sub(y).mul(den_inv).abs().toBytes();
118 }
119
120 /// Double a Ristretto255 element.
121 pub inline fn dbl(p: Ristretto255) Ristretto255 {
122 return .{ .p = p.p.dbl() };
123 }
124
125 /// Add two Ristretto255 elements.
126 pub inline fn add(p: Ristretto255, q: Ristretto255) Ristretto255 {
127 return .{ .p = p.p.add(q.p) };
128 }
129
130 /// Multiply a Ristretto255 element with a scalar.
131 /// Return error.WeakPublicKey if the resulting element is
132 /// the identity element.
133 pub inline fn mul(p: Ristretto255, s: [32]u8) !Ristretto255 {
134 return @as(Ristretto255, .{ .p = try p.p.mul(s) });
135 }
136};
137
138test "ristretto255" {
139 const p = Ristretto255.basePoint();
140 var buf: [256]u8 = undefined;
141 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
142 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
143
144 var r: [32]u8 = undefined;
145 try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
146 var q = try Ristretto255.fromBytes(r);
147 q = q.dbl().add(p);
148 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
149
150 const s = [_]u8{15} ++ [_]u8{0} ** 31;
151 const w = try p.mul(s);
152 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E");
153}
lib/std/crypto/25519/scalar25519.zig created+185
......@@ -0,0 +1,185 @@
1const std = @import("std");
2const mem = std.mem;
3
4inline fn fieldSize() [32]u8 {
5 return .{
6 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
7 };
8}
9
10const ScalarExpanded = struct {
11 const L = fieldSize();
12 limbs: [64]i64 = [_]i64{0} ** 64,
13
14 fn fromBytes(s: [32]u8) ScalarExpanded {
15 var limbs: [64]i64 = undefined;
16 for (s) |x, idx| {
17 limbs[idx] = @intCast(i64, x);
18 }
19 mem.set(i64, limbs[32..], 0);
20 return .{ .limbs = limbs };
21 }
22
23 fn fromBytes64(s: [64]u8) ScalarExpanded {
24 var limbs: [64]i64 = undefined;
25 for (s) |x, idx| {
26 limbs[idx] = @intCast(i64, x);
27 }
28 return .{ .limbs = limbs };
29 }
30
31 fn reduce(e: *ScalarExpanded) void {
32 const limbs = &e.limbs;
33 var carry: i64 = undefined;
34 var i: usize = 63;
35 while (i >= 32) : (i -= 1) {
36 carry = 0;
37 const k = i - 12;
38 const xi = limbs[i];
39 var j = i - 32;
40 while (j < k) : (j += 1) {
41 const xj = limbs[j] + carry - 16 * xi * @intCast(i64, L[j - (i - 32)]);
42 carry = (xj + 128) >> 8;
43 limbs[j] = xj - carry * 256;
44 }
45 limbs[k] += carry;
46 limbs[i] = 0;
47 }
48 carry = 0;
49 comptime var j: usize = 0;
50 inline while (j < 32) : (j += 1) {
51 const xi = limbs[j] + carry - (limbs[31] >> 4) * @intCast(i64, L[j]);
52 carry = xi >> 8;
53 limbs[j] = xi & 255;
54 }
55 j = 0;
56 inline while (j < 32) : (j += 1) {
57 limbs[j] -= carry * @intCast(i64, L[j]);
58 }
59 j = 0;
60 inline while (j < 32) : (j += 1) {
61 limbs[j + 1] += limbs[j] >> 8;
62 }
63 }
64
65 fn toBytes(e: *ScalarExpanded) [32]u8 {
66 e.reduce();
67 var r: [32]u8 = undefined;
68 var i: usize = 0;
69 while (i < 32) : (i += 1) {
70 r[i] = @intCast(u8, e.limbs[i]);
71 }
72 return r;
73 }
74
75 fn add(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
76 var r = ScalarExpanded{};
77 comptime var i = 0;
78 inline while (i < 64) : (i += 1) {
79 r.limbs[i] = a.limbs[i] + b.limbs[i];
80 }
81 return r;
82 }
83
84 fn mul(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
85 var r = ScalarExpanded{};
86 var i: usize = 0;
87 while (i < 32) : (i += 1) {
88 const ai = a.limbs[i];
89 comptime var j = 0;
90 inline while (j < 32) : (j += 1) {
91 r.limbs[i + j] += ai * b.limbs[j];
92 }
93 }
94 r.reduce();
95 return r;
96 }
97
98 fn sq(a: ScalarExpanded) ScalarExpanded {
99 return a.mul(a);
100 }
101
102 fn mulAdd(a: ScalarExpanded, b: ScalarExpanded, c: ScalarExpanded) ScalarExpanded {
103 var r: ScalarExpanded = .{ .limbs = c.limbs };
104 var i: usize = 0;
105 while (i < 32) : (i += 1) {
106 const ai = a.limbs[i];
107 comptime var j = 0;
108 inline while (j < 32) : (j += 1) {
109 r.limbs[i + j] += ai * b.limbs[j];
110 }
111 }
112 r.reduce();
113 return r;
114 }
115};
116
117/// Reject a scalar whose encoding is not canonical.
118pub fn rejectNonCanonical(s: [32]u8) !void {
119 const L = fieldSize();
120 var c: u8 = 0;
121 var n: u8 = 1;
122 var i: usize = 31;
123 while (true) {
124 const xs = @intCast(u16, s[i]);
125 const xL = @intCast(u16, L[i]);
126 c |= @intCast(u8, ((xs -% xL) >> 8) & n);
127 n &= @intCast(u8, ((xs ^ xL) -% 1) >> 8);
128 if (i == 0) break;
129 i -= 1;
130 }
131 if (c == 0) {
132 return error.NonCanonical;
133 }
134}
135
136/// Reduce a scalar to the field size.
137pub fn reduce(s: [32]u8) [32]u8 {
138 return ScalarExpanded.fromBytes(s).toBytes();
139}
140
141/// Reduce a 64-bytes scalar to the field size.
142pub fn reduce64(s: [64]u8) [32]u8 {
143 return ScalarExpanded.fromBytes64(s).toBytes();
144}
145
146/// Perform the X25519 "clamping" operation.
147/// The scalar is then guaranteed to be a multiple of the cofactor.
148pub inline fn clamp(s: *[32]u8) void {
149 s[0] &= 248;
150 s[31] = (s[31] & 127) | 64;
151}
152
153/// Return a*b+c (mod L)
154pub 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();
156}
157
158test "scalar25519" {
159 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);
161 var y = x.toBytes();
162 try rejectNonCanonical(y);
163 var buf: [128]u8 = undefined;
164 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
165 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{y}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F");
166
167 const field_size = fieldSize();
168 const reduced = reduce(field_size);
169 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{reduced}), "0000000000000000000000000000000000000000000000000000000000000000");
170}
171
172test "non-canonical scalar25519" {
173 const too_targe: [32]u8 = .{ 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 };
174 std.testing.expectError(error.NonCanonical, rejectNonCanonical(too_targe));
175}
176
177test "mulAdd overflow check" {
178 const a: [32]u8 = [_]u8{0xff} ** 32;
179 const b: [32]u8 = [_]u8{0xff} ** 32;
180 const c: [32]u8 = [_]u8{0xff} ** 32;
181 const x = mulAdd(a, b, c);
182 var buf: [128]u8 = undefined;
183 const alloc = &std.heap.FixedBufferAllocator.init(&buf).allocator;
184 std.testing.expectEqualStrings(try std.fmt.allocPrint(alloc, "{X}", .{x}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903");
185}
lib/std/crypto/25519/x25519.zig created+146
......@@ -0,0 +1,146 @@
1const std = @import("std");
2const mem = std.mem;
3const fmt = std.fmt;
4
5/// X25519 DH function.
6pub const X25519 = struct {
7 /// The underlying elliptic curve.
8 pub const Curve = @import("curve25519.zig").Curve25519;
9 /// Length (in bytes) of a secret key.
10 pub const secret_length = 32;
11 /// Length (in bytes) of the output of the DH function.
12 pub const minimum_key_length = 32;
13
14 /// Compute the public key for a given private key.
15 pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool {
16 std.debug.assert(private_key.len >= minimum_key_length);
17 std.debug.assert(public_key.len >= minimum_key_length);
18 var s: [32]u8 = undefined;
19 mem.copy(u8, &s, private_key[0..32]);
20 if (Curve.basePoint().clampedMul(s)) |q| {
21 mem.copy(u8, public_key, q.toBytes()[0..]);
22 return true;
23 } else |_| {
24 return false;
25 }
26 }
27
28 /// Compute the scalar product of a public key and a secret scalar.
29 /// Note that the output should not be used as a shared secret without
30 /// hashing it first.
31 pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
32 std.debug.assert(out.len >= secret_length);
33 std.debug.assert(private_key.len >= minimum_key_length);
34 std.debug.assert(public_key.len >= minimum_key_length);
35 var s: [32]u8 = undefined;
36 var b: [32]u8 = undefined;
37 mem.copy(u8, &s, private_key[0..32]);
38 mem.copy(u8, &b, public_key[0..32]);
39 if (Curve.fromBytes(b).clampedMul(s)) |q| {
40 mem.copy(u8, out, q.toBytes()[0..]);
41 return true;
42 } else |_| {
43 return false;
44 }
45 }
46};
47
48test "x25519 public key calculation from secret key" {
49 var sk: [32]u8 = undefined;
50 var pk_expected: [32]u8 = undefined;
51 var pk_calculated: [32]u8 = undefined;
52 try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
53 try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
54 std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk));
55 std.testing.expect(std.mem.eql(u8, &pk_calculated, &pk_expected));
56}
57
58test "x25519 rfc7748 vector1" {
59 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";
60 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";
61
62 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";
63
64 var output: [32]u8 = undefined;
65
66 std.testing.expect(X25519.create(output[0..], secret_key, public_key));
67 std.testing.expect(std.mem.eql(u8, &output, expected_output));
68}
69
70test "x25519 rfc7748 vector2" {
71 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";
72 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";
73
74 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";
75
76 var output: [32]u8 = undefined;
77
78 std.testing.expect(X25519.create(output[0..], secret_key, public_key));
79 std.testing.expect(std.mem.eql(u8, &output, expected_output));
80}
81
82test "x25519 rfc7748 one iteration" {
83 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".*;
84 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";
85
86 var k: [32]u8 = initial_value;
87 var u: [32]u8 = initial_value;
88
89 var i: usize = 0;
90 while (i < 1) : (i += 1) {
91 var output: [32]u8 = undefined;
92 std.testing.expect(X25519.create(output[0..], &k, &u));
93
94 std.mem.copy(u8, u[0..], k[0..]);
95 std.mem.copy(u8, k[0..], output[0..]);
96 }
97
98 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
99}
100
101test "x25519 rfc7748 1,000 iterations" {
102 // These iteration tests are slow so we always skip them. Results have been verified.
103 if (true) {
104 return error.SkipZigTest;
105 }
106
107 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";
108 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";
109
110 var k: [32]u8 = initial_value.*;
111 var u: [32]u8 = initial_value.*;
112
113 var i: usize = 0;
114 while (i < 1000) : (i += 1) {
115 var output: [32]u8 = undefined;
116 std.testing.expect(X25519.create(output[0..], &k, &u));
117
118 std.mem.copy(u8, u[0..], k[0..]);
119 std.mem.copy(u8, k[0..], output[0..]);
120 }
121
122 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
123}
124
125test "x25519 rfc7748 1,000,000 iterations" {
126 if (true) {
127 return error.SkipZigTest;
128 }
129
130 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";
131 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";
132
133 var k: [32]u8 = initial_value.*;
134 var u: [32]u8 = initial_value.*;
135
136 var i: usize = 0;
137 while (i < 1000000) : (i += 1) {
138 var output: [32]u8 = undefined;
139 std.testing.expect(X25519.create(output[0..], &k, &u));
140
141 std.mem.copy(u8, u[0..], k[0..]);
142 std.mem.copy(u8, k[0..], output[0..]);
143 }
144
145 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
146}
lib/std/crypto/x25519.zig deleted-675
......@@ -1,675 +0,0 @@
1// Translated from monocypher which is licensed under CC-0/BSD-3.
2//
3// https://monocypher.org/
4
5const std = @import("../std.zig");
6const builtin = @import("builtin");
7const fmt = std.fmt;
8
9const Endian = builtin.Endian;
10const readIntLittle = std.mem.readIntLittle;
11const writeIntLittle = std.mem.writeIntLittle;
12
13// Based on Supercop's ref10 implementation.
14pub const X25519 = struct {
15 pub const secret_length = 32;
16 pub const minimum_key_length = 32;
17
18 fn trimScalar(s: []u8) void {
19 s[0] &= 248;
20 s[31] &= 127;
21 s[31] |= 64;
22 }
23
24 fn scalarBit(s: []const u8, i: usize) i32 {
25 return (s[i >> 3] >> @intCast(u3, i & 7)) & 1;
26 }
27
28 pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool {
29 std.debug.assert(out.len >= secret_length);
30 std.debug.assert(private_key.len >= minimum_key_length);
31 std.debug.assert(public_key.len >= minimum_key_length);
32
33 var storage: [7]Fe = undefined;
34 var x1 = &storage[0];
35 var x2 = &storage[1];
36 var z2 = &storage[2];
37 var x3 = &storage[3];
38 var z3 = &storage[4];
39 var t0 = &storage[5];
40 var t1 = &storage[6];
41
42 // computes the scalar product
43 Fe.fromBytes(x1, public_key);
44
45 // restrict the possible scalar values
46 var e: [32]u8 = undefined;
47 for (e[0..]) |_, i| {
48 e[i] = private_key[i];
49 }
50 trimScalar(e[0..]);
51
52 // computes the actual scalar product (the result is in x2 and z2)
53
54 // Montgomery ladder
55 // In projective coordinates, to avoid divisions: x = X / Z
56 // We don't care about the y coordinate, it's only 1 bit of information
57 Fe.init1(x2);
58 Fe.init0(z2); // "zero" point
59 Fe.copy(x3, x1);
60 Fe.init1(z3);
61
62 var swap: i32 = 0;
63 var pos: isize = 254;
64 while (pos >= 0) : (pos -= 1) {
65 // constant time conditional swap before ladder step
66 const b = scalarBit(&e, @intCast(usize, pos));
67 swap ^= b; // xor trick avoids swapping at the end of the loop
68 Fe.cswap(x2, x3, swap);
69 Fe.cswap(z2, z3, swap);
70 swap = b; // anticipates one last swap after the loop
71
72 // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
73 // with differential addition
74 Fe.sub(t0, x3, z3);
75 Fe.sub(t1, x2, z2);
76 Fe.add(x2, x2, z2);
77 Fe.add(z2, x3, z3);
78 Fe.mul(z3, t0, x2);
79 Fe.mul(z2, z2, t1);
80 Fe.sq(t0, t1);
81 Fe.sq(t1, x2);
82 Fe.add(x3, z3, z2);
83 Fe.sub(z2, z3, z2);
84 Fe.mul(x2, t1, t0);
85 Fe.sub(t1, t1, t0);
86 Fe.sq(z2, z2);
87 Fe.mulSmall(z3, t1, 121666);
88 Fe.sq(x3, x3);
89 Fe.add(t0, t0, z3);
90 Fe.mul(z3, x1, z2);
91 Fe.mul(z2, t1, t0);
92 }
93
94 // last swap is necessary to compensate for the xor trick
95 // Note: after this swap, P3 == P2 + P1.
96 Fe.cswap(x2, x3, swap);
97 Fe.cswap(z2, z3, swap);
98
99 // normalises the coordinates: x == X / Z
100 Fe.invert(z2, z2);
101 Fe.mul(x2, x2, z2);
102 Fe.toBytes(out, x2);
103
104 x1.secureZero();
105 x2.secureZero();
106 x3.secureZero();
107 t0.secureZero();
108 t1.secureZero();
109 z2.secureZero();
110 z3.secureZero();
111 std.mem.secureZero(u8, e[0..]);
112
113 // Returns false if the output is all zero
114 // (happens with some malicious public keys)
115 return !zerocmp(u8, out);
116 }
117
118 pub fn createPublicKey(public_key: []u8, private_key: []const u8) bool {
119 var base_point = [_]u8{9} ++ [_]u8{0} ** 31;
120 return create(public_key, private_key, &base_point);
121 }
122};
123
124// Constant time compare to zero.
125fn zerocmp(comptime T: type, a: []const T) bool {
126 var s: T = 0;
127 for (a) |b| {
128 s |= b;
129 }
130 return s == 0;
131}
132
133////////////////////////////////////
134/// Arithmetic modulo 2^255 - 19 ///
135////////////////////////////////////
136// Taken from Supercop's ref10 implementation.
137// A bit bigger than TweetNaCl, over 4 times faster.
138
139// field element
140const Fe = struct {
141 b: [10]i32,
142
143 fn secureZero(self: *Fe) void {
144 std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]);
145 }
146
147 fn init0(h: *Fe) void {
148 for (h.b) |*e| {
149 e.* = 0;
150 }
151 }
152
153 fn init1(h: *Fe) void {
154 for (h.b[1..]) |*e| {
155 e.* = 0;
156 }
157 h.b[0] = 1;
158 }
159
160 fn copy(h: *Fe, f: *const Fe) void {
161 for (h.b) |_, i| {
162 h.b[i] = f.b[i];
163 }
164 }
165
166 fn neg(h: *Fe, f: *const Fe) void {
167 for (h.b) |_, i| {
168 h.b[i] = -f.b[i];
169 }
170 }
171
172 fn add(h: *Fe, f: *const Fe, g: *const Fe) void {
173 for (h.b) |_, i| {
174 h.b[i] = f.b[i] + g.b[i];
175 }
176 }
177
178 fn sub(h: *Fe, f: *const Fe, g: *const Fe) void {
179 for (h.b) |_, i| {
180 h.b[i] = f.b[i] - g.b[i];
181 }
182 }
183
184 fn cswap(f: *Fe, g: *Fe, b: i32) void {
185 for (f.b) |_, i| {
186 const x = (f.b[i] ^ g.b[i]) & -b;
187 f.b[i] ^= x;
188 g.b[i] ^= x;
189 }
190 }
191
192 fn ccopy(f: *Fe, g: *const Fe, b: i32) void {
193 for (f.b) |_, i| {
194 const x = (f.b[i] ^ g.b[i]) & -b;
195 f.b[i] ^= x;
196 }
197 }
198
199 inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void {
200 const j = (i + 1) % 10;
201
202 c[i] = (t[i] + (@as(i64, 1) << shift)) >> (shift + 1);
203 t[j] += c[i] * mult;
204 t[i] -= c[i] * (@as(i64, 1) << (shift + 1));
205 }
206
207 fn carry1(h: *Fe, t: []i64) void {
208 var c: [10]i64 = undefined;
209
210 var sc = c[0..];
211 var st = t[0..];
212
213 carryRound(sc, st, 9, 24, 19);
214 carryRound(sc, st, 1, 24, 1);
215 carryRound(sc, st, 3, 24, 1);
216 carryRound(sc, st, 5, 24, 1);
217 carryRound(sc, st, 7, 24, 1);
218 carryRound(sc, st, 0, 25, 1);
219 carryRound(sc, st, 2, 25, 1);
220 carryRound(sc, st, 4, 25, 1);
221 carryRound(sc, st, 6, 25, 1);
222 carryRound(sc, st, 8, 25, 1);
223
224 for (h.b) |_, i| {
225 h.b[i] = @intCast(i32, t[i]);
226 }
227 }
228
229 fn carry2(h: *Fe, t: []i64) void {
230 var c: [10]i64 = undefined;
231
232 var sc = c[0..];
233 var st = t[0..];
234
235 carryRound(sc, st, 0, 25, 1);
236 carryRound(sc, st, 4, 25, 1);
237 carryRound(sc, st, 1, 24, 1);
238 carryRound(sc, st, 5, 24, 1);
239 carryRound(sc, st, 2, 25, 1);
240 carryRound(sc, st, 6, 25, 1);
241 carryRound(sc, st, 3, 24, 1);
242 carryRound(sc, st, 7, 24, 1);
243 carryRound(sc, st, 4, 25, 1);
244 carryRound(sc, st, 8, 25, 1);
245 carryRound(sc, st, 9, 24, 19);
246 carryRound(sc, st, 0, 25, 1);
247
248 for (h.b) |_, i| {
249 h.b[i] = @intCast(i32, t[i]);
250 }
251 }
252
253 fn fromBytes(h: *Fe, s: []const u8) void {
254 std.debug.assert(s.len >= 32);
255
256 var t: [10]i64 = undefined;
257
258 t[0] = readIntLittle(u32, s[0..4]);
259 t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6;
260 t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5;
261 t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3;
262 t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2;
263 t[5] = readIntLittle(u32, s[16..20]);
264 t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7;
265 t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5;
266 t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4;
267 t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2;
268
269 carry1(h, t[0..]);
270 }
271
272 fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void {
273 var t: [10]i64 = undefined;
274
275 for (t[0..]) |_, i| {
276 t[i] = @as(i64, f.b[i]) * g;
277 }
278
279 carry1(h, t[0..]);
280 }
281
282 fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void {
283 const f = f1.b;
284 const g = g1.b;
285
286 var F: [10]i32 = undefined;
287 var G: [10]i32 = undefined;
288
289 F[1] = f[1] * 2;
290 F[3] = f[3] * 2;
291 F[5] = f[5] * 2;
292 F[7] = f[7] * 2;
293 F[9] = f[9] * 2;
294
295 G[1] = g[1] * 19;
296 G[2] = g[2] * 19;
297 G[3] = g[3] * 19;
298 G[4] = g[4] * 19;
299 G[5] = g[5] * 19;
300 G[6] = g[6] * 19;
301 G[7] = g[7] * 19;
302 G[8] = g[8] * 19;
303 G[9] = g[9] * 19;
304
305 // t's become h
306 var t: [10]i64 = undefined;
307
308 t[0] = f[0] * @as(i64, g[0]) + F[1] * @as(i64, G[9]) + f[2] * @as(i64, G[8]) + F[3] * @as(i64, G[7]) + f[4] * @as(i64, G[6]) + F[5] * @as(i64, G[5]) + f[6] * @as(i64, G[4]) + F[7] * @as(i64, G[3]) + f[8] * @as(i64, G[2]) + F[9] * @as(i64, G[1]);
309 t[1] = f[0] * @as(i64, g[1]) + f[1] * @as(i64, g[0]) + f[2] * @as(i64, G[9]) + f[3] * @as(i64, G[8]) + f[4] * @as(i64, G[7]) + f[5] * @as(i64, G[6]) + f[6] * @as(i64, G[5]) + f[7] * @as(i64, G[4]) + f[8] * @as(i64, G[3]) + f[9] * @as(i64, G[2]);
310 t[2] = f[0] * @as(i64, g[2]) + F[1] * @as(i64, g[1]) + f[2] * @as(i64, g[0]) + F[3] * @as(i64, G[9]) + f[4] * @as(i64, G[8]) + F[5] * @as(i64, G[7]) + f[6] * @as(i64, G[6]) + F[7] * @as(i64, G[5]) + f[8] * @as(i64, G[4]) + F[9] * @as(i64, G[3]);
311 t[3] = f[0] * @as(i64, g[3]) + f[1] * @as(i64, g[2]) + f[2] * @as(i64, g[1]) + f[3] * @as(i64, g[0]) + f[4] * @as(i64, G[9]) + f[5] * @as(i64, G[8]) + f[6] * @as(i64, G[7]) + f[7] * @as(i64, G[6]) + f[8] * @as(i64, G[5]) + f[9] * @as(i64, G[4]);
312 t[4] = f[0] * @as(i64, g[4]) + F[1] * @as(i64, g[3]) + f[2] * @as(i64, g[2]) + F[3] * @as(i64, g[1]) + f[4] * @as(i64, g[0]) + F[5] * @as(i64, G[9]) + f[6] * @as(i64, G[8]) + F[7] * @as(i64, G[7]) + f[8] * @as(i64, G[6]) + F[9] * @as(i64, G[5]);
313 t[5] = f[0] * @as(i64, g[5]) + f[1] * @as(i64, g[4]) + f[2] * @as(i64, g[3]) + f[3] * @as(i64, g[2]) + f[4] * @as(i64, g[1]) + f[5] * @as(i64, g[0]) + f[6] * @as(i64, G[9]) + f[7] * @as(i64, G[8]) + f[8] * @as(i64, G[7]) + f[9] * @as(i64, G[6]);
314 t[6] = f[0] * @as(i64, g[6]) + F[1] * @as(i64, g[5]) + f[2] * @as(i64, g[4]) + F[3] * @as(i64, g[3]) + f[4] * @as(i64, g[2]) + F[5] * @as(i64, g[1]) + f[6] * @as(i64, g[0]) + F[7] * @as(i64, G[9]) + f[8] * @as(i64, G[8]) + F[9] * @as(i64, G[7]);
315 t[7] = f[0] * @as(i64, g[7]) + f[1] * @as(i64, g[6]) + f[2] * @as(i64, g[5]) + f[3] * @as(i64, g[4]) + f[4] * @as(i64, g[3]) + f[5] * @as(i64, g[2]) + f[6] * @as(i64, g[1]) + f[7] * @as(i64, g[0]) + f[8] * @as(i64, G[9]) + f[9] * @as(i64, G[8]);
316 t[8] = f[0] * @as(i64, g[8]) + F[1] * @as(i64, g[7]) + f[2] * @as(i64, g[6]) + F[3] * @as(i64, g[5]) + f[4] * @as(i64, g[4]) + F[5] * @as(i64, g[3]) + f[6] * @as(i64, g[2]) + F[7] * @as(i64, g[1]) + f[8] * @as(i64, g[0]) + F[9] * @as(i64, G[9]);
317 t[9] = f[0] * @as(i64, g[9]) + f[1] * @as(i64, g[8]) + f[2] * @as(i64, g[7]) + f[3] * @as(i64, g[6]) + f[4] * @as(i64, g[5]) + f[5] * @as(i64, g[4]) + f[6] * @as(i64, g[3]) + f[7] * @as(i64, g[2]) + f[8] * @as(i64, g[1]) + f[9] * @as(i64, g[0]);
318
319 carry2(h, t[0..]);
320 }
321
322 // we could use Fe.mul() for this, but this is significantly faster
323 fn sq(h: *Fe, fz: *const Fe) void {
324 const f0 = fz.b[0];
325 const f1 = fz.b[1];
326 const f2 = fz.b[2];
327 const f3 = fz.b[3];
328 const f4 = fz.b[4];
329 const f5 = fz.b[5];
330 const f6 = fz.b[6];
331 const f7 = fz.b[7];
332 const f8 = fz.b[8];
333 const f9 = fz.b[9];
334
335 const f0_2 = f0 * 2;
336 const f1_2 = f1 * 2;
337 const f2_2 = f2 * 2;
338 const f3_2 = f3 * 2;
339 const f4_2 = f4 * 2;
340 const f5_2 = f5 * 2;
341 const f6_2 = f6 * 2;
342 const f7_2 = f7 * 2;
343 const f5_38 = f5 * 38;
344 const f6_19 = f6 * 19;
345 const f7_38 = f7 * 38;
346 const f8_19 = f8 * 19;
347 const f9_38 = f9 * 38;
348
349 var t: [10]i64 = undefined;
350
351 t[0] = f0 * @as(i64, f0) + f1_2 * @as(i64, f9_38) + f2_2 * @as(i64, f8_19) + f3_2 * @as(i64, f7_38) + f4_2 * @as(i64, f6_19) + f5 * @as(i64, f5_38);
352 t[1] = f0_2 * @as(i64, f1) + f2 * @as(i64, f9_38) + f3_2 * @as(i64, f8_19) + f4 * @as(i64, f7_38) + f5_2 * @as(i64, f6_19);
353 t[2] = f0_2 * @as(i64, f2) + f1_2 * @as(i64, f1) + f3_2 * @as(i64, f9_38) + f4_2 * @as(i64, f8_19) + f5_2 * @as(i64, f7_38) + f6 * @as(i64, f6_19);
354 t[3] = f0_2 * @as(i64, f3) + f1_2 * @as(i64, f2) + f4 * @as(i64, f9_38) + f5_2 * @as(i64, f8_19) + f6 * @as(i64, f7_38);
355 t[4] = f0_2 * @as(i64, f4) + f1_2 * @as(i64, f3_2) + f2 * @as(i64, f2) + f5_2 * @as(i64, f9_38) + f6_2 * @as(i64, f8_19) + f7 * @as(i64, f7_38);
356 t[5] = f0_2 * @as(i64, f5) + f1_2 * @as(i64, f4) + f2_2 * @as(i64, f3) + f6 * @as(i64, f9_38) + f7_2 * @as(i64, f8_19);
357 t[6] = f0_2 * @as(i64, f6) + f1_2 * @as(i64, f5_2) + f2_2 * @as(i64, f4) + f3_2 * @as(i64, f3) + f7_2 * @as(i64, f9_38) + f8 * @as(i64, f8_19);
358 t[7] = f0_2 * @as(i64, f7) + f1_2 * @as(i64, f6) + f2_2 * @as(i64, f5) + f3_2 * @as(i64, f4) + f8 * @as(i64, f9_38);
359 t[8] = f0_2 * @as(i64, f8) + f1_2 * @as(i64, f7_2) + f2_2 * @as(i64, f6) + f3_2 * @as(i64, f5_2) + f4 * @as(i64, f4) + f9 * @as(i64, f9_38);
360 t[9] = f0_2 * @as(i64, f9) + f1_2 * @as(i64, f8) + f2_2 * @as(i64, f7) + f3_2 * @as(i64, f6) + f4 * @as(i64, f5_2);
361
362 carry2(h, t[0..]);
363 }
364
365 fn sq2(h: *Fe, f: *const Fe) void {
366 Fe.sq(h, f);
367 Fe.mul_small(h, h, 2);
368 }
369
370 // This could be simplified, but it would be slower
371 fn invert(out: *Fe, z: *const Fe) void {
372 var i: usize = undefined;
373
374 var t: [4]Fe = undefined;
375 var t0 = &t[0];
376 var t1 = &t[1];
377 var t2 = &t[2];
378 var t3 = &t[3];
379
380 Fe.sq(t0, z);
381 Fe.sq(t1, t0);
382 Fe.sq(t1, t1);
383 Fe.mul(t1, z, t1);
384 Fe.mul(t0, t0, t1);
385
386 Fe.sq(t2, t0);
387 Fe.mul(t1, t1, t2);
388
389 Fe.sq(t2, t1);
390 i = 1;
391 while (i < 5) : (i += 1) Fe.sq(t2, t2);
392 Fe.mul(t1, t2, t1);
393
394 Fe.sq(t2, t1);
395 i = 1;
396 while (i < 10) : (i += 1) Fe.sq(t2, t2);
397 Fe.mul(t2, t2, t1);
398
399 Fe.sq(t3, t2);
400 i = 1;
401 while (i < 20) : (i += 1) Fe.sq(t3, t3);
402 Fe.mul(t2, t3, t2);
403
404 Fe.sq(t2, t2);
405 i = 1;
406 while (i < 10) : (i += 1) Fe.sq(t2, t2);
407 Fe.mul(t1, t2, t1);
408
409 Fe.sq(t2, t1);
410 i = 1;
411 while (i < 50) : (i += 1) Fe.sq(t2, t2);
412 Fe.mul(t2, t2, t1);
413
414 Fe.sq(t3, t2);
415 i = 1;
416 while (i < 100) : (i += 1) Fe.sq(t3, t3);
417 Fe.mul(t2, t3, t2);
418
419 Fe.sq(t2, t2);
420 i = 1;
421 while (i < 50) : (i += 1) Fe.sq(t2, t2);
422 Fe.mul(t1, t2, t1);
423
424 Fe.sq(t1, t1);
425 i = 1;
426 while (i < 5) : (i += 1) Fe.sq(t1, t1);
427 Fe.mul(out, t1, t0);
428
429 t0.secureZero();
430 t1.secureZero();
431 t2.secureZero();
432 t3.secureZero();
433 }
434
435 // This could be simplified, but it would be slower
436 fn pow22523(out: *Fe, z: *const Fe) void {
437 var i: usize = undefined;
438
439 var t: [3]Fe = undefined;
440 var t0 = &t[0];
441 var t1 = &t[1];
442 var t2 = &t[2];
443
444 Fe.sq(t0, z);
445 Fe.sq(t1, t0);
446 Fe.sq(t1, t1);
447 Fe.mul(t1, z, t1);
448 Fe.mul(t0, t0, t1);
449
450 Fe.sq(t0, t0);
451 Fe.mul(t0, t1, t0);
452
453 Fe.sq(t1, t0);
454 i = 1;
455 while (i < 5) : (i += 1) Fe.sq(t1, t1);
456 Fe.mul(t0, t1, t0);
457
458 Fe.sq(t1, t0);
459 i = 1;
460 while (i < 10) : (i += 1) Fe.sq(t1, t1);
461 Fe.mul(t1, t1, t0);
462
463 Fe.sq(t2, t1);
464 i = 1;
465 while (i < 20) : (i += 1) Fe.sq(t2, t2);
466 Fe.mul(t1, t2, t1);
467
468 Fe.sq(t1, t1);
469 i = 1;
470 while (i < 10) : (i += 1) Fe.sq(t1, t1);
471 Fe.mul(t0, t1, t0);
472
473 Fe.sq(t1, t0);
474 i = 1;
475 while (i < 50) : (i += 1) Fe.sq(t1, t1);
476 Fe.mul(t1, t1, t0);
477
478 Fe.sq(t2, t1);
479 i = 1;
480 while (i < 100) : (i += 1) Fe.sq(t2, t2);
481 Fe.mul(t1, t2, t1);
482
483 Fe.sq(t1, t1);
484 i = 1;
485 while (i < 50) : (i += 1) Fe.sq(t1, t1);
486 Fe.mul(t0, t1, t0);
487
488 Fe.sq(t0, t0);
489 i = 1;
490 while (i < 2) : (i += 1) Fe.sq(t0, t0);
491 Fe.mul(out, t0, z);
492
493 t0.secureZero();
494 t1.secureZero();
495 t2.secureZero();
496 }
497
498 inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void {
499 c[i] = t[i] >> shift;
500 if (i + 1 < 10) {
501 t[i + 1] += c[i];
502 }
503 t[i] -= c[i] * (@as(i32, 1) << shift);
504 }
505
506 fn toBytes(s: []u8, h: *const Fe) void {
507 std.debug.assert(s.len >= 32);
508
509 var t: [10]i64 = undefined;
510 for (h.b[0..]) |_, i| {
511 t[i] = h.b[i];
512 }
513
514 var q = (19 * t[9] + ((@as(i32, 1) << 24))) >> 25;
515 {
516 var i: usize = 0;
517 while (i < 5) : (i += 1) {
518 q += t[2 * i];
519 q >>= 26;
520 q += t[2 * i + 1];
521 q >>= 25;
522 }
523 }
524 t[0] += 19 * q;
525
526 var c: [10]i64 = undefined;
527
528 var st = t[0..];
529 var sc = c[0..];
530
531 toBytesRound(sc, st, 0, 26);
532 toBytesRound(sc, st, 1, 25);
533 toBytesRound(sc, st, 2, 26);
534 toBytesRound(sc, st, 3, 25);
535 toBytesRound(sc, st, 4, 26);
536 toBytesRound(sc, st, 5, 25);
537 toBytesRound(sc, st, 6, 26);
538 toBytesRound(sc, st, 7, 25);
539 toBytesRound(sc, st, 8, 26);
540 toBytesRound(sc, st, 9, 25);
541
542 var ut: [10]u32 = undefined;
543 for (ut[0..]) |_, i| {
544 ut[i] = @bitCast(u32, @intCast(i32, t[i]));
545 }
546
547 writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26));
548 writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19));
549 writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13));
550 writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6));
551 writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25));
552 writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19));
553 writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12));
554 writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6));
555
556 std.mem.secureZero(i64, t[0..]);
557 }
558
559 // Parity check. Returns 0 if even, 1 if odd
560 fn isNegative(f: *const Fe) bool {
561 var s: [32]u8 = undefined;
562 Fe.toBytes(s[0..], f);
563 const isneg = s[0] & 1;
564 s.secureZero();
565 return isneg;
566 }
567
568 fn isNonZero(f: *const Fe) bool {
569 var s: [32]u8 = undefined;
570 Fe.toBytes(s[0..], f);
571 const isnonzero = zerocmp(u8, s[0..]);
572 s.secureZero();
573 return isneg;
574 }
575};
576
577test "x25519 public key calculation from secret key" {
578 var sk: [32]u8 = undefined;
579 var pk_expected: [32]u8 = undefined;
580 var pk_calculated: [32]u8 = undefined;
581 try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
582 try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
583 std.testing.expect(X25519.createPublicKey(pk_calculated[0..], &sk));
584 std.testing.expect(std.mem.eql(u8, &pk_calculated, &pk_expected));
585}
586
587test "x25519 rfc7748 vector1" {
588 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";
589 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";
590
591 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";
592
593 var output: [32]u8 = undefined;
594
595 std.testing.expect(X25519.create(output[0..], secret_key, public_key));
596 std.testing.expect(std.mem.eql(u8, &output, expected_output));
597}
598
599test "x25519 rfc7748 vector2" {
600 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";
601 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";
602
603 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";
604
605 var output: [32]u8 = undefined;
606
607 std.testing.expect(X25519.create(output[0..], secret_key, public_key));
608 std.testing.expect(std.mem.eql(u8, &output, expected_output));
609}
610
611test "x25519 rfc7748 one iteration" {
612 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".*;
613 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";
614
615 var k: [32]u8 = initial_value;
616 var u: [32]u8 = initial_value;
617
618 var i: usize = 0;
619 while (i < 1) : (i += 1) {
620 var output: [32]u8 = undefined;
621 std.testing.expect(X25519.create(output[0..], &k, &u));
622
623 std.mem.copy(u8, u[0..], k[0..]);
624 std.mem.copy(u8, k[0..], output[0..]);
625 }
626
627 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
628}
629
630test "x25519 rfc7748 1,000 iterations" {
631 // These iteration tests are slow so we always skip them. Results have been verified.
632 if (true) {
633 return error.SkipZigTest;
634 }
635
636 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";
637 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";
638
639 var k: [32]u8 = initial_value.*;
640 var u: [32]u8 = initial_value.*;
641
642 var i: usize = 0;
643 while (i < 1000) : (i += 1) {
644 var output: [32]u8 = undefined;
645 std.testing.expect(X25519.create(output[0..], &k, &u));
646
647 std.mem.copy(u8, u[0..], k[0..]);
648 std.mem.copy(u8, k[0..], output[0..]);
649 }
650
651 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
652}
653
654test "x25519 rfc7748 1,000,000 iterations" {
655 if (true) {
656 return error.SkipZigTest;
657 }
658
659 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";
660 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";
661
662 var k: [32]u8 = initial_value.*;
663 var u: [32]u8 = initial_value.*;
664
665 var i: usize = 0;
666 while (i < 1000000) : (i += 1) {
667 var output: [32]u8 = undefined;
668 std.testing.expect(X25519.create(output[0..], &k, &u));
669
670 std.mem.copy(u8, u[0..], k[0..]);
671 std.mem.copy(u8, k[0..], output[0..]);
672 }
673
674 std.testing.expect(std.mem.eql(u8, k[0..], expected_output));
675}
lib/std/mem.zig+25
......@@ -334,6 +334,31 @@ test "mem.secureZero" {
334334 testing.expectEqualSlices(u8, a[0..], b[0..]);
335335}
336336
337/// Constant-time (for a given length) comparison.
338pub fn timingSafeEqual(comptime T: type, a: []const T, b: []const T) bool {
339 const length = a.len;
340 if (length != b.len) {
341 return false;
342 }
343 const ap = @ptrCast([*]const volatile T, a.ptr);
344 const bp = @ptrCast([*]const volatile T, b.ptr);
345 var c: u8 = 0;
346 var i: usize = 0;
347 while (i < length) : (i += 1) {
348 c |= a[i] ^ b[i];
349 }
350 return c == 0;
351}
352
353test "mem.timingSafeEqual" {
354 var a = [_]u8{0xfe} ** 8;
355 var b = [_]u8{0xfe} ** 8;
356
357 testing.expect(timingSafeEqual(u8, &a, &b));
358 a[0] += 1;
359 testing.expect(!timingSafeEqual(u8, &a, &b));
360}
361
337362/// Initializes all fields of the struct with their default value, or zero values if no default value is present.
338363/// If the field is present in the provided initial values, it will have that value instead.
339364/// Structs are initialized recursively.