LCOV - code coverage report
Current view: top level - src/crypto - chacha20.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 269 269 100.0 %
Date: 2026-06-25 07:23:43 Functions: 16 16 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2017-2019 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 <string.h>
      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             : #define REPEAT10(a) do { {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; {a}; } while(0)
      24             : 
      25     9623817 : void ChaCha20Aligned::SetKey(Span<const std::byte> key) noexcept
      26             : {
      27     9624007 :     assert(key.size() == KEYLEN);
      28     9623817 :     input[0] = ReadLE32(UCharCast(key.data() + 0));
      29     9623791 :     input[1] = ReadLE32(UCharCast(key.data() + 4));
      30     9623809 :     input[2] = ReadLE32(UCharCast(key.data() + 8));
      31     9623803 :     input[3] = ReadLE32(UCharCast(key.data() + 12));
      32     9623812 :     input[4] = ReadLE32(UCharCast(key.data() + 16));
      33     9623814 :     input[5] = ReadLE32(UCharCast(key.data() + 20));
      34     9623818 :     input[6] = ReadLE32(UCharCast(key.data() + 24));
      35     9623825 :     input[7] = ReadLE32(UCharCast(key.data() + 28));
      36     9623729 :     input[8] = 0;
      37     9623729 :     input[9] = 0;
      38     9623729 :     input[10] = 0;
      39     9623729 :     input[11] = 0;
      40     9623729 : }
      41             : 
      42    10303651 : ChaCha20Aligned::~ChaCha20Aligned()
      43     5151825 : {
      44     5151826 :     memory_cleanse(input, sizeof(input));
      45    10303651 : }
      46             : 
      47    10304223 : ChaCha20Aligned::ChaCha20Aligned(Span<const std::byte> key) noexcept
      48     5152112 : {
      49     5152111 :     SetKey(key);
      50    10304223 : }
      51             : 
      52     2081983 : void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept
      53             : {
      54     2081983 :     input[8] = block_counter;
      55     2081983 :     input[9] = nonce.first;
      56     2081983 :     input[10] = nonce.second;
      57     2081983 :     input[11] = nonce.second >> 32;
      58     2081983 : }
      59             : 
      60     8707174 : inline void ChaCha20Aligned::Keystream(Span<std::byte> output) noexcept
      61             : {
      62     8707191 :     unsigned char* c = UCharCast(output.data());
      63     8707157 :     size_t blocks = output.size() / BLOCKLEN;
      64     8707157 :     assert(blocks * BLOCKLEN == output.size());
      65             : 
      66             :     uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
      67             :     uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
      68             : 
      69     8707157 :     if (!blocks) return;
      70             : 
      71     8707157 :     j4 = input[0];
      72     8707157 :     j5 = input[1];
      73     8707157 :     j6 = input[2];
      74     8707157 :     j7 = input[3];
      75     8707157 :     j8 = input[4];
      76     8707157 :     j9 = input[5];
      77     8707157 :     j10 = input[6];
      78     8707157 :     j11 = input[7];
      79     8707157 :     j12 = input[8];
      80     8707157 :     j13 = input[9];
      81     8707157 :     j14 = input[10];
      82     8707157 :     j15 = input[11];
      83             : 
      84    10881933 :     for (;;) {
      85    10881933 :         x0 = 0x61707865;
      86    10881933 :         x1 = 0x3320646e;
      87    10881933 :         x2 = 0x79622d32;
      88    10881933 :         x3 = 0x6b206574;
      89    10881933 :         x4 = j4;
      90    10881933 :         x5 = j5;
      91    10881933 :         x6 = j6;
      92    10881933 :         x7 = j7;
      93    10881933 :         x8 = j8;
      94    10881933 :         x9 = j9;
      95    10881933 :         x10 = j10;
      96    10881933 :         x11 = j11;
      97    10881933 :         x12 = j12;
      98    10881933 :         x13 = j13;
      99    10881933 :         x14 = j14;
     100    10881933 :         x15 = j15;
     101             : 
     102             :         // The 20 inner ChaCha20 rounds are unrolled here for performance.
     103    10881933 :         REPEAT10(
     104             :             QUARTERROUND( x0, x4, x8,x12);
     105             :             QUARTERROUND( x1, x5, x9,x13);
     106             :             QUARTERROUND( x2, x6,x10,x14);
     107             :             QUARTERROUND( x3, x7,x11,x15);
     108             :             QUARTERROUND( x0, x5,x10,x15);
     109             :             QUARTERROUND( x1, x6,x11,x12);
     110             :             QUARTERROUND( x2, x7, x8,x13);
     111             :             QUARTERROUND( x3, x4, x9,x14);
     112             :         );
     113             : 
     114    10881933 :         x0 += 0x61707865;
     115    10881933 :         x1 += 0x3320646e;
     116    10881933 :         x2 += 0x79622d32;
     117    10881933 :         x3 += 0x6b206574;
     118    10881933 :         x4 += j4;
     119    10881933 :         x5 += j5;
     120    10881933 :         x6 += j6;
     121    10881933 :         x7 += j7;
     122    10881933 :         x8 += j8;
     123    10881933 :         x9 += j9;
     124    10881933 :         x10 += j10;
     125    10881933 :         x11 += j11;
     126    10881933 :         x12 += j12;
     127    10881933 :         x13 += j13;
     128    10881933 :         x14 += j14;
     129    10881933 :         x15 += j15;
     130             : 
     131    10881933 :         ++j12;
     132    10881933 :         if (!j12) ++j13;
     133             : 
     134    10881933 :         WriteLE32(c + 0, x0);
     135    10881933 :         WriteLE32(c + 4, x1);
     136    10881933 :         WriteLE32(c + 8, x2);
     137    10881936 :         WriteLE32(c + 12, x3);
     138    10881936 :         WriteLE32(c + 16, x4);
     139    10881937 :         WriteLE32(c + 20, x5);
     140    10881937 :         WriteLE32(c + 24, x6);
     141    10881939 :         WriteLE32(c + 28, x7);
     142    10881937 :         WriteLE32(c + 32, x8);
     143    10881940 :         WriteLE32(c + 36, x9);
     144    10881937 :         WriteLE32(c + 40, x10);
     145    10881938 :         WriteLE32(c + 44, x11);
     146    10881938 :         WriteLE32(c + 48, x12);
     147    10881937 :         WriteLE32(c + 52, x13);
     148    10881937 :         WriteLE32(c + 56, x14);
     149    10881938 :         WriteLE32(c + 60, x15);
     150             : 
     151    10881938 :         if (blocks == 1) {
     152     8707162 :             input[8] = j12;
     153     8707162 :             input[9] = j13;
     154     8707162 :             return;
     155             :         }
     156     2174776 :         blocks -= 1;
     157     2174776 :         c += BLOCKLEN;
     158             :     }
     159     8707162 : }
     160             : 
     161       98122 : inline void ChaCha20Aligned::Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept
     162             : {
     163       98122 :     assert(in_bytes.size() == out_bytes.size());
     164       98122 :     const unsigned char* m = UCharCast(in_bytes.data());
     165       98122 :     unsigned char* c = UCharCast(out_bytes.data());
     166       98122 :     size_t blocks = out_bytes.size() / BLOCKLEN;
     167       98122 :     assert(blocks * BLOCKLEN == out_bytes.size());
     168             : 
     169             :     uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
     170             :     uint32_t j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
     171             : 
     172       98122 :     if (!blocks) return;
     173             : 
     174       98122 :     j4 = input[0];
     175       98122 :     j5 = input[1];
     176       98122 :     j6 = input[2];
     177       98122 :     j7 = input[3];
     178       98122 :     j8 = input[4];
     179       98122 :     j9 = input[5];
     180       98122 :     j10 = input[6];
     181       98122 :     j11 = input[7];
     182       98122 :     j12 = input[8];
     183       98122 :     j13 = input[9];
     184       98122 :     j14 = input[10];
     185       98122 :     j15 = input[11];
     186             : 
     187     4873021 :     for (;;) {
     188     4873021 :         x0 = 0x61707865;
     189     4873021 :         x1 = 0x3320646e;
     190     4873021 :         x2 = 0x79622d32;
     191     4873021 :         x3 = 0x6b206574;
     192     4873021 :         x4 = j4;
     193     4873021 :         x5 = j5;
     194     4873021 :         x6 = j6;
     195     4873021 :         x7 = j7;
     196     4873021 :         x8 = j8;
     197     4873021 :         x9 = j9;
     198     4873021 :         x10 = j10;
     199     4873021 :         x11 = j11;
     200     4873021 :         x12 = j12;
     201     4873021 :         x13 = j13;
     202     4873021 :         x14 = j14;
     203     4873021 :         x15 = j15;
     204             : 
     205             :         // The 20 inner ChaCha20 rounds are unrolled here for performance.
     206     4873021 :         REPEAT10(
     207             :             QUARTERROUND( x0, x4, x8,x12);
     208             :             QUARTERROUND( x1, x5, x9,x13);
     209             :             QUARTERROUND( x2, x6,x10,x14);
     210             :             QUARTERROUND( x3, x7,x11,x15);
     211             :             QUARTERROUND( x0, x5,x10,x15);
     212             :             QUARTERROUND( x1, x6,x11,x12);
     213             :             QUARTERROUND( x2, x7, x8,x13);
     214             :             QUARTERROUND( x3, x4, x9,x14);
     215             :         );
     216             : 
     217     4873021 :         x0 += 0x61707865;
     218     4873021 :         x1 += 0x3320646e;
     219     4873021 :         x2 += 0x79622d32;
     220     4873021 :         x3 += 0x6b206574;
     221     4873021 :         x4 += j4;
     222     4873021 :         x5 += j5;
     223     4873021 :         x6 += j6;
     224     4873021 :         x7 += j7;
     225     4873021 :         x8 += j8;
     226     4873021 :         x9 += j9;
     227     4873021 :         x10 += j10;
     228     4873021 :         x11 += j11;
     229     4873021 :         x12 += j12;
     230     4873021 :         x13 += j13;
     231     4873021 :         x14 += j14;
     232     4873021 :         x15 += j15;
     233             : 
     234     4873021 :         x0 ^= ReadLE32(m + 0);
     235     4873021 :         x1 ^= ReadLE32(m + 4);
     236     4873021 :         x2 ^= ReadLE32(m + 8);
     237     4873021 :         x3 ^= ReadLE32(m + 12);
     238     4873021 :         x4 ^= ReadLE32(m + 16);
     239     4873021 :         x5 ^= ReadLE32(m + 20);
     240     4873021 :         x6 ^= ReadLE32(m + 24);
     241     4873021 :         x7 ^= ReadLE32(m + 28);
     242     4873021 :         x8 ^= ReadLE32(m + 32);
     243     4873021 :         x9 ^= ReadLE32(m + 36);
     244     4873021 :         x10 ^= ReadLE32(m + 40);
     245     4873021 :         x11 ^= ReadLE32(m + 44);
     246     4873021 :         x12 ^= ReadLE32(m + 48);
     247     4873021 :         x13 ^= ReadLE32(m + 52);
     248     4873021 :         x14 ^= ReadLE32(m + 56);
     249     4873021 :         x15 ^= ReadLE32(m + 60);
     250             : 
     251     4873021 :         ++j12;
     252     4873021 :         if (!j12) ++j13;
     253             : 
     254     4873021 :         WriteLE32(c + 0, x0);
     255     4873021 :         WriteLE32(c + 4, x1);
     256     4873021 :         WriteLE32(c + 8, x2);
     257     4873021 :         WriteLE32(c + 12, x3);
     258     4873021 :         WriteLE32(c + 16, x4);
     259     4873021 :         WriteLE32(c + 20, x5);
     260     4873021 :         WriteLE32(c + 24, x6);
     261     4873021 :         WriteLE32(c + 28, x7);
     262     4873021 :         WriteLE32(c + 32, x8);
     263     4873021 :         WriteLE32(c + 36, x9);
     264     4873021 :         WriteLE32(c + 40, x10);
     265     4873021 :         WriteLE32(c + 44, x11);
     266     4873021 :         WriteLE32(c + 48, x12);
     267     4873021 :         WriteLE32(c + 52, x13);
     268     4873021 :         WriteLE32(c + 56, x14);
     269     4873021 :         WriteLE32(c + 60, x15);
     270             : 
     271     4873021 :         if (blocks == 1) {
     272       98122 :             input[8] = j12;
     273       98122 :             input[9] = j13;
     274       98122 :             return;
     275             :         }
     276     4774899 :         blocks -= 1;
     277     4774899 :         c += BLOCKLEN;
     278     4774899 :         m += BLOCKLEN;
     279             :     }
     280       98122 : }
     281             : 
     282    24363258 : void ChaCha20::Keystream(Span<std::byte> out) noexcept
     283             : {
     284    24363258 :     if (out.empty()) return;
     285    24363001 :     if (m_bufleft) {
     286    16106460 :         unsigned reuse = std::min<size_t>(m_bufleft, out.size());
     287    16106454 :         std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin());
     288    16106454 :         m_bufleft -= reuse;
     289    16106454 :         out = out.subspan(reuse);
     290    16106454 :     }
     291    24362998 :     if (out.size() >= m_aligned.BLOCKLEN) {
     292     1453597 :         size_t blocks = out.size() / m_aligned.BLOCKLEN;
     293     1453597 :         m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN));
     294     1453597 :         out = out.subspan(blocks * m_aligned.BLOCKLEN);
     295     1453597 :     }
     296    24362998 :     if (!out.empty()) {
     297     6961172 :         m_aligned.Keystream(m_buffer);
     298     6961172 :         std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin());
     299     6961169 :         m_bufleft = m_aligned.BLOCKLEN - out.size();
     300     6961169 :     }
     301    24363252 : }
     302             : 
     303     1753262 : void ChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
     304             : {
     305     1753262 :     assert(input.size() == output.size());
     306             : 
     307     1753262 :     if (!input.size()) return;
     308      498635 :     if (m_bufleft) {
     309      319117 :         unsigned reuse = std::min<size_t>(m_bufleft, input.size());
     310     8261384 :         for (unsigned i = 0; i < reuse; i++) {
     311     7942267 :             output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i];
     312     7942267 :         }
     313      319117 :         m_bufleft -= reuse;
     314      319117 :         output = output.subspan(reuse);
     315      319117 :         input = input.subspan(reuse);
     316      319117 :     }
     317      498635 :     if (input.size() >= m_aligned.BLOCKLEN) {
     318       98122 :         size_t blocks = input.size() / m_aligned.BLOCKLEN;
     319       98122 :         m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN));
     320       98122 :         output = output.subspan(blocks * m_aligned.BLOCKLEN);
     321       98122 :         input = input.subspan(blocks * m_aligned.BLOCKLEN);
     322       98122 :     }
     323      498635 :     if (!input.empty()) {
     324      289140 :         m_aligned.Keystream(m_buffer);
     325     3897483 :         for (unsigned i = 0; i < input.size(); i++) {
     326     3608343 :             output[i] = input[i] ^ m_buffer[i];
     327     3608343 :         }
     328      289140 :         m_bufleft = m_aligned.BLOCKLEN - input.size();
     329      289140 :     }
     330     1753262 : }
     331             : 
     332    10297145 : ChaCha20::~ChaCha20()
     333     5148570 : {
     334     5148575 :     memory_cleanse(m_buffer.data(), m_buffer.size());
     335    10297145 : }
     336             : 
     337     4471724 : void ChaCha20::SetKey(Span<const std::byte> key) noexcept
     338             : {
     339     4471724 :     m_aligned.SetKey(key);
     340     4471724 :     m_bufleft = 0;
     341     4471724 :     memory_cleanse(m_buffer.data(), m_buffer.size());
     342     4471724 : }
     343             : 
     344        2130 : FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
     345        1065 :     m_chacha20(key), m_rekey_interval(rekey_interval)
     346        1065 : {
     347             :     assert(key.size() == KEYLEN);
     348        1065 : }
     349             : 
     350      196658 : void FSChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
     351             : {
     352      196658 :     assert(input.size() == output.size());
     353             : 
     354             :     // Invoke internal stream cipher for actual encryption/decryption.
     355      196658 :     m_chacha20.Crypt(input, output);
     356             : 
     357             :     // Rekey after m_rekey_interval encryptions/decryptions.
     358      196658 :     if (++m_chunk_counter == m_rekey_interval) {
     359             :         // Get new key from the stream cipher.
     360             :         std::byte new_key[KEYLEN];
     361         731 :         m_chacha20.Keystream(new_key);
     362             :         // Update its key.
     363         731 :         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         731 :         memory_cleanse(new_key, sizeof(new_key));
     367             :         // Set the nonce for the new section of output.
     368         731 :         m_chacha20.Seek({0, ++m_rekey_counter}, 0);
     369             :         // Reset the chunk counter.
     370         731 :         m_chunk_counter = 0;
     371         731 :     }
     372      196658 : }

Generated by: LCOV version 1.16