| ... | ... | @@ -109,14 +109,18 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type |
| 109 | 109 | mem.readIntLittle(u32, c[8..12]), |
| 110 | 110 | mem.readIntLittle(u32, c[12..16]), |
| 111 | 111 | }; |
| 112 | const n1 = @addWithOverflow(d[0], 1); |
| 112 | 113 | return BlockVec{ |
| 113 | 114 | constant_le, |
| 114 | 115 | Lane{ key[0], key[1], key[2], key[3], key[0], key[1], key[2], key[3] }, |
| 115 | 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 | 120 | 4 => { |
| 121 | const n1 = @addWithOverflow(d[0], 1); |
| 122 | const n2 = @addWithOverflow(d[0], 2); |
| 123 | const n3 = @addWithOverflow(d[0], 3); |
| 120 | 124 | const constant_le = Lane{ |
| 121 | 125 | mem.readIntLittle(u32, c[0..4]), |
| 122 | 126 | mem.readIntLittle(u32, c[4..8]), |
| ... | ... | @@ -139,10 +143,10 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type |
| 139 | 143 | constant_le, |
| 140 | 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 | 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 | } |
| 148 | 152 | |
| ... | ... | @@ -153,19 +157,19 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type |
| 153 | 157 | 1 => [_]i32{ 3, 0, 1, 2 }, |
| 154 | 158 | 2 => [_]i32{ 3, 0, 1, 2 } ++ [_]i32{ 7, 4, 5, 6 }, |
| 155 | 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 | 162 | const m1 = switch (degree) { |
| 159 | 163 | 1 => [_]i32{ 2, 3, 0, 1 }, |
| 160 | 164 | 2 => [_]i32{ 2, 3, 0, 1 } ++ [_]i32{ 6, 7, 4, 5 }, |
| 161 | 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 | 168 | const m2 = switch (degree) { |
| 165 | 169 | 1 => [_]i32{ 1, 2, 3, 0 }, |
| 166 | 170 | 2 => [_]i32{ 1, 2, 3, 0 } ++ [_]i32{ 5, 6, 7, 4 }, |
| 167 | 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 | }; |
| 170 | 174 | |
| 171 | 175 | var r: usize = 0; |
| ... | ... | @@ -212,8 +216,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type |
| 212 | 216 | |
| 213 | 217 | inline fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void { |
| 214 | 218 | for (0..dm) |d| { |
| 215 | | var i: usize = 0; |
| 216 | | while (i < 4) : (i += 1) { |
| 219 | for (0..4) |i| { |
| 217 | 220 | mem.writeIntLittle(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d]); |
| 218 | 221 | mem.writeIntLittle(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d]); |
| 219 | 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 | 232 | x[3] +%= ctx[3]; |
| 230 | 233 | } |
| 231 | 234 | |
| 232 | | fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void { |
| 233 | | var ctx = initContext(key, counter); |
| 235 | fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void { |
| 236 | var ctx = initContext(key, nonce_and_counter); |
| 234 | 237 | var x: BlockVec = undefined; |
| 235 | 238 | var buf: [64 * degree]u8 = undefined; |
| 236 | 239 | var i: usize = 0; |
| ... | ... | @@ -242,16 +245,20 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type |
| 242 | 245 | |
| 243 | 246 | var xout = out[i..]; |
| 244 | 247 | const xin = in[i..]; |
| 245 | | var j: usize = 0; |
| 246 | | while (j < 64 * d) : (j += 1) { |
| 248 | for (0..64 * d) |j| { |
| 247 | 249 | xout[j] = xin[j]; |
| 248 | 250 | } |
| 249 | | j = 0; |
| 250 | | while (j < 64 * d) : (j += 1) { |
| 251 | for (0..64 * d) |j| { |
| 251 | 252 | xout[j] ^= buf[j]; |
| 252 | 253 | } |
| 253 | 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 | 269 | |
| 263 | 270 | var xout = out[i..]; |
| 264 | 271 | const xin = in[i..]; |
| 265 | | var j: usize = 0; |
| 266 | | while (j < in.len % 64) : (j += 1) { |
| 272 | for (0..in.len % 64) |j| { |
| 267 | 273 | xout[j] = xin[j] ^ buf[j]; |
| 268 | 274 | } |
| 269 | 275 | } |
| 270 | 276 | } |
| 271 | 277 | |
| 272 | | fn chacha20Stream(out: []u8, key: [8]u32, counter: [4]u32) void { |
| 273 | | var ctx = initContext(key, counter); |
| 278 | fn chacha20Stream(out: []u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void { |
| 279 | var ctx = initContext(key, nonce_and_counter); |
| 274 | 280 | var x: BlockVec = undefined; |
| 275 | 281 | var i: usize = 0; |
| 276 | 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 | 285 | contextFeedback(&x, ctx); |
| 280 | 286 | hashToBytes(d, out[i..][0 .. 64 * d], x); |
| 281 | 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 | 394 | } |
| 383 | 395 | |
| 384 | 396 | inline fn hashToBytes(out: *[64]u8, x: BlockVec) void { |
| 385 | | var i: usize = 0; |
| 386 | | while (i < 4) : (i += 1) { |
| 397 | for (0..4) |i| { |
| 387 | 398 | mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]); |
| 388 | 399 | mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]); |
| 389 | 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 | 403 | } |
| 393 | 404 | |
| 394 | 405 | inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void { |
| 395 | | var i: usize = 0; |
| 396 | | while (i < 16) : (i += 1) { |
| 406 | for (0..16) |i| { |
| 397 | 407 | x[i] +%= ctx[i]; |
| 398 | 408 | } |
| 399 | 409 | } |
| 400 | 410 | |
| 401 | | fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void { |
| 402 | | var ctx = initContext(key, counter); |
| 411 | fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void { |
| 412 | var ctx = initContext(key, nonce_and_counter); |
| 403 | 413 | var x: BlockVec = undefined; |
| 404 | 414 | var buf: [64]u8 = undefined; |
| 405 | 415 | var i: usize = 0; |
| ... | ... | @@ -410,15 +420,19 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { |
| 410 | 420 | |
| 411 | 421 | var xout = out[i..]; |
| 412 | 422 | const xin = in[i..]; |
| 413 | | var j: usize = 0; |
| 414 | | while (j < 64) : (j += 1) { |
| 423 | for (0..64) |j| { |
| 415 | 424 | xout[j] = xin[j]; |
| 416 | 425 | } |
| 417 | | j = 0; |
| 418 | | while (j < 64) : (j += 1) { |
| 426 | for (0..64) |j| { |
| 419 | 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 | 437 | if (i < in.len) { |
| 424 | 438 | chacha20Core(x[0..], ctx); |
| ... | ... | @@ -427,22 +441,27 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { |
| 427 | 441 | |
| 428 | 442 | var xout = out[i..]; |
| 429 | 443 | const xin = in[i..]; |
| 430 | | var j: usize = 0; |
| 431 | | while (j < in.len % 64) : (j += 1) { |
| 444 | for (0..in.len % 64) |j| { |
| 432 | 445 | xout[j] = xin[j] ^ buf[j]; |
| 433 | 446 | } |
| 434 | 447 | } |
| 435 | 448 | } |
| 436 | 449 | |
| 437 | | fn chacha20Stream(out: []u8, key: [8]u32, counter: [4]u32) void { |
| 438 | | var ctx = initContext(key, counter); |
| 450 | fn chacha20Stream(out: []u8, key: [8]u32, nonce_and_counter: [4]u32, comptime count64: bool) void { |
| 451 | var ctx = initContext(key, nonce_and_counter); |
| 439 | 452 | var x: BlockVec = undefined; |
| 440 | 453 | var i: usize = 0; |
| 441 | 454 | while (i + 64 <= out.len) : (i += 64) { |
| 442 | 455 | chacha20Core(x[0..], ctx); |
| 443 | 456 | contextFeedback(&x, ctx); |
| 444 | 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 | 466 | if (i < out.len) { |
| 448 | 467 | chacha20Core(x[0..], ctx); |
| ... | ... | @@ -496,8 +515,7 @@ fn ChaChaImpl(comptime rounds_nb: usize) type { |
| 496 | 515 | |
| 497 | 516 | fn keyToWords(key: [32]u8) [8]u32 { |
| 498 | 517 | var k: [8]u32 = undefined; |
| 499 | | var i: usize = 0; |
| 500 | | while (i < 8) : (i += 1) { |
| 518 | for (0..8) |i| { |
| 501 | 519 | k[i] = mem.readIntLittle(u32, key[i * 4 ..][0..4]); |
| 502 | 520 | } |
| 503 | 521 | return k; |
| ... | ... | @@ -527,26 +545,26 @@ fn ChaChaIETF(comptime rounds_nb: usize) type { |
| 527 | 545 | /// Using the AEAD or one of the `box` versions is usually preferred. |
| 528 | 546 | pub fn xor(out: []u8, in: []const u8, counter: u32, key: [key_length]u8, nonce: [nonce_length]u8) void { |
| 529 | 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)); |
| 531 | 549 | |
| 532 | 550 | var d: [4]u32 = undefined; |
| 533 | 551 | d[0] = counter; |
| 534 | 552 | d[1] = mem.readIntLittle(u32, nonce[0..4]); |
| 535 | 553 | d[2] = mem.readIntLittle(u32, nonce[4..8]); |
| 536 | 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 | } |
| 539 | 557 | |
| 540 | 558 | /// Write the output of the ChaCha20 stream cipher into `out`. |
| 541 | 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)); |
| 543 | 561 | |
| 544 | 562 | var d: [4]u32 = undefined; |
| 545 | 563 | d[0] = counter; |
| 546 | 564 | d[1] = mem.readIntLittle(u32, nonce[0..4]); |
| 547 | 565 | d[2] = mem.readIntLittle(u32, nonce[4..8]); |
| 548 | 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 | 583 | /// Using the AEAD or one of the `box` versions is usually preferred. |
| 566 | 584 | pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void { |
| 567 | 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)); |
| 569 | 587 | |
| 570 | | var cursor: usize = 0; |
| 571 | 588 | const k = keyToWords(key); |
| 572 | 589 | var c: [4]u32 = undefined; |
| 573 | 590 | c[0] = @truncate(u32, counter); |
| 574 | 591 | c[1] = @truncate(u32, counter >> 32); |
| 575 | 592 | c[2] = mem.readIntLittle(u32, nonce[0..4]); |
| 576 | 593 | c[3] = mem.readIntLittle(u32, nonce[4..8]); |
| 577 | | |
| 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); |
| 594 | ChaChaImpl(rounds_nb).chacha20Xor(out, in, k, c, true); |
| 597 | 595 | } |
| 598 | 596 | |
| 599 | 597 | /// Write the output of the ChaCha20 stream cipher into `out`. |
| 600 | 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 | 601 | const k = keyToWords(key); |
| 603 | 602 | var c: [4]u32 = undefined; |
| 604 | 603 | c[0] = @truncate(u32, counter); |
| 605 | 604 | c[1] = @truncate(u32, counter >> 32); |
| 606 | 605 | c[2] = mem.readIntLittle(u32, nonce[0..4]); |
| 607 | 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 | 648 | /// k: private key |
| 650 | 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 | 650 | assert(c.len == m.len); |
| 651 | assert(m.len <= 64 * (@as(u39, 1 << 32) - 1)); |
| 652 | 652 | |
| 653 | 653 | var polyKey = [_]u8{0} ** 32; |
| 654 | 654 | ChaChaIETF(rounds_nb).xor(polyKey[0..], polyKey[0..], 0, k, npub); |
| ... | ... | @@ -766,7 +766,7 @@ test "chacha20 AEAD API" { |
| 766 | 766 | aead.encrypt(c[0..], tag[0..], m, ad, nonce, key); |
| 767 | 767 | try aead.decrypt(out[0..], c[0..], tag, ad[0..], nonce, key); |
| 768 | 768 | try testing.expectEqualSlices(u8, out[0..], m); |
| 769 | | c[0] += 1; |
| 769 | c[0] +%= 1; |
| 770 | 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 | 1154 | var buf: [2 * c.len]u8 = undefined; |
| 1155 | 1155 | try testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&c)}), "994D2DD32333F48E53650C02C7A2ABB8E018B0836D7175AEC779F52E961780768F815C58F1AA52D211498DB89B9216763F569C9433A6BBFCEFB4D4A49387A4C5207FBB3B5A92B5941294DF30588C6740D39DC16FA1F0E634F7246CF7CDCB978E44347D89381B7A74EB7084F754B90BDE9AAF5A94B8F2A85EFD0B50692AE2D425E234"); |
| 1156 | 1156 | try testing.expectEqualSlices(u8, out[0..], m); |
| 1157 | | c[0] += 1; |
| 1157 | c[0] +%= 1; |
| 1158 | 1158 | try testing.expectError(error.AuthenticationFailed, XChaCha20Poly1305.decrypt(out[0..], c[0..m.len], c[m.len..].*, ad, nonce, key)); |
| 1159 | 1159 | } |
| 1160 | 1160 | } |