authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2023-05-23 16:36:44+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-05-23 14:36:44+00:00
log057d30bacc4d97f53c2e69585dc8d50186cfc7f5
tree0611d5cf2910f3f90707b71dbdac044d75ac030b
parent7cb3a6750762ffc6a17a76a0924079cbf4613a82
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.crypto.chacha: remove the hack for ChaCha with a 64-bit counter (#15818)

Support for 64-bit counters was a hack built upon the version with a 32-bit counter, that emulated a larger counter by splitting the input into large blocks. This is fragile, particularily if the initial counter is set to a non-default value and if we have parallelism. Simply add a comptime parameter to check if we have a 32 bit or a 64 bit counter instead. Also convert a couple while() loops to for(), and change @panic() to @compileError().

1 files changed, 68 insertions(+), 68 deletions(-)

lib/std/crypto/chacha20.zig+68-68
...@@ -109,14 +109,18 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -109,14 +109,18 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
109 mem.readIntLittle(u32, c[8..12]),109 mem.readIntLittle(u32, c[8..12]),
110 mem.readIntLittle(u32, c[12..16]),110 mem.readIntLittle(u32, c[12..16]),
111 };111 };
112 const n1 = @addWithOverflow(d[0], 1);
112 return BlockVec{113 return BlockVec{
113 constant_le,114 constant_le,
114 Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] },115 Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] },
115 Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] },116 Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] },
116 Lane{ d[0], d[1], d[2], d[3], d[0] +% 1, d[1], d[2], d[3] },117 Lane{ d[0], d[1], d[2], d[3], n1[0], d[1] +% n1[1], d[2], d[3] },
117 };118 };
118 },119 },
119 4 => {120 4 => {
121 const n1 = @addWithOverflow(d[0], 1);
122 const n2 = @addWithOverflow(d[0], 2);
123 const n3 = @addWithOverflow(d[0], 3);
120 const constant_le = Lane{124 const constant_le = Lane{
121 mem.readIntLittle(u32, c[0..4]),125 mem.readIntLittle(u32, c[0..4]),
122 mem.readIntLittle(u32, c[4..8]),126 mem.readIntLittle(u32, c[4..8]),
...@@ -139,10 +143,10 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -139,10 +143,10 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
139 constant_le,143 constant_le,
140 Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] },144 Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] },
141 Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] },145 Lane{ key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7], key[4], key[5], key[6], key[7] },
142 Lane{ d[0], d[1], d[2], d[3], d[0] +% 1, d[1], d[2], d[3], d[0] +% 2, d[1], d[2], d[3], d[0] +% 3, d[1], d[2], d[3] },146 Lane{ d[0], d[1], d[2], d[3], n1[0], d[1] +% n1[1], d[2], d[3], n2[0], d[1] +% n2[1], d[2], d[3], n3[0], d[1] +% n3[1], d[2], d[3] },
143 };147 };
144 },148 },
145 else => @panic("invalid degree"),149 else => @compileError("invalid degree"),
146 }150 }
147 }151 }
148152
...@@ -153,19 +157,19 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -153,19 +157,19 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
153 1 => [_]i32{ 3, 0, 1, 2 },157 1 => [_]i32{ 3, 0, 1, 2 },
154 2 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 },158 2 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 },
155 4 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 } ++ [_]i32{ 11, 8, 9, 10 } ++ [_]i32{ 15, 12, 13, 14 },159 4 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 } ++ [_]i32{ 11, 8, 9, 10 } ++ [_]i32{ 15, 12, 13, 14 },
156 else => @panic("invalid degree"),160 else => @compileError("invalid degree"),
157 };161 };
158 const m1 = switch (degree) {162 const m1 = switch (degree) {
159 1 => [_]i32{ 2, 3, 0, 1 },163 1 => [_]i32{ 2, 3, 0, 1 },
160 2 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 },164 2 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 },
161 4 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 } ++ [_]i32{ 10, 11, 8, 9 } ++ [_]i32{ 14, 15, 12, 13 },165 4 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 } ++ [_]i32{ 10, 11, 8, 9 } ++ [_]i32{ 14, 15, 12, 13 },
162 else => @panic("invalid degree"),166 else => @compileError("invalid degree"),
163 };167 };
164 const m2 = switch (degree) {168 const m2 = switch (degree) {
165 1 => [_]i32{ 1, 2, 3, 0 },169 1 => [_]i32{ 1, 2, 3, 0 },
166 2 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 },170 2 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 },
167 4 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 } ++ [_]i32{ 9, 10, 11, 8 } ++ [_]i32{ 13, 14, 15, 12 },171 4 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 } ++ [_]i32{ 9, 10, 11, 8 } ++ [_]i32{ 13, 14, 15, 12 },
168 else => @panic("invalid degree"),172 else => @compileError("invalid degree"),
169 };173 };
170174
171 var r: usize = 0;175 var r: usize = 0;
...@@ -212,8 +216,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -212,8 +216,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
212216
213 inline fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void {217 inline fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void {
214 for (0..dm) |d| {218 for (0..dm) |d| {
215 var i: usize = 0;219 for (0..4) |i| {
216 while (i < 4) : (i += 1) {
217 mem.writeIntLittle(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d]);220 mem.writeIntLittle(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d]);
218 mem.writeIntLittle(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d]);221 mem.writeIntLittle(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d]);
219 mem.writeIntLittle(u32, out[64 * d + 16 * i + 8 ..][0..4], x[i][2 + 4 * d]);222 mem.writeIntLittle(u32, out[64 * d + 16 * i + 8 ..][0..4], x[i][2 + 4 * d]);
...@@ -229,8 +232,8 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -229,8 +232,8 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
229 x[3] +%= ctx[3];232 x[3] +%= ctx[3];
230 }233 }
231234
232 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {235 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void {
233 var ctx = initContext(key, counter);236 var ctx = initContext(key, nonce_and_counter);
234 var x: BlockVec = undefined;237 var x: BlockVec = undefined;
235 var buf: [64 * degree]u8 = undefined;238 var buf: [64 * degree]u8 = undefined;
236 var i: usize = 0;239 var i: usize = 0;
...@@ -242,16 +245,20 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -242,16 +245,20 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
242245
243 var xout = out[i..];246 var xout = out[i..];
244 const xin = in[i..];247 const xin = in[i..];
245 var j: usize = 0;248 for (0..64 * d) |j| {
246 while (j < 64 * d) : (j += 1) {
247 xout[j] = xin[j];249 xout[j] = xin[j];
248 }250 }
249 j = 0;251 for (0..64 * d) |j| {
250 while (j < 64 * d) : (j += 1) {
251 xout[j] ^= buf[j];252 xout[j] ^= buf[j];
252 }253 }
253 inline for (0..d) |d_| {254 inline for (0..d) |d_| {
254 ctx[3][4 * d_] += @intCast(u32, d);255 if (count64) {
256 const next = @addWithOverflow(ctx[3][4 * d_], d);
257 ctx[3][4 * d_] = next[0];
258 ctx[3][4 * d_ + 1] +%= next[1];
259 } else {
260 ctx[3][4 * d_] +%= d;
261 }
255 }262 }
256 }263 }
257 }264 }
...@@ -262,15 +269,14 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -262,15 +269,14 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
262269
263 var xout = out[i..];270 var xout = out[i..];
264 const xin = in[i..];271 const xin = in[i..];
265 var j: usize = 0;272 for (0..in.len % 64) |j| {
266 while (j < in.len % 64) : (j += 1) {
267 xout[j] = xin[j] ^ buf[j];273 xout[j] = xin[j] ^ buf[j];
268 }274 }
269 }275 }
270 }276 }
271277
272 fn chacha20Stream(out: []u8, key: [8]u32, counter: [4]u32) void {278 fn chacha20Stream(out: []u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void {
273 var ctx = initContext(key, counter);279 var ctx = initContext(key, nonce_and_counter);
274 var x: BlockVec = undefined;280 var x: BlockVec = undefined;
275 var i: usize = 0;281 var i: usize = 0;
276 inline for ([_]comptime_int{ 4, 2, 1 }) |d| {282 inline for ([_]comptime_int{ 4, 2, 1 }) |d| {
...@@ -279,7 +285,13 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -279,7 +285,13 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
279 contextFeedback(&x, ctx);285 contextFeedback(&x, ctx);
280 hashToBytes(d, out[i..][0 .. 64 * d], x);286 hashToBytes(d, out[i..][0 .. 64 * d], x);
281 inline for (0..d) |d_| {287 inline for (0..d) |d_| {
282 ctx[3][4 * d_] += @intCast(u32, d);288 if (count64) {
289 const next = @addWithOverflow(ctx[3][4 * d_], d);
290 ctx[3][4 * d_] = next[0];
291 ctx[3][4 * d_ + 1] +%= next[1];
292 } else {
293 ctx[3][4 * d_] +%= d;
294 }
283 }295 }
284 }296 }
285 }297 }
...@@ -382,8 +394,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -382,8 +394,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
382 }394 }
383395
384 inline fn hashToBytes(out: *[64]u8, x: BlockVec) void {396 inline fn hashToBytes(out: *[64]u8, x: BlockVec) void {
385 var i: usize = 0;397 for (0..4) |i| {
386 while (i < 4) : (i += 1) {
387 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);398 mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
388 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);399 mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);
389 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);400 mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);
...@@ -392,14 +403,13 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -392,14 +403,13 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
392 }403 }
393404
394 inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void {405 inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void {
395 var i: usize = 0;406 for (0..16) |i| {
396 while (i < 16) : (i += 1) {
397 x[i] +%= ctx[i];407 x[i] +%= ctx[i];
398 }408 }
399 }409 }
400410
401 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void {411 fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void {
402 var ctx = initContext(key, counter);412 var ctx = initContext(key, nonce_and_counter);
403 var x: BlockVec = undefined;413 var x: BlockVec = undefined;
404 var buf: [64]u8 = undefined;414 var buf: [64]u8 = undefined;
405 var i: usize = 0;415 var i: usize = 0;
...@@ -410,15 +420,19 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -410,15 +420,19 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
410420
411 var xout = out[i..];421 var xout = out[i..];
412 const xin = in[i..];422 const xin = in[i..];
413 var j: usize = 0;423 for (0..64) |j| {
414 while (j < 64) : (j += 1) {
415 xout[j] = xin[j];424 xout[j] = xin[j];
416 }425 }
417 j = 0;426 for (0..64) |j| {
418 while (j < 64) : (j += 1) {
419 xout[j] ^= buf[j];427 xout[j] ^= buf[j];
420 }428 }
421 ctx[12] += 1;429 if (count64) {
430 const next = @addWithOverflow(ctx[12], 1);
431 ctx[12] = next[0];
432 ctx[13] +%= next[1];
433 } else {
434 ctx[12] +%= 1;
435 }
422 }436 }
423 if (i < in.len) {437 if (i < in.len) {
424 chacha20Core(x[0..], ctx);438 chacha20Core(x[0..], ctx);
...@@ -427,22 +441,27 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -427,22 +441,27 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
427441
428 var xout = out[i..];442 var xout = out[i..];
429 const xin = in[i..];443 const xin = in[i..];
430 var j: usize = 0;444 for (0..in.len % 64) |j| {
431 while (j < in.len % 64) : (j += 1) {
432 xout[j] = xin[j] ^ buf[j];445 xout[j] = xin[j] ^ buf[j];
433 }446 }
434 }447 }
435 }448 }
436449
437 fn chacha20Stream(out: []u8, key: [8]u32, counter: [4]u32) void {450 fn chacha20Stream(out: []u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void {
438 var ctx = initContext(key, counter);451 var ctx = initContext(key, nonce_and_counter);
439 var x: BlockVec = undefined;452 var x: BlockVec = undefined;
440 var i: usize = 0;453 var i: usize = 0;
441 while (i + 64 <= out.len) : (i += 64) {454 while (i + 64 <= out.len) : (i += 64) {
442 chacha20Core(x[0..], ctx);455 chacha20Core(x[0..], ctx);
443 contextFeedback(&x, ctx);456 contextFeedback(&x, ctx);
444 hashToBytes(out[i..][0..64], x);457 hashToBytes(out[i..][0..64], x);
445 ctx[12] += 1;458 if (count64) {
459 const next = @addWithOverflow(ctx[12], 1);
460 ctx[12] = next[0];
461 ctx[13] +%= next[1];
462 } else {
463 ctx[12] +%= 1;
464 }
446 }465 }
447 if (i < out.len) {466 if (i < out.len) {
448 chacha20Core(x[0..], ctx);467 chacha20Core(x[0..], ctx);
...@@ -496,8 +515,7 @@ fn ChaChaImpl(comptime rounds_nb: usize) type {...@@ -496,8 +515,7 @@ fn ChaChaImpl(comptime rounds_nb: usize) type {
496515
497fn keyToWords(key: [32]u8) [8]u32 {516fn keyToWords(key: [32]u8) [8]u32 {
498 var k: [8]u32 = undefined;517 var k: [8]u32 = undefined;
499 var i: usize = 0;518 for (0..8) |i| {
500 while (i < 8) : (i += 1) {
501 k[i] = mem.readIntLittle(u32, key[i * 4 ..][0..4]);519 k[i] = mem.readIntLittle(u32, key[i * 4 ..][0..4]);
502 }520 }
503 return k;521 return k;
...@@ -527,26 +545,26 @@ fn ChaChaIETF(comptime rounds_nb: usize) type {...@@ -527,26 +545,26 @@ fn ChaChaIETF(comptime rounds_nb: usize) type {
527 /// Using the AEAD or one of the `box` versions is usually preferred.545 /// Using the AEAD or one of the `box` versions is usually preferred.
528 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {546 pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
529 assert(in.len == out.len);547 assert(in.len == out.len);
530 assert(in.len / 64 <= (1 << 32 - 1) - counter);548 assert(in.len <= 64 * (@as(u39, 1 << 32) - counter));
531549
532 var d: [4]u32 = undefined;550 var d: [4]u32 = undefined;
533 d[0] = counter;551 d[0] = counter;
534 d[1] = mem.readIntLittle(u32, nonce[0..4]);552 d[1] = mem.readIntLittle(u32, nonce[0..4]);
535 d[2] = mem.readIntLittle(u32, nonce[4..8]);553 d[2] = mem.readIntLittle(u32, nonce[4..8]);
536 d[3] = mem.readIntLittle(u32, nonce[8..12]);554 d[3] = mem.readIntLittle(u32, nonce[8..12]);
537 ChaChaImpl(rounds_nb).chacha20Xor(out, in, keyToWords(key), d);555 ChaChaImpl(rounds_nb).chacha20Xor(out, in, keyToWords(key), d, false);
538 }556 }
539557
540 /// Write the output of the ChaCha20 stream cipher into `out`.558 /// Write the output of the ChaCha20 stream cipher into `out`.
541 pub fn stream(out: []u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {559 pub fn stream(out: []u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
542 assert(out.len / 64 <= (1 << 32 - 1) - counter);560 assert(out.len <= 64 * (@as(u39, 1 << 32) - counter));
543561
544 var d: [4]u32 = undefined;562 var d: [4]u32 = undefined;
545 d[0] = counter;563 d[0] = counter;
546 d[1] = mem.readIntLittle(u32, nonce[0..4]);564 d[1] = mem.readIntLittle(u32, nonce[0..4]);
547 d[2] = mem.readIntLittle(u32, nonce[4..8]);565 d[2] = mem.readIntLittle(u32, nonce[4..8]);
548 d[3] = mem.readIntLittle(u32, nonce[8..12]);566 d[3] = mem.readIntLittle(u32, nonce[8..12]);
549 ChaChaImpl(rounds_nb).chacha20Stream(out, keyToWords(key), d);567 ChaChaImpl(rounds_nb).chacha20Stream(out, keyToWords(key), d, false);
550 }568 }
551 };569 };
552}570}
...@@ -565,47 +583,28 @@ fn ChaChaWith64BitNonce(comptime rounds_nb: usize) type {...@@ -565,47 +583,28 @@ fn ChaChaWith64BitNonce(comptime rounds_nb: usize) type {
565 /// Using the AEAD or one of the `box` versions is usually preferred.583 /// Using the AEAD or one of the `box` versions is usually preferred.
566 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {584 pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void {
567 assert(in.len == out.len);585 assert(in.len == out.len);
568 assert(in.len / 64 <= (1 << 64 - 1) - counter);586 assert(in.len <= 64 * (@as(u71, 1 << 64) - counter));
569587
570 var cursor: usize = 0;
571 const k = keyToWords(key);588 const k = keyToWords(key);
572 var c: [4]u32 = undefined;589 var c: [4]u32 = undefined;
573 c[0] = @truncate(u32, counter);590 c[0] = @truncate(u32, counter);
574 c[1] = @truncate(u32, counter >> 32);591 c[1] = @truncate(u32, counter >> 32);
575 c[2] = mem.readIntLittle(u32, nonce[0..4]);592 c[2] = mem.readIntLittle(u32, nonce[0..4]);
576 c[3] = mem.readIntLittle(u32, nonce[4..8]);593 c[3] = mem.readIntLittle(u32, nonce[4..8]);
577594 ChaChaImpl(rounds_nb).chacha20Xor(out, in, k, c, true);
578 // The full block size is greater than the address space on a 32bit machine
579 const big_block = if (@sizeOf(usize) > 4) (block_length << 32) else maxInt(usize);
580
581 // first partial big block
582 if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
583 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..big_block], in[cursor..big_block], k, c);
584 cursor = big_block - cursor;
585 c[1] += 1;
586 if (comptime @sizeOf(usize) > 4) {
587 // A big block is giant: 256 GiB, but we can avoid this limitation
588 var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
589 while (remaining_blocks > 0) : (remaining_blocks -= 1) {
590 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
591 c[1] += 1; // upper 32-bit of counter, generic chacha20Xor() doesn't know about this.
592 cursor += big_block;
593 }
594 }
595 }
596 ChaChaImpl(rounds_nb).chacha20Xor(out[cursor..], in[cursor..], k, c);
597 }595 }
598596
599 /// Write the output of the ChaCha20 stream cipher into `out`.597 /// Write the output of the ChaCha20 stream cipher into `out`.
600 pub fn stream(out: []u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {598 pub fn stream(out: []u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void {
601 assert(out.len / 64 <= (1 << 32 - 1) - counter);599 assert(out.len <= 64 * (@as(u71, 1 << 64) - counter));
600
602 const k = keyToWords(key);601 const k = keyToWords(key);
603 var c: [4]u32 = undefined;602 var c: [4]u32 = undefined;
604 c[0] = @truncate(u32, counter);603 c[0] = @truncate(u32, counter);
605 c[1] = @truncate(u32, counter >> 32);604 c[1] = @truncate(u32, counter >> 32);
606 c[2] = mem.readIntLittle(u32, nonce[0..4]);605 c[2] = mem.readIntLittle(u32, nonce[0..4]);
607 c[3] = mem.readIntLittle(u32, nonce[4..8]);606 c[3] = mem.readIntLittle(u32, nonce[4..8]);
608 ChaChaImpl(rounds_nb).chacha20Stream(out, k, c);607 ChaChaImpl(rounds_nb).chacha20Stream(out, k, c, true);
609 }608 }
610 };609 };
611}610}
...@@ -649,6 +648,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {...@@ -649,6 +648,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {
649 /// k: private key648 /// k: private key
650 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {649 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
651 assert(c.len == m.len);650 assert(c.len == m.len);
651 assert(m.len <= 64 * (@as(u39, 1 << 32) - 1));
652652
653 var polyKey = [_]u8{0} ** 32;653 var polyKey = [_]u8{0} ** 32;
654 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);654 ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub);
...@@ -766,7 +766,7 @@ test "chacha20 AEAD API" {...@@ -766,7 +766,7 @@ test "chacha20 AEAD API" {
766 aead.encrypt(c[0..], tag[0..], m, ad, nonce, key);766 aead.encrypt(c[0..], tag[0..], m, ad, nonce, key);
767 try aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key);767 try aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key);
768 try testing.expectEqualSlices(u8, out[0..], m);768 try testing.expectEqualSlices(u8, out[0..], m);
769 c[0] += 1;769 c[0] +%= 1;
770 try testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key));770 try testing.expectError(error.AuthenticationFailed, aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key));
771 }771 }
772}772}
...@@ -1154,7 +1154,7 @@ test "crypto.xchacha20" {...@@ -1154,7 +1154,7 @@ test "crypto.xchacha20" {
1154 var buf: [2 * c.len]u8 = undefined;1154 var buf: [2 * c.len]u8 = undefined;
1155 try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");1155 try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234");
1156 try testing.expectEqualSlices(u8, out[0..], m);1156 try testing.expectEqualSlices(u8, out[0..], m);
1157 c[0] += 1;1157 c[0] +%= 1;
1158 try testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key));1158 try testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key));
1159 }1159 }
1160}1160}