chacha20.hpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #pragma once
  2. #include <nall/arithmetic.hpp>
  3. #include <nall/array-view.hpp>
  4. namespace nall::Cipher {
  5. //64-bit nonce; 64-bit x 64-byte (256GB) counter
  6. struct ChaCha20 {
  7. ChaCha20(uint256_t key, uint64_t nonce, uint64_t counter = 0) {
  8. static const uint128_t sigma = 0x6b20657479622d323320646e61707865_u128; //"expand 32-byte k"
  9. input[ 0] = sigma >> 0;
  10. input[ 1] = sigma >> 32;
  11. input[ 2] = sigma >> 64;
  12. input[ 3] = sigma >> 96;
  13. input[ 4] = key >> 0;
  14. input[ 5] = key >> 32;
  15. input[ 6] = key >> 64;
  16. input[ 7] = key >> 96;
  17. input[ 8] = key >> 128;
  18. input[ 9] = key >> 160;
  19. input[10] = key >> 192;
  20. input[11] = key >> 224;
  21. input[12] = counter >> 0;
  22. input[13] = counter >> 32;
  23. input[14] = nonce >> 0;
  24. input[15] = nonce >> 32;
  25. offset = 0;
  26. }
  27. auto encrypt(array_view<uint8_t> input) -> vector<uint8_t> {
  28. vector<uint8_t> output;
  29. while(input) {
  30. if(!offset) {
  31. cipher();
  32. increment();
  33. }
  34. auto byte = offset++;
  35. output.append(*input++ ^ (block[byte >> 2] >> (byte & 3) * 8));
  36. offset &= 63;
  37. }
  38. return output;
  39. }
  40. auto decrypt(array_view<uint8_t> input) -> vector<uint8_t> {
  41. return encrypt(input); //reciprocal cipher
  42. }
  43. //protected:
  44. inline auto rol(uint32_t value, uint bits) -> uint32_t {
  45. return value << bits | value >> 32 - bits;
  46. }
  47. auto quarterRound(uint32_t x[16], uint a, uint b, uint c, uint d) -> void {
  48. x[a] += x[b]; x[d] = rol(x[d] ^ x[a], 16);
  49. x[c] += x[d]; x[b] = rol(x[b] ^ x[c], 12);
  50. x[a] += x[b]; x[d] = rol(x[d] ^ x[a], 8);
  51. x[c] += x[d]; x[b] = rol(x[b] ^ x[c], 7);
  52. }
  53. auto cipher() -> void {
  54. memory::copy(block, input, 64);
  55. for(uint n : range(10)) {
  56. quarterRound(block, 0, 4, 8, 12);
  57. quarterRound(block, 1, 5, 9, 13);
  58. quarterRound(block, 2, 6, 10, 14);
  59. quarterRound(block, 3, 7, 11, 15);
  60. quarterRound(block, 0, 5, 10, 15);
  61. quarterRound(block, 1, 6, 11, 12);
  62. quarterRound(block, 2, 7, 8, 13);
  63. quarterRound(block, 3, 4, 9, 14);
  64. }
  65. }
  66. auto increment() -> void {
  67. for(uint n : range(16)) {
  68. block[n] += input[n];
  69. }
  70. if(!++input[12]) ++input[13];
  71. }
  72. uint32_t input[16];
  73. uint32_t block[16];
  74. uint64_t offset;
  75. };
  76. struct HChaCha20 : protected ChaCha20 {
  77. HChaCha20(uint256_t key, uint128_t nonce) : ChaCha20(key, nonce >> 64, nonce >> 0) {
  78. cipher();
  79. }
  80. auto key() const -> uint256_t {
  81. uint256_t key = 0;
  82. for(uint n : range(4)) key |= (uint256_t)block[ 0 + n] << (n + 0) * 32;
  83. for(uint n : range(4)) key |= (uint256_t)block[12 + n] << (n + 4) * 32;
  84. return key;
  85. }
  86. };
  87. //192-bit nonce; 64-bit x 64-byte (256GB) counter
  88. struct XChaCha20 : ChaCha20 {
  89. XChaCha20(uint256_t key, uint192_t nonce, uint64_t counter = 0):
  90. ChaCha20(HChaCha20(key, nonce).key(), nonce >> 128, counter) {
  91. }
  92. };
  93. }