authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2022-11-06 23:52:41+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 13:51:56-07:00
log30d392a87e7ebd51269a557b6bccbc3163e8db75
tree89d6b99524223fb3938c719e3cd587a88292aa7d
parent8d488246da1665f147dcc00b5e6d253cfc3598eb

crypto.salsa20: make the number of rounds a comptime parameter (#13442)

...instead of hard-coding it to 20. - This is consistent with the ChaCha implementation - NaCl and libsodium, that this API is designed to interop with, also support 8 and 12 round variants. The 12 round variant, in particular, provides the same security level as the 20 round variant, but is obviously faster. - scrypt currently uses its own non optimized version of Salsa, just because it use 8 rounds instead of 20. This will help remove code duplication. No behavior nor public API changes. The Salsa20 and XSalsa20 still represent the 20-round variant.

2 files changed, 304 insertions(+), 300 deletions(-)

lib/std/crypto.zig+2
......@@ -147,6 +147,8 @@ pub const stream = struct {
147147 };
148148
149149 pub const salsa = struct {
150 pub const Salsa = @import("crypto/salsa20.zig").Salsa;
151 pub const XSalsa = @import("crypto/salsa20.zig").XSalsa;
150152 pub const Salsa20 = @import("crypto/salsa20.zig").Salsa20;
151153 pub const XSalsa20 = @import("crypto/salsa20.zig").XSalsa20;
152154 };
lib/std/crypto/salsa20.zig+302-300
......@@ -14,297 +14,293 @@ const AuthenticationError = crypto.errors.AuthenticationError;
1414const IdentityElementError = crypto.errors.IdentityElementError;
1515const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
1616
17const Salsa20VecImpl = struct {
18 const Lane = @Vector(4, u32);
19 const Half = @Vector(2, u32);
20 const BlockVec = [4]Lane;
21
22 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
23 const c = "expand 32-byte k";
24 const constant_le = comptime [4]u32{
25 mem.readIntLittle(u32, c[0..4]),
26 mem.readIntLittle(u32, c[4..8]),
27 mem.readIntLittle(u32, c[8..12]),
28 mem.readIntLittle(u32, c[12..16]),
29 };
30 return BlockVec{
31 Lane{ key[0], key[1], key[2], key[3] },
32 Lane{ key[4], key[5], key[6], key[7] },
33 Lane{ constant_le[0], constant_le[1], constant_le[2], constant_le[3] },
34 Lane{ d[0], d[1], d[2], d[3] },
35 };
36 }
37
38 inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
39 const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] };
40 const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] };
41 const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] };
42 const k0k1 = Half{ input[0][0], input[0][1] };
43 const k2k3 = Half{ input[0][2], input[0][3] };
44 const k4k5 = Half{ input[1][0], input[1][1] };
45 const k6k7 = Half{ input[1][2], input[1][3] };
46 const n0k0 = Half{ n3n0[1], k0k1[0] };
47 const k0n0 = Half{ n0k0[1], n0k0[0] };
48 const k4k5k0n0 = Lane{ k4k5[0], k4k5[1], k0n0[0], k0n0[1] };
49 const k1k6 = Half{ k0k1[1], k6k7[0] };
50 const k6k1 = Half{ k1k6[1], k1k6[0] };
51 const n1n2k6k1 = Lane{ n1n2[0], n1n2[1], k6k1[0], k6k1[1] };
52 const k7n3 = Half{ k6k7[1], n3n0[0] };
53 const n3k7 = Half{ k7n3[1], k7n3[0] };
54 const k2k3n3k7 = Lane{ k2k3[0], k2k3[1], n3k7[0], n3k7[1] };
55
56 var diag0 = input[2];
57 var diag1 = @shuffle(u32, k4k5k0n0, undefined, [_]i32{ 1, 2, 3, 0 });
58 var diag2 = @shuffle(u32, n1n2k6k1, undefined, [_]i32{ 1, 2, 3, 0 });
59 var diag3 = @shuffle(u32, k2k3n3k7, undefined, [_]i32{ 1, 2, 3, 0 });
60
61 const start0 = diag0;
62 const start1 = diag1;
63 const start2 = diag2;
64 const start3 = diag3;
65
66 var i: usize = 0;
67 while (i < 20) : (i += 2) {
68 var a0 = diag1 +% diag0;
69 diag3 ^= math.rotl(Lane, a0, 7);
70 var a1 = diag0 +% diag3;
71 diag2 ^= math.rotl(Lane, a1, 9);
72 var a2 = diag3 +% diag2;
73 diag1 ^= math.rotl(Lane, a2, 13);
74 var a3 = diag2 +% diag1;
75 diag0 ^= math.rotl(Lane, a3, 18);
76
77 var diag3_shift = @shuffle(u32, diag3, undefined, [_]i32{ 3, 0, 1, 2 });
78 var diag2_shift = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 });
79 var diag1_shift = @shuffle(u32, diag1, undefined, [_]i32{ 1, 2, 3, 0 });
80 diag3 = diag3_shift;
81 diag2 = diag2_shift;
82 diag1 = diag1_shift;
83
84 a0 = diag3 +% diag0;
85 diag1 ^= math.rotl(Lane, a0, 7);
86 a1 = diag0 +% diag1;
87 diag2 ^= math.rotl(Lane, a1, 9);
88 a2 = diag1 +% diag2;
89 diag3 ^= math.rotl(Lane, a2, 13);
90 a3 = diag2 +% diag3;
91 diag0 ^= math.rotl(Lane, a3, 18);
92
93 diag1_shift = @shuffle(u32, diag1, undefined, [_]i32{ 3, 0, 1, 2 });
94 diag2_shift = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 });
95 diag3_shift = @shuffle(u32, diag3, undefined, [_]i32{ 1, 2, 3, 0 });
96 diag1 = diag1_shift;
97 diag2 = diag2_shift;
98 diag3 = diag3_shift;
17/// The Salsa cipher with 20 rounds.
18pub const Salsa20 = Salsa(20);
19
20/// The XSalsa cipher with 20 rounds.
21pub const XSalsa20 = XSalsa(20);
22
23fn SalsaVecImpl(comptime rounds: comptime_int) type {
24 return struct {
25 const Lane = @Vector(4, u32);
26 const Half = @Vector(2, u32);
27 const BlockVec = [4]Lane;
28
29 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
30 const c = "expand 32-byte k";
31 const constant_le = comptime [4]u32{
32 mem.readIntLittle(u32, c[0..4]),
33 mem.readIntLittle(u32, c[4..8]),
34 mem.readIntLittle(u32, c[8..12]),
35 mem.readIntLittle(u32, c[12..16]),
36 };
37 return BlockVec{
38 Lane{ key[0], key[1], key[2], key[3] },
39 Lane{ key[4], key[5], key[6], key[7] },
40 Lane{ constant_le[0], constant_le[1], constant_le[2], constant_le[3] },
41 Lane{ d[0], d[1], d[2], d[3] },
42 };
9943 }
10044
101 if (feedback) {
102 diag0 +%= start0;
103 diag1 +%= start1;
104 diag2 +%= start2;
105 diag3 +%= start3;
106 }
45 inline fn salsaCore(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
46 const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] };
47 const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] };
48 const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] };
49 const k0k1 = Half{ input[0][0], input[0][1] };
50 const k2k3 = Half{ input[0][2], input[0][3] };
51 const k4k5 = Half{ input[1][0], input[1][1] };
52 const k6k7 = Half{ input[1][2], input[1][3] };
53 const n0k0 = Half{ n3n0[1], k0k1[0] };
54 const k0n0 = Half{ n0k0[1], n0k0[0] };
55 const k4k5k0n0 = Lane{ k4k5[0], k4k5[1], k0n0[0], k0n0[1] };
56 const k1k6 = Half{ k0k1[1], k6k7[0] };
57 const k6k1 = Half{ k1k6[1], k1k6[0] };
58 const n1n2k6k1 = Lane{ n1n2[0], n1n2[1], k6k1[0], k6k1[1] };
59 const k7n3 = Half{ k6k7[1], n3n0[0] };
60 const n3k7 = Half{ k7n3[1], k7n3[0] };
61 const k2k3n3k7 = Lane{ k2k3[0], k2k3[1], n3k7[0], n3k7[1] };
62
63 var diag0 = input[2];
64 var diag1 = @shuffle(u32, k4k5k0n0, undefined, [_]i32{ 1, 2, 3, 0 });
65 var diag2 = @shuffle(u32, n1n2k6k1, undefined, [_]i32{ 1, 2, 3, 0 });
66 var diag3 = @shuffle(u32, k2k3n3k7, undefined, [_]i32{ 1, 2, 3, 0 });
67
68 const start0 = diag0;
69 const start1 = diag1;
70 const start2 = diag2;
71 const start3 = diag3;
72
73 var i: usize = 0;
74 while (i < rounds) : (i += 2) {
75 diag3 ^= math.rotl(Lane, diag1 +% diag0, 7);
76 diag2 ^= math.rotl(Lane, diag0 +% diag3, 9);
77 diag1 ^= math.rotl(Lane, diag3 +% diag2, 13);
78 diag0 ^= math.rotl(Lane, diag2 +% diag1, 18);
79
80 diag3 = @shuffle(u32, diag3, undefined, [_]i32{ 3, 0, 1, 2 });
81 diag2 = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 });
82 diag1 = @shuffle(u32, diag1, undefined, [_]i32{ 1, 2, 3, 0 });
83
84 diag1 ^= math.rotl(Lane, diag3 +% diag0, 7);
85 diag2 ^= math.rotl(Lane, diag0 +% diag1, 9);
86 diag3 ^= math.rotl(Lane, diag1 +% diag2, 13);
87 diag0 ^= math.rotl(Lane, diag2 +% diag3, 18);
88
89 diag1 = @shuffle(u32, diag1, undefined, [_]i32{ 3, 0, 1, 2 });
90 diag2 = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 });
91 diag3 = @shuffle(u32, diag3, undefined, [_]i32{ 1, 2, 3, 0 });
92 }
10793
108 const x0x1x10x11 = Lane{ diag0[0], diag1[1], diag0[2], diag1[3] };
109 const x12x13x6x7 = Lane{ diag1[0], diag2[1], diag1[2], diag2[3] };
110 const x8x9x2x3 = Lane{ diag2[0], diag3[1], diag2[2], diag3[3] };
111 const x4x5x14x15 = Lane{ diag3[0], diag0[1], diag3[2], diag0[3] };
94 if (feedback) {
95 diag0 +%= start0;
96 diag1 +%= start1;
97 diag2 +%= start2;
98 diag3 +%= start3;
99 }
112100
113 x[0] = Lane{ x0x1x10x11[0], x0x1x10x11[1], x8x9x2x3[2], x8x9x2x3[3] };
114 x[1] = Lane{ x4x5x14x15[0], x4x5x14x15[1], x12x13x6x7[2], x12x13x6x7[3] };
115 x[2] = Lane{ x8x9x2x3[0], x8x9x2x3[1], x0x1x10x11[2], x0x1x10x11[3] };
116 x[3] = Lane{ x12x13x6x7[0], x12x13x6x7[1], x4x5x14x15[2], x4x5x14x15[3] };
117 }
101 const x0x1x10x11 = Lane{ diag0[0], diag1[1], diag0[2], diag1[3] };
102 const x12x13x6x7 = Lane{ diag1[0], diag2[1], diag1[2], diag2[3] };
103 const x8x9x2x3 = Lane{ diag2[0], diag3[1], diag2[2], diag3[3] };
104 const x4x5x14x15 = Lane{ diag3[0], diag0[1], diag3[2], diag0[3] };
118105
119 fn hashToBytes(out: *[64]u8, x: BlockVec) void {
120 var i: usize = 0;
121 while (i < 4) : (i += 1) {
122 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
123 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);
124 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);
125 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);
106 x[0] = Lane{ x0x1x10x11[0], x0x1x10x11[1], x8x9x2x3[2], x8x9x2x3[3] };
107 x[1] = Lane{ x4x5x14x15[0], x4x5x14x15[1], x12x13x6x7[2], x12x13x6x7[3] };
108 x[2] = Lane{ x8x9x2x3[0], x8x9x2x3[1], x0x1x10x11[2], x0x1x10x11[3] };
109 x[3] = Lane{ x12x13x6x7[0], x12x13x6x7[1], x4x5x14x15[2], x4x5x14x15[3] };
126110 }
127 }
128111
129 fn salsa20Xor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void {
130 var ctx = initContext(key, d);
131 var x: BlockVec = undefined;
132 var buf: [64]u8 = undefined;
133 var i: usize = 0;
134 while (i + 64 <= in.len) : (i += 64) {
135 salsa20Core(x[0..], ctx, true);
136 hashToBytes(buf[0..], x);
137 var xout = out[i..];
138 const xin = in[i..];
139 var j: usize = 0;
140 while (j < 64) : (j += 1) {
141 xout[j] = xin[j];
112 fn hashToBytes(out: *[64]u8, x: BlockVec) void {
113 var i: usize = 0;
114 while (i < 4) : (i += 1) {
115 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]);
116 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]);
117 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]);
118 mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]);
142119 }
143 j = 0;
144 while (j < 64) : (j += 1) {
145 xout[j] ^= buf[j];
120 }
121
122 fn salsaXor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void {
123 var ctx = initContext(key, d);
124 var x: BlockVec = undefined;
125 var buf: [64]u8 = undefined;
126 var i: usize = 0;
127 while (i + 64 <= in.len) : (i += 64) {
128 salsaCore(x[0..], ctx, true);
129 hashToBytes(buf[0..], x);
130 var xout = out[i..];
131 const xin = in[i..];
132 var j: usize = 0;
133 while (j < 64) : (j += 1) {
134 xout[j] = xin[j];
135 }
136 j = 0;
137 while (j < 64) : (j += 1) {
138 xout[j] ^= buf[j];
139 }
140 ctx[3][2] +%= 1;
141 if (ctx[3][2] == 0) {
142 ctx[3][3] += 1;
143 }
146144 }
147 ctx[3][2] +%= 1;
148 if (ctx[3][2] == 0) {
149 ctx[3][3] += 1;
145 if (i < in.len) {
146 salsaCore(x[0..], ctx, true);
147 hashToBytes(buf[0..], x);
148
149 var xout = out[i..];
150 const xin = in[i..];
151 var j: usize = 0;
152 while (j < in.len % 64) : (j += 1) {
153 xout[j] = xin[j] ^ buf[j];
154 }
150155 }
151156 }
152 if (i < in.len) {
153 salsa20Core(x[0..], ctx, true);
154 hashToBytes(buf[0..], x);
155157
156 var xout = out[i..];
157 const xin = in[i..];
158 var j: usize = 0;
159 while (j < in.len % 64) : (j += 1) {
160 xout[j] = xin[j] ^ buf[j];
158 fn hsalsa(input: [16]u8, key: [32]u8) [32]u8 {
159 var c: [4]u32 = undefined;
160 for (c) |_, i| {
161 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
161162 }
163 const ctx = initContext(keyToWords(key), c);
164 var x: BlockVec = undefined;
165 salsaCore(x[0..], ctx, false);
166 var out: [32]u8 = undefined;
167 mem.writeIntLittle(u32, out[0..4], x[0][0]);
168 mem.writeIntLittle(u32, out[4..8], x[1][1]);
169 mem.writeIntLittle(u32, out[8..12], x[2][2]);
170 mem.writeIntLittle(u32, out[12..16], x[3][3]);
171 mem.writeIntLittle(u32, out[16..20], x[1][2]);
172 mem.writeIntLittle(u32, out[20..24], x[1][3]);
173 mem.writeIntLittle(u32, out[24..28], x[2][0]);
174 mem.writeIntLittle(u32, out[28..32], x[2][1]);
175 return out;
162176 }
163 }
177 };
178}
164179
165 fn hsalsa20(input: [16]u8, key: [32]u8) [32]u8 {
166 var c: [4]u32 = undefined;
167 for (c) |_, i| {
168 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
180fn SalsaNonVecImpl(comptime rounds: comptime_int) type {
181 return struct {
182 const BlockVec = [16]u32;
183
184 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
185 const c = "expand 32-byte k";
186 const constant_le = comptime [4]u32{
187 mem.readIntLittle(u32, c[0..4]),
188 mem.readIntLittle(u32, c[4..8]),
189 mem.readIntLittle(u32, c[8..12]),
190 mem.readIntLittle(u32, c[12..16]),
191 };
192 return BlockVec{
193 constant_le[0], key[0], key[1], key[2],
194 key[3], constant_le[1], d[0], d[1],
195 d[2], d[3], constant_le[2], key[4],
196 key[5], key[6], key[7], constant_le[3],
197 };
169198 }
170 const ctx = initContext(keyToWords(key), c);
171 var x: BlockVec = undefined;
172 salsa20Core(x[0..], ctx, false);
173 var out: [32]u8 = undefined;
174 mem.writeIntLittle(u32, out[0..4], x[0][0]);
175 mem.writeIntLittle(u32, out[4..8], x[1][1]);
176 mem.writeIntLittle(u32, out[8..12], x[2][2]);
177 mem.writeIntLittle(u32, out[12..16], x[3][3]);
178 mem.writeIntLittle(u32, out[16..20], x[1][2]);
179 mem.writeIntLittle(u32, out[20..24], x[1][3]);
180 mem.writeIntLittle(u32, out[24..28], x[2][0]);
181 mem.writeIntLittle(u32, out[28..32], x[2][1]);
182 return out;
183 }
184};
185199
186const Salsa20NonVecImpl = struct {
187 const BlockVec = [16]u32;
188
189 fn initContext(key: [8]u32, d: [4]u32) BlockVec {
190 const c = "expand 32-byte k";
191 const constant_le = comptime [4]u32{
192 mem.readIntLittle(u32, c[0..4]),
193 mem.readIntLittle(u32, c[4..8]),
194 mem.readIntLittle(u32, c[8..12]),
195 mem.readIntLittle(u32, c[12..16]),
196 };
197 return BlockVec{
198 constant_le[0], key[0], key[1], key[2],
199 key[3], constant_le[1], d[0], d[1],
200 d[2], d[3], constant_le[2], key[4],
201 key[5], key[6], key[7], constant_le[3],
200 const QuarterRound = struct {
201 a: usize,
202 b: usize,
203 c: usize,
204 d: u6,
202205 };
203 }
204206
205 const QuarterRound = struct {
206 a: usize,
207 b: usize,
208 c: usize,
209 d: u6,
210 };
211
212 inline fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound {
213 return QuarterRound{
214 .a = a,
215 .b = b,
216 .c = c,
217 .d = d,
218 };
219 }
207 inline fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound {
208 return QuarterRound{
209 .a = a,
210 .b = b,
211 .c = c,
212 .d = d,
213 };
214 }
220215
221 inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
222 const arx_steps = comptime [_]QuarterRound{
223 Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18),
224 Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18),
225 Rp(14, 10, 6, 7), Rp(2, 14, 10, 9), Rp(6, 2, 14, 13), Rp(10, 6, 2, 18),
226 Rp(3, 15, 11, 7), Rp(7, 3, 15, 9), Rp(11, 7, 3, 13), Rp(15, 11, 7, 18),
227 Rp(1, 0, 3, 7), Rp(2, 1, 0, 9), Rp(3, 2, 1, 13), Rp(0, 3, 2, 18),
228 Rp(6, 5, 4, 7), Rp(7, 6, 5, 9), Rp(4, 7, 6, 13), Rp(5, 4, 7, 18),
229 Rp(11, 10, 9, 7), Rp(8, 11, 10, 9), Rp(9, 8, 11, 13), Rp(10, 9, 8, 18),
230 Rp(12, 15, 14, 7), Rp(13, 12, 15, 9), Rp(14, 13, 12, 13), Rp(15, 14, 13, 18),
231 };
232 x.* = input;
233 var j: usize = 0;
234 while (j < 20) : (j += 2) {
235 inline for (arx_steps) |r| {
236 x[r.a] ^= math.rotl(u32, x[r.b] +% x[r.c], r.d);
216 inline fn salsaCore(x: *BlockVec, input: BlockVec, comptime feedback: bool) void {
217 const arx_steps = comptime [_]QuarterRound{
218 Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18),
219 Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18),
220 Rp(14, 10, 6, 7), Rp(2, 14, 10, 9), Rp(6, 2, 14, 13), Rp(10, 6, 2, 18),
221 Rp(3, 15, 11, 7), Rp(7, 3, 15, 9), Rp(11, 7, 3, 13), Rp(15, 11, 7, 18),
222 Rp(1, 0, 3, 7), Rp(2, 1, 0, 9), Rp(3, 2, 1, 13), Rp(0, 3, 2, 18),
223 Rp(6, 5, 4, 7), Rp(7, 6, 5, 9), Rp(4, 7, 6, 13), Rp(5, 4, 7, 18),
224 Rp(11, 10, 9, 7), Rp(8, 11, 10, 9), Rp(9, 8, 11, 13), Rp(10, 9, 8, 18),
225 Rp(12, 15, 14, 7), Rp(13, 12, 15, 9), Rp(14, 13, 12, 13), Rp(15, 14, 13, 18),
226 };
227 x.* = input;
228 var j: usize = 0;
229 while (j < rounds) : (j += 2) {
230 inline for (arx_steps) |r| {
231 x[r.a] ^= math.rotl(u32, x[r.b] +% x[r.c], r.d);
232 }
237233 }
238 }
239 if (feedback) {
240 j = 0;
241 while (j < 16) : (j += 1) {
242 x[j] +%= input[j];
234 if (feedback) {
235 j = 0;
236 while (j < 16) : (j += 1) {
237 x[j] +%= input[j];
238 }
243239 }
244240 }
245 }
246241
247 fn hashToBytes(out: *[64]u8, x: BlockVec) void {
248 for (x) |w, i| {
249 mem.writeIntLittle(u32, out[i * 4 ..][0..4], w);
242 fn hashToBytes(out: *[64]u8, x: BlockVec) void {
243 for (x) |w, i| {
244 mem.writeIntLittle(u32, out[i * 4 ..][0..4], w);
245 }
250246 }
251 }
252247
253 fn salsa20Xor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void {
254 var ctx = initContext(key, d);
255 var x: BlockVec = undefined;
256 var buf: [64]u8 = undefined;
257 var i: usize = 0;
258 while (i + 64 <= in.len) : (i += 64) {
259 salsa20Core(x[0..], ctx, true);
260 hashToBytes(buf[0..], x);
261 var xout = out[i..];
262 const xin = in[i..];
263 var j: usize = 0;
264 while (j < 64) : (j += 1) {
265 xout[j] = xin[j];
248 fn salsaXor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void {
249 var ctx = initContext(key, d);
250 var x: BlockVec = undefined;
251 var buf: [64]u8 = undefined;
252 var i: usize = 0;
253 while (i + 64 <= in.len) : (i += 64) {
254 salsaCore(x[0..], ctx, true);
255 hashToBytes(buf[0..], x);
256 var xout = out[i..];
257 const xin = in[i..];
258 var j: usize = 0;
259 while (j < 64) : (j += 1) {
260 xout[j] = xin[j];
261 }
262 j = 0;
263 while (j < 64) : (j += 1) {
264 xout[j] ^= buf[j];
265 }
266 ctx[9] += @boolToInt(@addWithOverflow(u32, ctx[8], 1, &ctx[8]));
266267 }
267 j = 0;
268 while (j < 64) : (j += 1) {
269 xout[j] ^= buf[j];
268 if (i < in.len) {
269 salsaCore(x[0..], ctx, true);
270 hashToBytes(buf[0..], x);
271
272 var xout = out[i..];
273 const xin = in[i..];
274 var j: usize = 0;
275 while (j < in.len % 64) : (j += 1) {
276 xout[j] = xin[j] ^ buf[j];
277 }
270278 }
271 ctx[9] += @boolToInt(@addWithOverflow(u32, ctx[8], 1, &ctx[8]));
272279 }
273 if (i < in.len) {
274 salsa20Core(x[0..], ctx, true);
275 hashToBytes(buf[0..], x);
276280
277 var xout = out[i..];
278 const xin = in[i..];
279 var j: usize = 0;
280 while (j < in.len % 64) : (j += 1) {
281 xout[j] = xin[j] ^ buf[j];
281 fn hsalsa(input: [16]u8, key: [32]u8) [32]u8 {
282 var c: [4]u32 = undefined;
283 for (c) |_, i| {
284 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
282285 }
286 const ctx = initContext(keyToWords(key), c);
287 var x: BlockVec = undefined;
288 salsaCore(x[0..], ctx, false);
289 var out: [32]u8 = undefined;
290 mem.writeIntLittle(u32, out[0..4], x[0]);
291 mem.writeIntLittle(u32, out[4..8], x[5]);
292 mem.writeIntLittle(u32, out[8..12], x[10]);
293 mem.writeIntLittle(u32, out[12..16], x[15]);
294 mem.writeIntLittle(u32, out[16..20], x[6]);
295 mem.writeIntLittle(u32, out[20..24], x[7]);
296 mem.writeIntLittle(u32, out[24..28], x[8]);
297 mem.writeIntLittle(u32, out[28..32], x[9]);
298 return out;
283299 }
284 }
285
286 fn hsalsa20(input: [16]u8, key: [32]u8) [32]u8 {
287 var c: [4]u32 = undefined;
288 for (c) |_, i| {
289 c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]);
290 }
291 const ctx = initContext(keyToWords(key), c);
292 var x: BlockVec = undefined;
293 salsa20Core(x[0..], ctx, false);
294 var out: [32]u8 = undefined;
295 mem.writeIntLittle(u32, out[0..4], x[0]);
296 mem.writeIntLittle(u32, out[4..8], x[5]);
297 mem.writeIntLittle(u32, out[8..12], x[10]);
298 mem.writeIntLittle(u32, out[12..16], x[15]);
299 mem.writeIntLittle(u32, out[16..20], x[6]);
300 mem.writeIntLittle(u32, out[20..24], x[7]);
301 mem.writeIntLittle(u32, out[24..28], x[8]);
302 mem.writeIntLittle(u32, out[28..32], x[9]);
303 return out;
304 }
305};
300 };
301}
306302
307const Salsa20Impl = if (builtin.cpu.arch == .x86_64) Salsa20VecImpl else Salsa20NonVecImpl;
303const SalsaImpl = if (builtin.cpu.arch == .x86_64) SalsaVecImpl else SalsaNonVecImpl;
308304
309305fn keyToWords(key: [32]u8) [8]u32 {
310306 var k: [8]u32 = undefined;
......@@ -315,52 +311,56 @@ fn keyToWords(key: [32]u8) [8]u32 {
315311 return k;
316312}
317313
318fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [8]u8 } {
314fn extend(comptime rounds: comptime_int, key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [8]u8 } {
319315 return .{
320 .key = Salsa20Impl.hsalsa20(nonce[0..16].*, key),
316 .key = SalsaImpl(rounds).hsalsa(nonce[0..16].*, key),
321317 .nonce = nonce[16..24].*,
322318 };
323319}
324320
325/// The Salsa20 stream cipher.
326pub const Salsa20 = struct {
327 /// Nonce length in bytes.
328 pub const nonce_length = 8;
329 /// Key length in bytes.
330 pub const key_length = 32;
331
332 /// Add the output of the Salsa20 stream cipher to `in` and stores the result into `out`.
333 /// WARNING: This function doesn't provide authenticated encryption.
334 /// Using the AEAD or one of the `box` versions is usually preferred.
335 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
336 debug.assert(in.len == out.len);
337
338 var d: [4]u32 = undefined;
339 d[0] = mem.readIntLittle(u32, nonce[0..4]);
340 d[1] = mem.readIntLittle(u32, nonce[4..8]);
341 d[2] = @truncate(u32, counter);
342 d[3] = @truncate(u32, counter >> 32);
343 Salsa20Impl.salsa20Xor(out, in, keyToWords(key), d);
344 }
345};
321/// The Salsa stream cipher.
322pub fn Salsa(comptime rounds: comptime_int) type {
323 return struct {
324 /// Nonce length in bytes.
325 pub const nonce_length = 8;
326 /// Key length in bytes.
327 pub const key_length = 32;
328
329 /// Add the output of the Salsa stream cipher to `in` and stores the result into `out`.
330 /// WARNING: This function doesn't provide authenticated encryption.
331 /// Using the AEAD or one of the `box` versions is usually preferred.
332 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
333 debug.assert(in.len == out.len);
334
335 var d: [4]u32 = undefined;
336 d[0] = mem.readIntLittle(u32, nonce[0..4]);
337 d[1] = mem.readIntLittle(u32, nonce[4..8]);
338 d[2] = @truncate(u32, counter);
339 d[3] = @truncate(u32, counter >> 32);
340 SalsaImpl(rounds).salsaXor(out, in, keyToWords(key), d);
341 }
342 };
343}
346344
347/// The XSalsa20 stream cipher.
348pub const XSalsa20 = struct {
349 /// Nonce length in bytes.
350 pub const nonce_length = 24;
351 /// Key length in bytes.
352 pub const key_length = 32;
353
354 /// Add the output of the XSalsa20 stream cipher to `in` and stores the result into `out`.
355 /// WARNING: This function doesn't provide authenticated encryption.
356 /// Using the AEAD or one of the `box` versions is usually preferred.
357 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
358 const extended = extend(key, nonce);
359 Salsa20.xor(out, in, counter, extended.key, extended.nonce);
360 }
361};
345/// The XSalsa stream cipher.
346pub fn XSalsa(comptime rounds: comptime_int) type {
347 return struct {
348 /// Nonce length in bytes.
349 pub const nonce_length = 24;
350 /// Key length in bytes.
351 pub const key_length = 32;
352
353 /// Add the output of the XSalsa stream cipher to `in` and stores the result into `out`.
354 /// WARNING: This function doesn't provide authenticated encryption.
355 /// Using the AEAD or one of the `box` versions is usually preferred.
356 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
357 const extended = extend(rounds, key, nonce);
358 Salsa(rounds).xor(out, in, counter, extended.key, extended.nonce);
359 }
360 };
361}
362362
363/// The XSalsa20 stream cipher, combined with the Poly1305 MAC
363/// The XSalsa stream cipher, combined with the Poly1305 MAC
364364pub const XSalsa20Poly1305 = struct {
365365 /// Authentication tag length in bytes.
366366 pub const tag_length = Poly1305.mac_length;
......@@ -369,6 +369,8 @@ pub const XSalsa20Poly1305 = struct {
369369 /// Key length in bytes.
370370 pub const key_length = XSalsa20.key_length;
371371
372 const rounds = 20;
373
372374 /// c: ciphertext: output buffer should be of size m.len
373375 /// tag: authentication tag: output MAC
374376 /// m: message
......@@ -377,7 +379,7 @@ pub const XSalsa20Poly1305 = struct {
377379 /// k: private key
378380 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
379381 debug.assert(c.len == m.len);
380 const extended = extend(k, npub);
382 const extended = extend(rounds, k, npub);
381383 var block0 = [_]u8{0} ** 64;
382384 const mlen0 = math.min(32, m.len);
383385 mem.copy(u8, block0[32..][0..mlen0], m[0..mlen0]);
......@@ -398,7 +400,7 @@ pub const XSalsa20Poly1305 = struct {
398400 /// k: private key
399401 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
400402 debug.assert(c.len == m.len);
401 const extended = extend(k, npub);
403 const extended = extend(rounds, k, npub);
402404 var block0 = [_]u8{0} ** 64;
403405 const mlen0 = math.min(32, c.len);
404406 mem.copy(u8, block0[32..][0..mlen0], c[0..mlen0]);
......@@ -482,7 +484,7 @@ pub const Box = struct {
482484 pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 {
483485 const p = try X25519.scalarmult(secret_key, public_key);
484486 const zero = [_]u8{0} ** 16;
485 return Salsa20Impl.hsalsa20(zero, p);
487 return SalsaImpl(20).hsalsa(zero, p);
486488 }
487489
488490 /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`.