authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-16 22:35:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-16 22:35:39-07:00
logaddeff889a1017beeef8c710dfdd55dbda2449e3
tree36beac5edb4683d4b14bd395addcf55f846e315d
parentf46e375bbe0ac0893717bd477eab78f51863e277
parent7f9a227abfbce0e67746cc57dd9b6a4bf0a8d94a

Merge branch 'jedisct1-25519'

closes #6050

10 files changed, 1315 insertions(+), 678 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/field.zig");
67 _ = @import("crypto/25519/scalar.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+144
......@@ -0,0 +1,144 @@
1const std = @import("std");
2
3/// Group operations over Curve25519.
4pub const Curve25519 = struct {
5 /// The underlying prime field.
6 pub const Fe = @import("field.zig").Fe;
7 /// Field arithmetic mod the order of the main subgroup.
8 pub const scalar = @import("scalar.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 /// The Curve25519 base point.
23 pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
24
25 /// Check that the encoding of a Curve25519 point is canonical.
26 pub fn rejectNonCanonical(s: [32]u8) !void {
27 return Fe.rejectNonCanonical(s, false);
28 }
29
30 /// Reject the neutral element.
31 pub fn rejectIdentity(p: Curve25519) !void {
32 if (p.x.isZero()) {
33 return error.IdentityElement;
34 }
35 }
36
37 fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) !Curve25519 {
38 var x1 = p.x;
39 var x2 = Fe.one;
40 var z2 = Fe.zero;
41 var x3 = x1;
42 var z3 = Fe.one;
43 var swap: u8 = 0;
44 var pos: usize = bits - 1;
45 while (true) : (pos -= 1) {
46 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 1;
47 swap ^= bit;
48 Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
49 swap = bit;
50 const a = x2.add(z2);
51 const b = x2.sub(z2);
52 const aa = a.sq();
53 const bb = b.sq();
54 x2 = aa.mul(bb);
55 const e = aa.sub(bb);
56 const da = x3.sub(z3).mul(a);
57 const cb = x3.add(z3).mul(b);
58 x3 = da.add(cb).sq();
59 z3 = x1.mul(da.sub(cb).sq());
60 z2 = e.mul(bb.add(e.mul32(121666)));
61 if (pos == 0) break;
62 }
63 Fe.cSwap2(&x2, &x3, &z2, &z3, swap);
64 z2 = z2.invert();
65 x2 = x2.mul(z2);
66 if (x2.isZero()) {
67 return error.IdentityElement;
68 }
69 return Curve25519{ .x = x2 };
70 }
71
72 /// Multiply a Curve25519 point by a scalar after "clamping" it.
73 /// Clamping forces the scalar to be a multiple of the cofactor in
74 /// order to prevent small subgroups attacks. This is the standard
75 /// way to use Curve25519 for a DH operation.
76 /// Return error.IdentityElement if the resulting point is
77 /// the identity element.
78 pub fn clampedMul(p: Curve25519, s: [32]u8) !Curve25519 {
79 var t: [32]u8 = s;
80 scalar.clamp(&t);
81 return try ladder(p, t, 255);
82 }
83
84 /// Multiply a Curve25519 point by a scalar without clamping it.
85 /// Return error.IdentityElement if the resulting point is
86 /// the identity element or error.WeakPublicKey if the public
87 /// key is a low-order point.
88 pub fn mul(p: Curve25519, s: [32]u8) !Curve25519 {
89 const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
90 _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;
91 return try ladder(p, s, 256);
92 }
93};
94
95test "curve25519" {
96 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 };
97 const p = try Curve25519.basePoint.clampedMul(s);
98 try p.rejectIdentity();
99 var buf: [128]u8 = undefined;
100 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E6F2A4D1C28EE5C7AD0329268255A468AD407D2672824C0C0EB30EA6EF450145");
101 const q = try p.clampedMul(s);
102 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "3614E119FFE55EC55B87D6B19971A9F4CBC78EFE80BEC55B96392BABCC712537");
103
104 try Curve25519.rejectNonCanonical(s);
105 s[31] |= 0x80;
106 std.testing.expectError(error.NonCanonical, Curve25519.rejectNonCanonical(s));
107}
108
109test "curve25519 small order check" {
110 var s: [32]u8 = [_]u8{1} ++ [_]u8{0} ** 31;
111 const small_order_ss: [7][32]u8 = .{
112 .{
113 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)
114 },
115 .{
116 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)
117 },
118 .{
119 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) */
120 },
121 .{
122 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)
123 },
124 .{
125 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)
126 },
127 .{
128 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)
129 },
130 .{
131 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)
132 },
133 };
134 for (small_order_ss) |small_order_s| {
135 std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s));
136 var extra = small_order_s;
137 extra[31] ^= 0x80;
138 std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(extra).mul(s));
139 var valid = small_order_s;
140 valid[31] = 0x40;
141 s[0] = 0;
142 std.testing.expectError(error.IdentityElement, Curve25519.fromBytes(valid).mul(s));
143 }
144}
lib/std/crypto/25519/ed25519.zig created+134
......@@ -0,0 +1,134 @@
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 ///
23 /// As in RFC 8032, an Ed25519 public key is generated by hashing
24 /// the secret key using the SHA-512 function, and interpreting the
25 /// bit-swapped, clamped lower-half of the output as the secret scalar.
26 ///
27 /// For this reason, an EdDSA secret key is commonly called a seed,
28 /// from which the actual secret is derived.
29 pub fn createKeyPair(seed: [seed_length]u8) ![keypair_length]u8 {
30 var az: [Sha512.digest_length]u8 = undefined;
31 var h = Sha512.init();
32 h.update(&seed);
33 h.final(&az);
34 const p = try Curve.basePoint.clampedMul(az[0..32].*);
35 var keypair: [keypair_length]u8 = undefined;
36 mem.copy(u8, &keypair, &seed);
37 mem.copy(u8, keypair[seed_length..], &p.toBytes());
38 return keypair;
39 }
40
41 /// Return the public key for a given key pair.
42 pub fn publicKey(key_pair: [keypair_length]u8) [public_length]u8 {
43 var public_key: [public_length]u8 = undefined;
44 mem.copy(u8, public_key[0..], key_pair[seed_length..]);
45 return public_key;
46 }
47
48 /// Sign a message using a key pair, and optional random noise.
49 /// Having noise creates non-standard, non-deterministic signatures,
50 /// but has been proven to increase resilience against fault attacks.
51 pub fn sign(msg: []const u8, key_pair: [keypair_length]u8, noise: ?[noise_length]u8) ![signature_length]u8 {
52 const public_key = key_pair[32..];
53 var az: [Sha512.digest_length]u8 = undefined;
54 var h = Sha512.init();
55 h.update(key_pair[0..seed_length]);
56 h.final(&az);
57
58 h = Sha512.init();
59 if (noise) |*z| {
60 h.update(z);
61 }
62 h.update(az[32..]);
63 h.update(msg);
64 var nonce64: [64]u8 = undefined;
65 h.final(&nonce64);
66 const nonce = Curve.scalar.reduce64(nonce64);
67 const r = try Curve.basePoint.mul(nonce);
68
69 var sig: [signature_length]u8 = undefined;
70 mem.copy(u8, sig[0..32], &r.toBytes());
71 mem.copy(u8, sig[32..], public_key);
72 h = Sha512.init();
73 h.update(&sig);
74 h.update(msg);
75 var hram64: [Sha512.digest_length]u8 = undefined;
76 h.final(&hram64);
77 const hram = Curve.scalar.reduce64(hram64);
78
79 var x = az[0..32];
80 Curve.scalar.clamp(x);
81 const s = Curve.scalar.mulAdd(hram, x.*, nonce);
82 mem.copy(u8, sig[32..], s[0..]);
83 return sig;
84 }
85
86 /// Verify an Ed25519 signature given a message and a public key.
87 /// Returns error.InvalidSignature is the signature verification failed.
88 pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) !void {
89 const r = sig[0..32];
90 const s = sig[32..64];
91 try Curve.scalar.rejectNonCanonical(s.*);
92 try Curve.rejectNonCanonical(public_key);
93 const a = try Curve.fromBytes(public_key);
94 try a.rejectIdentity();
95
96 var h = Sha512.init();
97 h.update(r);
98 h.update(&public_key);
99 h.update(msg);
100 var hram64: [Sha512.digest_length]u8 = undefined;
101 h.final(&hram64);
102 const hram = Curve.scalar.reduce64(hram64);
103
104 const p = try a.neg().mul(hram);
105 const check = (try Curve.basePoint.mul(s.*)).add(p).toBytes();
106 if (mem.eql(u8, &check, r) == false) {
107 return error.InvalidSignature;
108 }
109 }
110};
111
112test "ed25519 key pair creation" {
113 var seed: [32]u8 = undefined;
114 try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
115 const key_pair = try Ed25519.createKeyPair(seed);
116 var buf: [256]u8 = undefined;
117 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
118
119 const public_key = Ed25519.publicKey(key_pair);
120 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{public_key}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
121}
122
123test "ed25519 signature" {
124 var seed: [32]u8 = undefined;
125 try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
126 const key_pair = try Ed25519.createKeyPair(seed);
127
128 const sig = try Ed25519.sign("test", key_pair, null);
129 var buf: [128]u8 = undefined;
130 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{sig}), "10A442B4A80CC4225B154F43BEF28D2472CA80221951262EB8E0DF9091575E2687CC486E77263C3418C757522D54F84B0359236ABBBD4ACD20DC297FDCA66808");
131 const public_key = Ed25519.publicKey(key_pair);
132 try Ed25519.verify(sig, "test", public_key);
133 std.testing.expectError(error.InvalidSignature, Ed25519.verify(sig, "TEST", public_key));
134}
lib/std/crypto/25519/edwards25519.zig created+214
......@@ -0,0 +1,214 @@
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("field.zig").Fe;
8 /// Field arithmetic mod the order of the main subgroup.
9 pub const scalar = @import("scalar.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) { // best-effort to avoid two conditional branches
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 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 /// The edwards25519 base point.
54 pub const basePoint = Edwards25519{
55 .x = Fe{ .limbs = .{ 3990542415680775, 3398198340507945, 4322667446711068, 2814063955482877, 2839572215813860 } },
56 .y = Fe{ .limbs = .{ 1801439850948184, 1351079888211148, 450359962737049, 900719925474099, 1801439850948198 } },
57 .z = Fe.one,
58 .t = Fe{ .limbs = .{ 1841354044333475, 16398895984059, 755974180946558, 900171276175154, 1821297809914039 } },
59 .is_base = true,
60 };
61
62 const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
63
64 /// Reject the neutral element.
65 pub fn rejectIdentity(p: Edwards25519) !void {
66 if (p.x.isZero()) {
67 return error.IdentityElement;
68 }
69 }
70
71 /// Flip the sign of the X coordinate.
72 pub inline fn neg(p: Edwards25519) Edwards25519 {
73 return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() };
74 }
75
76 /// Double an Edwards25519 point.
77 pub fn dbl(p: Edwards25519) Edwards25519 {
78 const t0 = p.x.add(p.y).sq();
79 var x = p.x.sq();
80 var z = p.y.sq();
81 const y = z.add(x);
82 z = z.sub(x);
83 x = t0.sub(y);
84 const t = p.z.sq2().sub(z);
85 return .{
86 .x = x.mul(t),
87 .y = y.mul(z),
88 .z = z.mul(t),
89 .t = x.mul(y),
90 };
91 }
92
93 /// Add two Edwards25519 points.
94 pub fn add(p: Edwards25519, q: Edwards25519) Edwards25519 {
95 const a = p.y.sub(p.x).mul(q.y.sub(q.x));
96 const b = p.x.add(p.y).mul(q.x.add(q.y));
97 const c = p.t.mul(q.t).mul(Fe.edwards25519d2);
98 var d = p.z.mul(q.z);
99 d = d.add(d);
100 const x = b.sub(a);
101 const y = b.add(a);
102 const z = d.add(c);
103 const t = d.sub(c);
104 return .{
105 .x = x.mul(t),
106 .y = y.mul(z),
107 .z = z.mul(t),
108 .t = x.mul(y),
109 };
110 }
111
112 inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void {
113 p.x.cMov(a.x, c);
114 p.y.cMov(a.y, c);
115 p.z.cMov(a.z, c);
116 p.t.cMov(a.t, c);
117 }
118
119 inline fn pcSelect(pc: [16]Edwards25519, b: u8) Edwards25519 {
120 var t = Edwards25519.identityElement;
121 comptime var i: u8 = 0;
122 inline while (i < 16) : (i += 1) {
123 t.cMov(pc[i], ((@as(usize, b ^ i) -% 1) >> 8) & 1);
124 }
125 return t;
126 }
127
128 fn pcMul(pc: [16]Edwards25519, s: [32]u8) !Edwards25519 {
129 var q = Edwards25519.identityElement;
130 var pos: usize = 252;
131 while (true) : (pos -= 4) {
132 q = q.dbl().dbl().dbl().dbl();
133 const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf;
134 q = q.add(pcSelect(pc, bit));
135 if (pos == 0) break;
136 }
137 try q.rejectIdentity();
138 return q;
139 }
140
141 fn precompute(p: Edwards25519) [16]Edwards25519 {
142 var pc: [16]Edwards25519 = undefined;
143 pc[0] = Edwards25519.identityElement;
144 pc[1] = p;
145 var i: usize = 2;
146 while (i < 16) : (i += 1) {
147 pc[i] = pc[i - 1].add(p);
148 }
149 return pc;
150 }
151
152 /// Multiply an Edwards25519 point by a scalar without clamping it.
153 /// Return error.WeakPublicKey if the resulting point is
154 /// the identity element.
155 pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 {
156 var pc: [16]Edwards25519 = undefined;
157 if (p.is_base) {
158 @setEvalBranchQuota(10000);
159 pc = comptime precompute(Edwards25519.basePoint);
160 } else {
161 pc = precompute(p);
162 pc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
163 }
164 return pcMul(pc, s);
165 }
166
167 /// Multiply an Edwards25519 point by a scalar after "clamping" it.
168 /// Clamping forces the scalar to be a multiple of the cofactor in
169 /// order to prevent small subgroups attacks.
170 /// This is strongly recommended for DH operations.
171 /// Return error.WeakPublicKey if the resulting point is
172 /// the identity element.
173 pub fn clampedMul(p: Edwards25519, s: [32]u8) !Edwards25519 {
174 var t: [32]u8 = s;
175 scalar.clamp(&t);
176 return mul(p, t);
177 }
178};
179
180test "edwards25519 packing/unpacking" {
181 const s = [_]u8{170} ++ [_]u8{0} ** 31;
182 var b = Edwards25519.basePoint;
183 const pk = try b.mul(s);
184 var buf: [128]u8 = undefined;
185 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{pk.toBytes()}), "074BC7E0FCBD587FDBC0969444245FADC562809C8F6E97E949AF62484B5B81A6");
186
187 const small_order_ss: [7][32]u8 = .{
188 .{
189 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)
190 },
191 .{
192 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)
193 },
194 .{
195 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)
196 },
197 .{
198 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)
199 },
200 .{
201 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)
202 },
203 .{
204 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)
205 },
206 .{
207 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)
208 },
209 };
210 for (small_order_ss) |small_order_s| {
211 const small_p = try Edwards25519.fromBytes(small_order_s);
212 std.testing.expectError(error.WeakPublicKey, small_p.mul(s));
213 }
214}
lib/std/crypto/25519/field.zig created+312
......@@ -0,0 +1,312 @@
1const std = @import("std");
2const readIntLittle = std.mem.readIntLittle;
3const writeIntLittle = std.mem.writeIntLittle;
4
5pub const Fe = struct {
6 limbs: [5]u64,
7
8 const MASK51: u64 = 0x7ffffffffffff;
9
10 pub const zero = Fe{ .limbs = .{ 0, 0, 0, 0, 0 } };
11
12 pub const one = Fe{ .limbs = .{ 1, 0, 0, 0, 0 } };
13
14 pub const sqrtm1 = Fe{ .limbs = .{ 1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133 } }; // sqrt(-1)
15
16 pub const curve25519BasePoint = Fe{ .limbs = .{ 9, 0, 0, 0, 0 } };
17
18 pub const edwards25519d = Fe{ .limbs = .{ 929955233495203, 466365720129213, 1662059464998953, 2033849074728123, 1442794654840575 } }; // 37095705934669439343138083508754565189542113879843219016388785533085940283555
19
20 pub const edwards25519d2 = Fe{ .limbs = .{ 1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903 } }; // 2d
21
22 pub const edwards25519sqrtamd = Fe{ .limbs = .{ 278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447 } }; // 1/sqrt(a-d)
23
24 pub inline fn isZero(fe: Fe) bool {
25 var reduced = fe;
26 reduced.reduce();
27 const limbs = reduced.limbs;
28 return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0;
29 }
30
31 pub inline fn equivalent(a: Fe, b: Fe) bool {
32 return a.sub(b).isZero();
33 }
34
35 pub fn fromBytes(s: [32]u8) Fe {
36 var fe: Fe = undefined;
37 fe.limbs[0] = readIntLittle(u64, s[0..8]) & MASK51;
38 fe.limbs[1] = (readIntLittle(u64, s[6..14]) >> 3) & MASK51;
39 fe.limbs[2] = (readIntLittle(u64, s[12..20]) >> 6) & MASK51;
40 fe.limbs[3] = (readIntLittle(u64, s[19..27]) >> 1) & MASK51;
41 fe.limbs[4] = (readIntLittle(u64, s[24..32]) >> 12) & MASK51;
42
43 return fe;
44 }
45
46 pub fn toBytes(fe: Fe) [32]u8 {
47 var reduced = fe;
48 reduced.reduce();
49 var s: [32]u8 = undefined;
50 writeIntLittle(u64, s[0..8], reduced.limbs[0] | (reduced.limbs[1] << 51));
51 writeIntLittle(u64, s[8..16], (reduced.limbs[1] >> 13) | (reduced.limbs[2] << 38));
52 writeIntLittle(u64, s[16..24], (reduced.limbs[2] >> 26) | (reduced.limbs[3] << 25));
53 writeIntLittle(u64, s[24..32], (reduced.limbs[3] >> 39) | (reduced.limbs[4] << 12));
54
55 return s;
56 }
57
58 pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) !void {
59 var c: u16 = (s[31] & 0x7f) ^ 0x7f;
60 comptime var i = 30;
61 inline while (i > 0) : (i -= 1) {
62 c |= s[i] ^ 0xff;
63 }
64 c = (c -% 1) >> 8;
65 const d = (@as(u16, 0xed - 1) -% @as(u16, s[0])) >> 8;
66 const x = if (ignore_extra_bit) 0 else s[31] >> 7;
67 if ((((c & d) | x) & 1) != 0) {
68 return error.NonCanonical;
69 }
70 }
71
72 fn reduce(fe: *Fe) void {
73 comptime var i = 0;
74 comptime var j = 0;
75 const limbs = &fe.limbs;
76 inline while (j < 2) : (j += 1) {
77 i = 0;
78 inline while (i < 4) : (i += 1) {
79 limbs[i + 1] += limbs[i] >> 51;
80 limbs[i] &= MASK51;
81 }
82 limbs[0] += 19 * (limbs[4] >> 51);
83 limbs[4] &= MASK51;
84 }
85 limbs[0] += 19;
86 i = 0;
87 inline while (i < 4) : (i += 1) {
88 limbs[i + 1] += limbs[i] >> 51;
89 limbs[i] &= MASK51;
90 }
91 limbs[0] += 19 * (limbs[4] >> 51);
92 limbs[4] &= MASK51;
93
94 limbs[0] += 0x8000000000000 - 19;
95 limbs[1] += 0x8000000000000 - 1;
96 limbs[2] += 0x8000000000000 - 1;
97 limbs[3] += 0x8000000000000 - 1;
98 limbs[4] += 0x8000000000000 - 1;
99
100 i = 0;
101 inline while (i < 4) : (i += 1) {
102 limbs[i + 1] += limbs[i] >> 51;
103 limbs[i] &= MASK51;
104 }
105 limbs[4] &= MASK51;
106 }
107
108 pub inline fn add(a: Fe, b: Fe) Fe {
109 var fe: Fe = undefined;
110 comptime var i = 0;
111 inline while (i < 5) : (i += 1) {
112 fe.limbs[i] = a.limbs[i] + b.limbs[i];
113 }
114 return fe;
115 }
116
117 pub inline fn sub(a: Fe, b: Fe) Fe {
118 var fe = b;
119 comptime var i = 0;
120 inline while (i < 4) : (i += 1) {
121 fe.limbs[i + 1] += fe.limbs[i] >> 51;
122 fe.limbs[i] &= MASK51;
123 }
124 fe.limbs[0] += 19 * (fe.limbs[4] >> 51);
125 fe.limbs[4] &= MASK51;
126 fe.limbs[0] = (a.limbs[0] + 0xfffffffffffda) - fe.limbs[0];
127 fe.limbs[1] = (a.limbs[1] + 0xffffffffffffe) - fe.limbs[1];
128 fe.limbs[2] = (a.limbs[2] + 0xffffffffffffe) - fe.limbs[2];
129 fe.limbs[3] = (a.limbs[3] + 0xffffffffffffe) - fe.limbs[3];
130 fe.limbs[4] = (a.limbs[4] + 0xffffffffffffe) - fe.limbs[4];
131
132 return fe;
133 }
134
135 pub inline fn neg(a: Fe) Fe {
136 return zero.sub(a);
137 }
138
139 pub inline fn isNegative(a: Fe) bool {
140 return (a.toBytes()[0] & 1) != 0;
141 }
142
143 pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void {
144 const mask: u64 = 0 -% c;
145 var x = fe.*;
146 comptime var i = 0;
147 inline while (i < 5) : (i += 1) {
148 x.limbs[i] ^= a.limbs[i];
149 }
150 i = 0;
151 inline while (i < 5) : (i += 1) {
152 x.limbs[i] &= mask;
153 }
154 i = 0;
155 inline while (i < 5) : (i += 1) {
156 fe.limbs[i] ^= x.limbs[i];
157 }
158 }
159
160 pub fn cSwap2(a0: *Fe, b0: *Fe, a1: *Fe, b1: *Fe, c: u64) void {
161 const mask: u64 = 0 -% c;
162 var x0 = a0.*;
163 var x1 = a1.*;
164 comptime var i = 0;
165 inline while (i < 5) : (i += 1) {
166 x0.limbs[i] ^= b0.limbs[i];
167 x1.limbs[i] ^= b1.limbs[i];
168 }
169 i = 0;
170 inline while (i < 5) : (i += 1) {
171 x0.limbs[i] &= mask;
172 x1.limbs[i] &= mask;
173 }
174 i = 0;
175 inline while (i < 5) : (i += 1) {
176 a0.limbs[i] ^= x0.limbs[i];
177 b0.limbs[i] ^= x0.limbs[i];
178 a1.limbs[i] ^= x1.limbs[i];
179 b1.limbs[i] ^= x1.limbs[i];
180 }
181 }
182
183 inline fn _carry128(r: *[5]u128) Fe {
184 var rs: [5]u64 = undefined;
185 comptime var i = 0;
186 inline while (i < 4) : (i += 1) {
187 rs[i] = @truncate(u64, r[i]) & MASK51;
188 r[i + 1] += @intCast(u64, r[i] >> 51);
189 }
190 rs[4] = @truncate(u64, r[4]) & MASK51;
191 var carry = @intCast(u64, r[4] >> 51);
192 rs[0] += 19 * carry;
193 carry = rs[0] >> 51;
194 rs[0] &= MASK51;
195 rs[1] += carry;
196 carry = rs[1] >> 51;
197 rs[1] &= MASK51;
198 rs[2] += carry;
199
200 return .{ .limbs = rs };
201 }
202
203 pub inline fn mul(a: Fe, b: Fe) Fe {
204 var ax: [5]u128 = undefined;
205 var bx: [5]u128 = undefined;
206 var a19: [5]u128 = undefined;
207 var r: [5]u128 = undefined;
208 comptime var i = 0;
209 inline while (i < 5) : (i += 1) {
210 ax[i] = @intCast(u128, a.limbs[i]);
211 bx[i] = @intCast(u128, b.limbs[i]);
212 }
213 i = 1;
214 inline while (i < 5) : (i += 1) {
215 a19[i] = 19 * ax[i];
216 }
217 r[0] = ax[0] * bx[0] + a19[1] * bx[4] + a19[2] * bx[3] + a19[3] * bx[2] + a19[4] * bx[1];
218 r[1] = ax[0] * bx[1] + ax[1] * bx[0] + a19[2] * bx[4] + a19[3] * bx[3] + a19[4] * bx[2];
219 r[2] = ax[0] * bx[2] + ax[1] * bx[1] + ax[2] * bx[0] + a19[3] * bx[4] + a19[4] * bx[3];
220 r[3] = ax[0] * bx[3] + ax[1] * bx[2] + ax[2] * bx[1] + ax[3] * bx[0] + a19[4] * bx[4];
221 r[4] = ax[0] * bx[4] + ax[1] * bx[3] + ax[2] * bx[2] + ax[3] * bx[1] + ax[4] * bx[0];
222
223 return _carry128(&r);
224 }
225
226 inline fn _sq(a: Fe, double: comptime bool) Fe {
227 var ax: [5]u128 = undefined;
228 var r: [5]u128 = undefined;
229 comptime var i = 0;
230 inline while (i < 5) : (i += 1) {
231 ax[i] = @intCast(u128, a.limbs[i]);
232 }
233 const a0_2 = 2 * ax[0];
234 const a1_2 = 2 * ax[1];
235 const a1_38 = 38 * ax[1];
236 const a2_38 = 38 * ax[2];
237 const a3_38 = 38 * ax[3];
238 const a3_19 = 19 * ax[3];
239 const a4_19 = 19 * ax[4];
240 r[0] = ax[0] * ax[0] + a1_38 * ax[4] + a2_38 * ax[3];
241 r[1] = a0_2 * ax[1] + a2_38 * ax[4] + a3_19 * ax[3];
242 r[2] = a0_2 * ax[2] + ax[1] * ax[1] + a3_38 * ax[4];
243 r[3] = a0_2 * ax[3] + a1_2 * ax[2] + a4_19 * ax[4];
244 r[4] = a0_2 * ax[4] + a1_2 * ax[3] + ax[2] * ax[2];
245 if (double) {
246 i = 0;
247 inline while (i < 5) : (i += 1) {
248 r[i] *= 2;
249 }
250 }
251 return _carry128(&r);
252 }
253
254 pub inline fn sq(a: Fe) Fe {
255 return _sq(a, false);
256 }
257
258 pub inline fn sq2(a: Fe) Fe {
259 return _sq(a, true);
260 }
261
262 pub inline fn mul32(a: Fe, comptime n: u32) Fe {
263 const sn = @intCast(u128, n);
264 var fe: Fe = undefined;
265 var x: u128 = 0;
266 comptime var i = 0;
267 inline while (i < 5) : (i += 1) {
268 x = a.limbs[i] * sn + (x >> 51);
269 fe.limbs[i] = @truncate(u64, x) & MASK51;
270 }
271 fe.limbs[0] += @intCast(u64, x >> 51) * 19;
272
273 return fe;
274 }
275
276 inline fn sqn(a: Fe, comptime n: comptime_int) Fe {
277 var i: usize = 0;
278 var fe = a;
279 while (i < n) : (i += 1) {
280 fe = fe.sq();
281 }
282 return fe;
283 }
284
285 pub fn invert(a: Fe) Fe {
286 var t0 = a.sq();
287 var t1 = t0.sqn(2).mul(a);
288 t0 = t0.mul(t1);
289 t1 = t1.mul(t0.sq());
290 t1 = t1.mul(t1.sqn(5));
291 var t2 = t1.sqn(10).mul(t1);
292 t2 = t2.mul(t2.sqn(20)).sqn(10);
293 t1 = t1.mul(t2);
294 t2 = t1.sqn(50).mul(t1);
295 return t1.mul(t2.mul(t2.sqn(100)).sqn(50)).sqn(5).mul(t0);
296 }
297
298 pub fn pow2523(a: Fe) Fe {
299 var c = a;
300 var i: usize = 0;
301 while (i < 249) : (i += 1) {
302 c = c.sq().mul(a);
303 }
304 return c.sq().sq().mul(a);
305 }
306
307 pub fn abs(a: Fe) Fe {
308 var r = a;
309 r.cMov(a.neg(), @boolToInt(a.isNegative()));
310 return r;
311 }
312};
lib/std/crypto/25519/ristretto255.zig created+144
......@@ -0,0 +1,144 @@
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,
14
15 fn sqrtRatioM1(u: Fe, v: Fe) !Fe {
16 const v3 = v.sq().mul(v); // v^3
17 var x = v3.sq().mul(u).mul(v).pow2523().mul(v3).mul(u); // uv^3(uv^7)^((q-5)/8)
18 const vxx = x.sq().mul(v); // vx^2
19 const m_root_check = vxx.sub(u); // vx^2-u
20 const p_root_check = vxx.add(u); // vx^2+u
21 const f_root_check = u.mul(Fe.sqrtm1).add(vxx); // vx^2+u*sqrt(-1)
22 const has_m_root = m_root_check.isZero();
23 const has_p_root = p_root_check.isZero();
24 const has_f_root = f_root_check.isZero();
25 const x_sqrtm1 = x.mul(Fe.sqrtm1); // x*sqrt(-1)
26 x.cMov(x_sqrtm1, @boolToInt(has_p_root) | @boolToInt(has_f_root));
27 const xa = x.abs();
28 if ((@boolToInt(has_m_root) | @boolToInt(has_p_root)) == 0) {
29 return error.NoRoot;
30 }
31 return xa;
32 }
33
34 fn rejectNonCanonical(s: [32]u8) !void {
35 if ((s[0] & 1) != 0) {
36 return error.NonCanonical;
37 }
38 try Fe.rejectNonCanonical(s, false);
39 }
40
41 /// Reject the neutral element.
42 pub inline fn rejectIdentity(p: Ristretto255) !void {
43 return p.p.rejectIdentity();
44 }
45
46 /// The base point (Ristretto is a curve in desguise).
47 pub const basePoint = Ristretto255{ .p = Curve.basePoint };
48
49 /// Decode a Ristretto255 representative.
50 pub fn fromBytes(s: [32]u8) !Ristretto255 {
51 try rejectNonCanonical(s);
52 const s_ = Fe.fromBytes(s);
53 const ss = s_.sq(); // s^2
54 const u1_ = Fe.one.sub(ss); // (1-s^2)
55 const u1u1 = u1_.sq(); // (1-s^2)^2
56 const u2_ = Fe.one.add(ss); // (1+s^2)
57 const u2u2 = u2_.sq(); // (1+s^2)^2
58 const v = Fe.edwards25519d.mul(u1u1).neg().sub(u2u2); // -(d*u1^2)-u2^2
59 const v_u2u2 = v.mul(u2u2); // v*u2^2
60 const inv_sqrt = sqrtRatioM1(Fe.one, v_u2u2) catch |e| {
61 return error.InvalidEncoding;
62 };
63 var x = inv_sqrt.mul(u2_);
64 const y = inv_sqrt.mul(x).mul(v).mul(u1_);
65 x = x.mul(s_);
66 x = x.add(x).abs();
67 const t = x.mul(y);
68 if ((@boolToInt(t.isNegative()) | @boolToInt(y.isZero())) != 0) {
69 return error.InvalidEncoding;
70 }
71 const p: Curve = .{
72 .x = x,
73 .y = y,
74 .z = Fe.one,
75 .t = t,
76 };
77 return Ristretto255{ .p = p };
78 }
79
80 /// Encode to a Ristretto255 representative.
81 pub fn toBytes(e: Ristretto255) [32]u8 {
82 const p = &e.p;
83 var u1_ = p.z.add(p.y); // Z+Y
84 const zmy = p.z.sub(p.y); // Z-Y
85 u1_ = u1_.mul(zmy); // (Z+Y)*(Z-Y)
86 const u2_ = p.x.mul(p.y); // X*Y
87 const u1_u2u2 = u2_.sq().mul(u1_); // u1*u2^2
88 const inv_sqrt = sqrtRatioM1(Fe.one, u1_u2u2) catch unreachable;
89 const den1 = inv_sqrt.mul(u1_);
90 const den2 = inv_sqrt.mul(u2_);
91 const z_inv = den1.mul(den2).mul(p.t); // den1*den2*T
92 const ix = p.x.mul(Fe.sqrtm1); // X*sqrt(-1)
93 const iy = p.y.mul(Fe.sqrtm1); // Y*sqrt(-1)
94 const eden = den1.mul(Fe.edwards25519sqrtamd); // den1/sqrt(a-d)
95 const t_z_inv = p.t.mul(z_inv); // T*z_inv
96
97 const rotate = @boolToInt(t_z_inv.isNegative());
98 var x = p.x;
99 var y = p.y;
100 var den_inv = den2;
101 x.cMov(iy, rotate);
102 y.cMov(ix, rotate);
103 den_inv.cMov(eden, rotate);
104
105 const x_z_inv = x.mul(z_inv);
106 const yneg = y.neg();
107 y.cMov(yneg, @boolToInt(x_z_inv.isNegative()));
108
109 return p.z.sub(y).mul(den_inv).abs().toBytes();
110 }
111
112 /// Double a Ristretto255 element.
113 pub inline fn dbl(p: Ristretto255) Ristretto255 {
114 return .{ .p = p.p.dbl() };
115 }
116
117 /// Add two Ristretto255 elements.
118 pub inline fn add(p: Ristretto255, q: Ristretto255) Ristretto255 {
119 return .{ .p = p.p.add(q.p) };
120 }
121
122 /// Multiply a Ristretto255 element with a scalar.
123 /// Return error.WeakPublicKey if the resulting element is
124 /// the identity element.
125 pub inline fn mul(p: Ristretto255, s: [32]u8) !Ristretto255 {
126 return Ristretto255{ .p = try p.p.mul(s) };
127 }
128};
129
130test "ristretto255" {
131 const p = Ristretto255.basePoint;
132 var buf: [256]u8 = undefined;
133 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76");
134
135 var r: [32]u8 = undefined;
136 try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919");
137 var q = try Ristretto255.fromBytes(r);
138 q = q.dbl().add(p);
139 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E");
140
141 const s = [_]u8{15} ++ [_]u8{0} ** 31;
142 const w = try p.mul(s);
143 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{w.toBytes()}), "E0C418F7C8D9C4CDD7395B93EA124F3AD99021BB681DFC3302A9D99A2E53E64E");
144}
lib/std/crypto/25519/scalar.zig created+177
......@@ -0,0 +1,177 @@
1const std = @import("std");
2const mem = std.mem;
3
4const field_size = [32]u8{
5 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
6};
7
8const ScalarExpanded = struct {
9 limbs: [64]i64 = [_]i64{0} ** 64,
10
11 fn fromBytes(s: [32]u8) ScalarExpanded {
12 var limbs: [64]i64 = undefined;
13 for (s) |x, idx| {
14 limbs[idx] = @as(i64, x);
15 }
16 mem.set(i64, limbs[32..], 0);
17 return .{ .limbs = limbs };
18 }
19
20 fn fromBytes64(s: [64]u8) ScalarExpanded {
21 var limbs: [64]i64 = undefined;
22 for (s) |x, idx| {
23 limbs[idx] = @as(i64, x);
24 }
25 return .{ .limbs = limbs };
26 }
27
28 fn reduce(e: *ScalarExpanded) void {
29 const limbs = &e.limbs;
30 var carry: i64 = undefined;
31 var i: usize = 63;
32 while (i >= 32) : (i -= 1) {
33 carry = 0;
34 const k = i - 12;
35 const xi = limbs[i];
36 var j = i - 32;
37 while (j < k) : (j += 1) {
38 const xj = limbs[j] + carry - 16 * xi * @as(i64, field_size[j - (i - 32)]);
39 carry = (xj + 128) >> 8;
40 limbs[j] = xj - carry * 256;
41 }
42 limbs[k] += carry;
43 limbs[i] = 0;
44 }
45 carry = 0;
46 comptime var j: usize = 0;
47 inline while (j < 32) : (j += 1) {
48 const xi = limbs[j] + carry - (limbs[31] >> 4) * @as(i64, field_size[j]);
49 carry = xi >> 8;
50 limbs[j] = xi & 255;
51 }
52 j = 0;
53 inline while (j < 32) : (j += 1) {
54 limbs[j] -= carry * @as(i64, field_size[j]);
55 }
56 j = 0;
57 inline while (j < 32) : (j += 1) {
58 limbs[j + 1] += limbs[j] >> 8;
59 }
60 }
61
62 fn toBytes(e: *ScalarExpanded) [32]u8 {
63 e.reduce();
64 var r: [32]u8 = undefined;
65 var i: usize = 0;
66 while (i < 32) : (i += 1) {
67 r[i] = @intCast(u8, e.limbs[i]);
68 }
69 return r;
70 }
71
72 fn add(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
73 var r = ScalarExpanded{};
74 comptime var i = 0;
75 inline while (i < 64) : (i += 1) {
76 r.limbs[i] = a.limbs[i] + b.limbs[i];
77 }
78 return r;
79 }
80
81 fn mul(a: ScalarExpanded, b: ScalarExpanded) ScalarExpanded {
82 var r = ScalarExpanded{};
83 var i: usize = 0;
84 while (i < 32) : (i += 1) {
85 const ai = a.limbs[i];
86 comptime var j = 0;
87 inline while (j < 32) : (j += 1) {
88 r.limbs[i + j] += ai * b.limbs[j];
89 }
90 }
91 r.reduce();
92 return r;
93 }
94
95 fn sq(a: ScalarExpanded) ScalarExpanded {
96 return a.mul(a);
97 }
98
99 fn mulAdd(a: ScalarExpanded, b: ScalarExpanded, c: ScalarExpanded) ScalarExpanded {
100 var r: ScalarExpanded = .{ .limbs = c.limbs };
101 var i: usize = 0;
102 while (i < 32) : (i += 1) {
103 const ai = a.limbs[i];
104 comptime var j = 0;
105 inline while (j < 32) : (j += 1) {
106 r.limbs[i + j] += ai * b.limbs[j];
107 }
108 }
109 r.reduce();
110 return r;
111 }
112};
113
114/// Reject a scalar whose encoding is not canonical.
115pub fn rejectNonCanonical(s: [32]u8) !void {
116 var c: u8 = 0;
117 var n: u8 = 1;
118 var i: usize = 31;
119 while (true) : (i -= 1) {
120 const xs = @as(u16, s[i]);
121 const xfield_size = @as(u16, field_size[i]);
122 c |= @intCast(u8, ((xs -% xfield_size) >> 8) & n);
123 n &= @intCast(u8, ((xs ^ xfield_size) -% 1) >> 8);
124 if (i == 0) break;
125 }
126 if (c == 0) {
127 return error.NonCanonical;
128 }
129}
130
131/// Reduce a scalar to the field size.
132pub fn reduce(s: [32]u8) [32]u8 {
133 return ScalarExpanded.fromBytes(s).toBytes();
134}
135
136/// Reduce a 64-bytes scalar to the field size.
137pub fn reduce64(s: [64]u8) [32]u8 {
138 return ScalarExpanded.fromBytes64(s).toBytes();
139}
140
141/// Perform the X25519 "clamping" operation.
142/// The scalar is then guaranteed to be a multiple of the cofactor.
143pub inline fn clamp(s: *[32]u8) void {
144 s[0] &= 248;
145 s[31] = (s[31] & 127) | 64;
146}
147
148/// Return a*b+c (mod L)
149pub fn mulAdd(a: [32]u8, b: [32]u8, c: [32]u8) [32]u8 {
150 return ScalarExpanded.fromBytes(a).mulAdd(ScalarExpanded.fromBytes(b), ScalarExpanded.fromBytes(c)).toBytes();
151}
152
153test "scalar25519" {
154 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 };
155 var x = ScalarExpanded.fromBytes(bytes);
156 var y = x.toBytes();
157 try rejectNonCanonical(y);
158 var buf: [128]u8 = undefined;
159 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{y}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F");
160
161 const reduced = reduce(field_size);
162 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{reduced}), "0000000000000000000000000000000000000000000000000000000000000000");
163}
164
165test "non-canonical scalar25519" {
166 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 };
167 std.testing.expectError(error.NonCanonical, rejectNonCanonical(too_targe));
168}
169
170test "mulAdd overflow check" {
171 const a: [32]u8 = [_]u8{0xff} ** 32;
172 const b: [32]u8 = [_]u8{0xff} ** 32;
173 const c: [32]u8 = [_]u8{0xff} ** 32;
174 const x = mulAdd(a, b, c);
175 var buf: [128]u8 = undefined;
176 std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{x}), "D14DF91389432C25AD60FF9791B9FD1D67BEF517D273ECCE3D9A307C1B419903");
177}
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.expectEqual(pk_calculated, pk_expected);
56}
57
58test "x25519 rfc7748 vector1" {
59 const secret_key = [32]u8{ 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 0x9a, 0xc4 };
60 const public_key = [32]u8{ 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c };
61
62 const expected_output = [32]u8{ 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 };
63
64 var output: [32]u8 = undefined;
65
66 std.testing.expect(X25519.create(output[0..], secret_key[0..], public_key[0..]));
67 std.testing.expectEqual(output, expected_output);
68}
69
70test "x25519 rfc7748 vector2" {
71 const secret_key = [32]u8{ 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 0xba, 0x0d };
72 const public_key = [32]u8{ 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93 };
73
74 const expected_output = [32]u8{ 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57 };
75
76 var output: [32]u8 = undefined;
77
78 std.testing.expect(X25519.create(output[0..], secret_key[0..], public_key[0..]));
79 std.testing.expectEqual(output, expected_output);
80}
81
82test "x25519 rfc7748 one iteration" {
83 const initial_value = [32]u8{ 0x09, 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 };
84 const expected_output = [32]u8{ 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, 0x11, 0xae, 0x30, 0x79 };
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 mem.copy(u8, u[0..], k[0..]);
95 mem.copy(u8, k[0..], output[0..]);
96 }
97
98 std.testing.expectEqual(k, 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 = [32]u8{ 0x09, 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 };
108 const expected_output = [32]u8{ 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, 0x99, 0x53, 0x2c, 0x51 };
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 mem.copy(u8, u[0..], k[0..]);
119 mem.copy(u8, k[0..], output[0..]);
120 }
121
122 std.testing.expectEqual(k, expected_output);
123}
124
125test "x25519 rfc7748 1,000,000 iterations" {
126 if (true) {
127 return error.SkipZigTest;
128 }
129
130 const initial_value = [32]u8{ 0x09, 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 };
131 const expected_output = [32]u8{ 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, 0x4f, 0x66, 0x54, 0x24 };
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 mem.copy(u8, u[0..], k[0..]);
142 mem.copy(u8, k[0..], output[0..]);
143 }
144
145 std.testing.expectEqual(k[0..], expected_output);
146}
lib/std/crypto/benchmark.zig+31-1
......@@ -90,7 +90,6 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
9090 var out: [DhKeyExchange.minimum_key_length]u8 = undefined;
9191 prng.random.bytes(out[0..]);
9292
93 var offset: usize = 0;
9493 var timer = try Timer.start();
9594 const start = timer.lap();
9695 {
......@@ -107,6 +106,30 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
107106 return throughput;
108107}
109108
109const signatures = [_]Crypto{Crypto{ .ty = crypto.Ed25519, .name = "ed25519" }};
110
111pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
112 var seed: [Signature.seed_length]u8 = undefined;
113 prng.random.bytes(seed[0..]);
114 const msg = [_]u8{0} ** 64;
115 const key_pair = try Signature.createKeyPair(seed);
116
117 var timer = try Timer.start();
118 const start = timer.lap();
119 {
120 var i: usize = 0;
121 while (i < signatures_count) : (i += 1) {
122 _ = try Signature.sign(&msg, key_pair, null);
123 }
124 }
125 const end = timer.read();
126
127 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
128 const throughput = @floatToInt(u64, signatures_count / elapsed_s);
129
130 return throughput;
131}
132
110133fn usage() void {
111134 std.debug.warn(
112135 \\throughput_test [options]
......@@ -183,4 +206,11 @@ pub fn main() !void {
183206 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
184207 }
185208 }
209
210 inline for (signatures) |E| {
211 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
212 const throughput = try benchmarkSignatures(E.ty, mode(1000));
213 try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });
214 }
215 }
186216}
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}