/root/bitcoin/src/crypto/chacha20.cpp
| Line | Count | Source | 
| 1 |  | // Copyright (c) 2017-present The Bitcoin Core developers | 
| 2 |  | // Distributed under the MIT software license, see the accompanying | 
| 3 |  | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | 
| 4 |  |  | 
| 5 |  | // Based on the public domain implementation 'merged' by D. J. Bernstein | 
| 6 |  | // See https://cr.yp.to/chacha.html. | 
| 7 |  |  | 
| 8 |  | #include <crypto/common.h> | 
| 9 |  | #include <crypto/chacha20.h> | 
| 10 |  | #include <support/cleanse.h> | 
| 11 |  | #include <span.h> | 
| 12 |  |  | 
| 13 |  | #include <algorithm> | 
| 14 |  | #include <bit> | 
| 15 |  | #include <cstring> | 
| 16 |  |  | 
| 17 |  | #define QUARTERROUND(a,b,c,d) \ | 
| 18 |  |   a += b; d = std::rotl(d ^ a, 16); \ | 
| 19 |  |   c += d; b = std::rotl(b ^ c, 12); \ | 
| 20 |  |   a += b; d = std::rotl(d ^ a, 8); \ | 
| 21 |  |   c += d; b = std::rotl(b ^ c, 7); | 
| 22 |  |  | 
| 23 | 284k | #define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0) | 
| 24 |  |  | 
| 25 |  | void ChaCha20Aligned::SetKey(std::span<const std::byte> key) noexcept | 
| 26 | 380k | { | 
| 27 | 380k |     assert(key.size() == KEYLEN); | 
| 28 | 380k |     input[0] = ReadLE32(key.data() + 0); | 
| 29 | 380k |     input[1] = ReadLE32(key.data() + 4); | 
| 30 | 380k |     input[2] = ReadLE32(key.data() + 8); | 
| 31 | 380k |     input[3] = ReadLE32(key.data() + 12); | 
| 32 | 380k |     input[4] = ReadLE32(key.data() + 16); | 
| 33 | 380k |     input[5] = ReadLE32(key.data() + 20); | 
| 34 | 380k |     input[6] = ReadLE32(key.data() + 24); | 
| 35 | 380k |     input[7] = ReadLE32(key.data() + 28); | 
| 36 | 380k |     input[8] = 0; | 
| 37 | 380k |     input[9] = 0; | 
| 38 | 380k |     input[10] = 0; | 
| 39 | 380k |     input[11] = 0; | 
| 40 | 380k | } | 
| 41 |  |  | 
| 42 |  | ChaCha20Aligned::~ChaCha20Aligned() | 
| 43 | 191k | { | 
| 44 | 191k |     memory_cleanse(input, sizeof(input)); | 
| 45 | 191k | } | 
| 46 |  |  | 
| 47 |  | ChaCha20Aligned::ChaCha20Aligned(std::span<const std::byte> key) noexcept | 
| 48 | 191k | { | 
| 49 | 191k |     SetKey(key); | 
| 50 | 191k | } | 
| 51 |  |  | 
| 52 |  | void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept | 
| 53 | 0 | { | 
| 54 | 0 |     input[8] = block_counter; | 
| 55 | 0 |     input[9] = nonce.first; | 
| 56 | 0 |     input[10] = nonce.second; | 
| 57 | 0 |     input[11] = nonce.second >> 32; | 
| 58 | 0 | } | 
| 59 |  |  | 
| 60 |  | inline void ChaCha20Aligned::Keystream(std::span<std::byte> output) noexcept | 
| 61 | 284k | { | 
| 62 | 284k |     std::byte* c = output.data(); | 
| 63 | 284k |     size_t blocks = output.size() / BLOCKLEN; | 
| 64 | 284k |     assert(blocks * BLOCKLEN == output.size()); | 
| 65 |  |  | 
| 66 | 284k |     uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; | 
| 67 | 284k |     uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; | 
| 68 |  |  | 
| 69 | 284k |     if (!blocks) return; | 
| 70 |  |  | 
| 71 | 284k |     j4 = input[0]; | 
| 72 | 284k |     j5 = input[1]; | 
| 73 | 284k |     j6 = input[2]; | 
| 74 | 284k |     j7 = input[3]; | 
| 75 | 284k |     j8 = input[4]; | 
| 76 | 284k |     j9 = input[5]; | 
| 77 | 284k |     j10 = input[6]; | 
| 78 | 284k |     j11 = input[7]; | 
| 79 | 284k |     j12 = input[8]; | 
| 80 | 284k |     j13 = input[9]; | 
| 81 | 284k |     j14 = input[10]; | 
| 82 | 284k |     j15 = input[11]; | 
| 83 |  |  | 
| 84 | 284k |     for (;;) { | 
| 85 | 284k |         x0 = 0x61707865; | 
| 86 | 284k |         x1 = 0x3320646e; | 
| 87 | 284k |         x2 = 0x79622d32; | 
| 88 | 284k |         x3 = 0x6b206574; | 
| 89 | 284k |         x4 = j4; | 
| 90 | 284k |         x5 = j5; | 
| 91 | 284k |         x6 = j6; | 
| 92 | 284k |         x7 = j7; | 
| 93 | 284k |         x8 = j8; | 
| 94 | 284k |         x9 = j9; | 
| 95 | 284k |         x10 = j10; | 
| 96 | 284k |         x11 = j11; | 
| 97 | 284k |         x12 = j12; | 
| 98 | 284k |         x13 = j13; | 
| 99 | 284k |         x14 = j14; | 
| 100 | 284k |         x15 = j15; | 
| 101 |  |  | 
| 102 |  |         // The 20 inner ChaCha20 rounds are unrolled here for performance. | 
| 103 | 284k |         REPEAT10( | 
| 104 | 284k |             QUARTERROUND( x0, x4, x8,x12); | 
| 105 | 284k |             QUARTERROUND( x1, x5, x9,x13); | 
| 106 | 284k |             QUARTERROUND( x2, x6,x10,x14); | 
| 107 | 284k |             QUARTERROUND( x3, x7,x11,x15); | 
| 108 | 284k |             QUARTERROUND( x0, x5,x10,x15); | 
| 109 | 284k |             QUARTERROUND( x1, x6,x11,x12); | 
| 110 | 284k |             QUARTERROUND( x2, x7, x8,x13); | 
| 111 | 284k |             QUARTERROUND( x3, x4, x9,x14); | 
| 112 | 284k |         ); | 
| 113 |  |  | 
| 114 | 284k |         x0 += 0x61707865; | 
| 115 | 284k |         x1 += 0x3320646e; | 
| 116 | 284k |         x2 += 0x79622d32; | 
| 117 | 284k |         x3 += 0x6b206574; | 
| 118 | 284k |         x4 += j4; | 
| 119 | 284k |         x5 += j5; | 
| 120 | 284k |         x6 += j6; | 
| 121 | 284k |         x7 += j7; | 
| 122 | 284k |         x8 += j8; | 
| 123 | 284k |         x9 += j9; | 
| 124 | 284k |         x10 += j10; | 
| 125 | 284k |         x11 += j11; | 
| 126 | 284k |         x12 += j12; | 
| 127 | 284k |         x13 += j13; | 
| 128 | 284k |         x14 += j14; | 
| 129 | 284k |         x15 += j15; | 
| 130 |  |  | 
| 131 | 284k |         ++j12; | 
| 132 | 284k |         if (!j12) ++j13; | 
| 133 |  |  | 
| 134 | 284k |         WriteLE32(c + 0, x0); | 
| 135 | 284k |         WriteLE32(c + 4, x1); | 
| 136 | 284k |         WriteLE32(c + 8, x2); | 
| 137 | 284k |         WriteLE32(c + 12, x3); | 
| 138 | 284k |         WriteLE32(c + 16, x4); | 
| 139 | 284k |         WriteLE32(c + 20, x5); | 
| 140 | 284k |         WriteLE32(c + 24, x6); | 
| 141 | 284k |         WriteLE32(c + 28, x7); | 
| 142 | 284k |         WriteLE32(c + 32, x8); | 
| 143 | 284k |         WriteLE32(c + 36, x9); | 
| 144 | 284k |         WriteLE32(c + 40, x10); | 
| 145 | 284k |         WriteLE32(c + 44, x11); | 
| 146 | 284k |         WriteLE32(c + 48, x12); | 
| 147 | 284k |         WriteLE32(c + 52, x13); | 
| 148 | 284k |         WriteLE32(c + 56, x14); | 
| 149 | 284k |         WriteLE32(c + 60, x15); | 
| 150 |  |  | 
| 151 | 284k |         if (blocks == 1) { | 
| 152 | 284k |             input[8] = j12; | 
| 153 | 284k |             input[9] = j13; | 
| 154 | 284k |             return; | 
| 155 | 284k |         } | 
| 156 | 0 |         blocks -= 1; | 
| 157 | 0 |         c += BLOCKLEN; | 
| 158 | 0 |     } | 
| 159 | 284k | } | 
| 160 |  |  | 
| 161 |  | inline void ChaCha20Aligned::Crypt(std::span<const std::byte> in_bytes, std::span<std::byte> out_bytes) noexcept | 
| 162 | 0 | { | 
| 163 | 0 |     assert(in_bytes.size() == out_bytes.size()); | 
| 164 | 0 |     const std::byte* m = in_bytes.data(); | 
| 165 | 0 |     std::byte* c = out_bytes.data(); | 
| 166 | 0 |     size_t blocks = out_bytes.size() / BLOCKLEN; | 
| 167 | 0 |     assert(blocks * BLOCKLEN == out_bytes.size()); | 
| 168 |  |  | 
| 169 | 0 |     uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; | 
| 170 | 0 |     uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; | 
| 171 |  | 
 | 
| 172 | 0 |     if (!blocks) return; | 
| 173 |  |  | 
| 174 | 0 |     j4 = input[0]; | 
| 175 | 0 |     j5 = input[1]; | 
| 176 | 0 |     j6 = input[2]; | 
| 177 | 0 |     j7 = input[3]; | 
| 178 | 0 |     j8 = input[4]; | 
| 179 | 0 |     j9 = input[5]; | 
| 180 | 0 |     j10 = input[6]; | 
| 181 | 0 |     j11 = input[7]; | 
| 182 | 0 |     j12 = input[8]; | 
| 183 | 0 |     j13 = input[9]; | 
| 184 | 0 |     j14 = input[10]; | 
| 185 | 0 |     j15 = input[11]; | 
| 186 |  | 
 | 
| 187 | 0 |     for (;;) { | 
| 188 | 0 |         x0 = 0x61707865; | 
| 189 | 0 |         x1 = 0x3320646e; | 
| 190 | 0 |         x2 = 0x79622d32; | 
| 191 | 0 |         x3 = 0x6b206574; | 
| 192 | 0 |         x4 = j4; | 
| 193 | 0 |         x5 = j5; | 
| 194 | 0 |         x6 = j6; | 
| 195 | 0 |         x7 = j7; | 
| 196 | 0 |         x8 = j8; | 
| 197 | 0 |         x9 = j9; | 
| 198 | 0 |         x10 = j10; | 
| 199 | 0 |         x11 = j11; | 
| 200 | 0 |         x12 = j12; | 
| 201 | 0 |         x13 = j13; | 
| 202 | 0 |         x14 = j14; | 
| 203 | 0 |         x15 = j15; | 
| 204 |  |  | 
| 205 |  |         // The 20 inner ChaCha20 rounds are unrolled here for performance. | 
| 206 | 0 |         REPEAT10( | 
| 207 | 0 |             QUARTERROUND( x0, x4, x8,x12); | 
| 208 | 0 |             QUARTERROUND( x1, x5, x9,x13); | 
| 209 | 0 |             QUARTERROUND( x2, x6,x10,x14); | 
| 210 | 0 |             QUARTERROUND( x3, x7,x11,x15); | 
| 211 | 0 |             QUARTERROUND( x0, x5,x10,x15); | 
| 212 | 0 |             QUARTERROUND( x1, x6,x11,x12); | 
| 213 | 0 |             QUARTERROUND( x2, x7, x8,x13); | 
| 214 | 0 |             QUARTERROUND( x3, x4, x9,x14); | 
| 215 | 0 |         ); | 
| 216 |  | 
 | 
| 217 | 0 |         x0 += 0x61707865; | 
| 218 | 0 |         x1 += 0x3320646e; | 
| 219 | 0 |         x2 += 0x79622d32; | 
| 220 | 0 |         x3 += 0x6b206574; | 
| 221 | 0 |         x4 += j4; | 
| 222 | 0 |         x5 += j5; | 
| 223 | 0 |         x6 += j6; | 
| 224 | 0 |         x7 += j7; | 
| 225 | 0 |         x8 += j8; | 
| 226 | 0 |         x9 += j9; | 
| 227 | 0 |         x10 += j10; | 
| 228 | 0 |         x11 += j11; | 
| 229 | 0 |         x12 += j12; | 
| 230 | 0 |         x13 += j13; | 
| 231 | 0 |         x14 += j14; | 
| 232 | 0 |         x15 += j15; | 
| 233 |  | 
 | 
| 234 | 0 |         x0 ^= ReadLE32(m + 0); | 
| 235 | 0 |         x1 ^= ReadLE32(m + 4); | 
| 236 | 0 |         x2 ^= ReadLE32(m + 8); | 
| 237 | 0 |         x3 ^= ReadLE32(m + 12); | 
| 238 | 0 |         x4 ^= ReadLE32(m + 16); | 
| 239 | 0 |         x5 ^= ReadLE32(m + 20); | 
| 240 | 0 |         x6 ^= ReadLE32(m + 24); | 
| 241 | 0 |         x7 ^= ReadLE32(m + 28); | 
| 242 | 0 |         x8 ^= ReadLE32(m + 32); | 
| 243 | 0 |         x9 ^= ReadLE32(m + 36); | 
| 244 | 0 |         x10 ^= ReadLE32(m + 40); | 
| 245 | 0 |         x11 ^= ReadLE32(m + 44); | 
| 246 | 0 |         x12 ^= ReadLE32(m + 48); | 
| 247 | 0 |         x13 ^= ReadLE32(m + 52); | 
| 248 | 0 |         x14 ^= ReadLE32(m + 56); | 
| 249 | 0 |         x15 ^= ReadLE32(m + 60); | 
| 250 |  | 
 | 
| 251 | 0 |         ++j12; | 
| 252 | 0 |         if (!j12) ++j13; | 
| 253 |  | 
 | 
| 254 | 0 |         WriteLE32(c + 0, x0); | 
| 255 | 0 |         WriteLE32(c + 4, x1); | 
| 256 | 0 |         WriteLE32(c + 8, x2); | 
| 257 | 0 |         WriteLE32(c + 12, x3); | 
| 258 | 0 |         WriteLE32(c + 16, x4); | 
| 259 | 0 |         WriteLE32(c + 20, x5); | 
| 260 | 0 |         WriteLE32(c + 24, x6); | 
| 261 | 0 |         WriteLE32(c + 28, x7); | 
| 262 | 0 |         WriteLE32(c + 32, x8); | 
| 263 | 0 |         WriteLE32(c + 36, x9); | 
| 264 | 0 |         WriteLE32(c + 40, x10); | 
| 265 | 0 |         WriteLE32(c + 44, x11); | 
| 266 | 0 |         WriteLE32(c + 48, x12); | 
| 267 | 0 |         WriteLE32(c + 52, x13); | 
| 268 | 0 |         WriteLE32(c + 56, x14); | 
| 269 | 0 |         WriteLE32(c + 60, x15); | 
| 270 |  | 
 | 
| 271 | 0 |         if (blocks == 1) { | 
| 272 | 0 |             input[8] = j12; | 
| 273 | 0 |             input[9] = j13; | 
| 274 | 0 |             return; | 
| 275 | 0 |         } | 
| 276 | 0 |         blocks -= 1; | 
| 277 | 0 |         c += BLOCKLEN; | 
| 278 | 0 |         m += BLOCKLEN; | 
| 279 | 0 |     } | 
| 280 | 0 | } | 
| 281 |  |  | 
| 282 |  | void ChaCha20::Keystream(std::span<std::byte> out) noexcept | 
| 283 | 379k | { | 
| 284 | 379k |     if (out.empty()) return; | 
| 285 | 379k |     if (m_bufleft) { | 
| 286 | 94.7k |         unsigned reuse = std::min<size_t>(m_bufleft, out.size()); | 
| 287 | 94.7k |         std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin()); | 
| 288 | 94.7k |         m_bufleft -= reuse; | 
| 289 | 94.7k |         out = out.subspan(reuse); | 
| 290 | 94.7k |     } | 
| 291 | 379k |     if (out.size() >= m_aligned.BLOCKLEN) { | 
| 292 | 0 |         size_t blocks = out.size() / m_aligned.BLOCKLEN; | 
| 293 | 0 |         m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN)); | 
| 294 | 0 |         out = out.subspan(blocks * m_aligned.BLOCKLEN); | 
| 295 | 0 |     } | 
| 296 | 379k |     if (!out.empty()) { | 
| 297 | 284k |         m_aligned.Keystream(m_buffer); | 
| 298 | 284k |         std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin()); | 
| 299 | 284k |         m_bufleft = m_aligned.BLOCKLEN - out.size(); | 
| 300 | 284k |     } | 
| 301 | 379k | } | 
| 302 |  |  | 
| 303 |  | void ChaCha20::Crypt(std::span<const std::byte> input, std::span<std::byte> output) noexcept | 
| 304 | 0 | { | 
| 305 | 0 |     assert(input.size() == output.size()); | 
| 306 |  |  | 
| 307 | 0 |     if (!input.size()) return; | 
| 308 | 0 |     if (m_bufleft) { | 
| 309 | 0 |         unsigned reuse = std::min<size_t>(m_bufleft, input.size()); | 
| 310 | 0 |         for (unsigned i = 0; i < reuse; i++) { | 
| 311 | 0 |             output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i]; | 
| 312 | 0 |         } | 
| 313 | 0 |         m_bufleft -= reuse; | 
| 314 | 0 |         output = output.subspan(reuse); | 
| 315 | 0 |         input = input.subspan(reuse); | 
| 316 | 0 |     } | 
| 317 | 0 |     if (input.size() >= m_aligned.BLOCKLEN) { | 
| 318 | 0 |         size_t blocks = input.size() / m_aligned.BLOCKLEN; | 
| 319 | 0 |         m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN)); | 
| 320 | 0 |         output = output.subspan(blocks * m_aligned.BLOCKLEN); | 
| 321 | 0 |         input = input.subspan(blocks * m_aligned.BLOCKLEN); | 
| 322 | 0 |     } | 
| 323 | 0 |     if (!input.empty()) { | 
| 324 | 0 |         m_aligned.Keystream(m_buffer); | 
| 325 | 0 |         for (unsigned i = 0; i < input.size(); i++) { | 
| 326 | 0 |             output[i] = input[i] ^ m_buffer[i]; | 
| 327 | 0 |         } | 
| 328 | 0 |         m_bufleft = m_aligned.BLOCKLEN - input.size(); | 
| 329 | 0 |     } | 
| 330 | 0 | } | 
| 331 |  |  | 
| 332 |  | ChaCha20::~ChaCha20() | 
| 333 | 191k | { | 
| 334 | 191k |     memory_cleanse(m_buffer.data(), m_buffer.size()); | 
| 335 | 191k | } | 
| 336 |  |  | 
| 337 |  | void ChaCha20::SetKey(std::span<const std::byte> key) noexcept | 
| 338 | 189k | { | 
| 339 | 189k |     m_aligned.SetKey(key); | 
| 340 | 189k |     m_bufleft = 0; | 
| 341 | 189k |     memory_cleanse(m_buffer.data(), m_buffer.size()); | 
| 342 | 189k | } | 
| 343 |  |  | 
| 344 |  | FSChaCha20::FSChaCha20(std::span<const std::byte> key, uint32_t rekey_interval) noexcept : | 
| 345 | 0 |     m_chacha20(key), m_rekey_interval(rekey_interval) | 
| 346 | 0 | { | 
| 347 | 0 |     assert(key.size() == KEYLEN); | 
| 348 | 0 | } | 
| 349 |  |  | 
| 350 |  | void FSChaCha20::Crypt(std::span<const std::byte> input, std::span<std::byte> output) noexcept | 
| 351 | 0 | { | 
| 352 | 0 |     assert(input.size() == output.size()); | 
| 353 |  |  | 
| 354 |  |     // Invoke internal stream cipher for actual encryption/decryption. | 
| 355 | 0 |     m_chacha20.Crypt(input, output); | 
| 356 |  |  | 
| 357 |  |     // Rekey after m_rekey_interval encryptions/decryptions. | 
| 358 | 0 |     if (++m_chunk_counter == m_rekey_interval) { | 
| 359 |  |         // Get new key from the stream cipher. | 
| 360 | 0 |         std::byte new_key[KEYLEN]; | 
| 361 | 0 |         m_chacha20.Keystream(new_key); | 
| 362 |  |         // Update its key. | 
| 363 | 0 |         m_chacha20.SetKey(new_key); | 
| 364 |  |         // Wipe the key (a copy remains inside m_chacha20, where it'll be wiped on the next rekey | 
| 365 |  |         // or on destruction). | 
| 366 | 0 |         memory_cleanse(new_key, sizeof(new_key)); | 
| 367 |  |         // Set the nonce for the new section of output. | 
| 368 | 0 |         m_chacha20.Seek({0, ++m_rekey_counter}, 0); | 
| 369 |  |         // Reset the chunk counter. | 
| 370 | 0 |         m_chunk_counter = 0; | 
| 371 | 0 |     } | 
| 372 | 0 | } |