LCOV - code coverage report
Current view: top level - src/crypto - chacha20.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 269 269 100.0 %
Date: 2026-06-25 07:23:51 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      965586 : void ChaCha20Aligned::SetKey(Span<const std::byte> key) noexcept
      26             : {
      27      965586 :     assert(key.size() == KEYLEN);
      28      965586 :     input[0] = ReadLE32(UCharCast(key.data() + 0));
      29      965586 :     input[1] = ReadLE32(UCharCast(key.data() + 4));
      30      965586 :     input[2] = ReadLE32(UCharCast(key.data() + 8));
      31      965586 :     input[3] = ReadLE32(UCharCast(key.data() + 12));
      32      965586 :     input[4] = ReadLE32(UCharCast(key.data() + 16));
      33      965586 :     input[5] = ReadLE32(UCharCast(key.data() + 20));
      34      965586 :     input[6] = ReadLE32(UCharCast(key.data() + 24));
      35      965586 :     input[7] = ReadLE32(UCharCast(key.data() + 28));
      36      965586 :     input[8] = 0;
      37      965586 :     input[9] = 0;
      38      965586 :     input[10] = 0;
      39      965586 :     input[11] = 0;
      40      965586 : }
      41             : 
      42      989896 : ChaCha20Aligned::~ChaCha20Aligned()
      43      494948 : {
      44      494948 :     memory_cleanse(input, sizeof(input));
      45      989896 : }
      46             : 
      47      990476 : ChaCha20Aligned::ChaCha20Aligned(Span<const std::byte> key) noexcept
      48      495238 : {
      49      495238 :     SetKey(key);
      50      990476 : }
      51             : 
      52     2038081 : void ChaCha20Aligned::Seek(Nonce96 nonce, uint32_t block_counter) noexcept
      53             : {
      54     2038081 :     input[8] = block_counter;
      55     2038081 :     input[9] = nonce.first;
      56     2038081 :     input[10] = nonce.second;
      57     2038081 :     input[11] = nonce.second >> 32;
      58     2038081 : }
      59             : 
      60     4358719 : inline void ChaCha20Aligned::Keystream(Span<std::byte> output) noexcept
      61             : {
      62     4358719 :     unsigned char* c = UCharCast(output.data());
      63     4358719 :     size_t blocks = output.size() / BLOCKLEN;
      64     4358719 :     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     4358719 :     if (!blocks) return;
      70             : 
      71     4358719 :     j4 = input[0];
      72     4358719 :     j5 = input[1];
      73     4358719 :     j6 = input[2];
      74     4358719 :     j7 = input[3];
      75     4358719 :     j8 = input[4];
      76     4358719 :     j9 = input[5];
      77     4358719 :     j10 = input[6];
      78     4358719 :     j11 = input[7];
      79     4358719 :     j12 = input[8];
      80     4358719 :     j13 = input[9];
      81     4358719 :     j14 = input[10];
      82     4358719 :     j15 = input[11];
      83             : 
      84     6510420 :     for (;;) {
      85     6510420 :         x0 = 0x61707865;
      86     6510420 :         x1 = 0x3320646e;
      87     6510420 :         x2 = 0x79622d32;
      88     6510420 :         x3 = 0x6b206574;
      89     6510420 :         x4 = j4;
      90     6510420 :         x5 = j5;
      91     6510420 :         x6 = j6;
      92     6510420 :         x7 = j7;
      93     6510420 :         x8 = j8;
      94     6510420 :         x9 = j9;
      95     6510420 :         x10 = j10;
      96     6510420 :         x11 = j11;
      97     6510420 :         x12 = j12;
      98     6510420 :         x13 = j13;
      99     6510420 :         x14 = j14;
     100     6510420 :         x15 = j15;
     101             : 
     102             :         // The 20 inner ChaCha20 rounds are unrolled here for performance.
     103     6510420 :         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     6510420 :         x0 += 0x61707865;
     115     6510420 :         x1 += 0x3320646e;
     116     6510420 :         x2 += 0x79622d32;
     117     6510420 :         x3 += 0x6b206574;
     118     6510420 :         x4 += j4;
     119     6510420 :         x5 += j5;
     120     6510420 :         x6 += j6;
     121     6510420 :         x7 += j7;
     122     6510420 :         x8 += j8;
     123     6510420 :         x9 += j9;
     124     6510420 :         x10 += j10;
     125     6510420 :         x11 += j11;
     126     6510420 :         x12 += j12;
     127     6510420 :         x13 += j13;
     128     6510420 :         x14 += j14;
     129     6510420 :         x15 += j15;
     130             : 
     131     6510420 :         ++j12;
     132     6510420 :         if (!j12) ++j13;
     133             : 
     134     6510420 :         WriteLE32(c + 0, x0);
     135     6510420 :         WriteLE32(c + 4, x1);
     136     6510420 :         WriteLE32(c + 8, x2);
     137     6510420 :         WriteLE32(c + 12, x3);
     138     6510420 :         WriteLE32(c + 16, x4);
     139     6510420 :         WriteLE32(c + 20, x5);
     140     6510420 :         WriteLE32(c + 24, x6);
     141     6510420 :         WriteLE32(c + 28, x7);
     142     6510420 :         WriteLE32(c + 32, x8);
     143     6510420 :         WriteLE32(c + 36, x9);
     144     6510420 :         WriteLE32(c + 40, x10);
     145     6510420 :         WriteLE32(c + 44, x11);
     146     6510420 :         WriteLE32(c + 48, x12);
     147     6510420 :         WriteLE32(c + 52, x13);
     148     6510420 :         WriteLE32(c + 56, x14);
     149     6510420 :         WriteLE32(c + 60, x15);
     150             : 
     151     6510420 :         if (blocks == 1) {
     152     4358719 :             input[8] = j12;
     153     4358719 :             input[9] = j13;
     154     4358719 :             return;
     155             :         }
     156     2151701 :         blocks -= 1;
     157     2151701 :         c += BLOCKLEN;
     158             :     }
     159     4358719 : }
     160             : 
     161       85426 : inline void ChaCha20Aligned::Crypt(Span<const std::byte> in_bytes, Span<std::byte> out_bytes) noexcept
     162             : {
     163       85426 :     assert(in_bytes.size() == out_bytes.size());
     164       85426 :     const unsigned char* m = UCharCast(in_bytes.data());
     165       85426 :     unsigned char* c = UCharCast(out_bytes.data());
     166       85426 :     size_t blocks = out_bytes.size() / BLOCKLEN;
     167       85426 :     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       85426 :     if (!blocks) return;
     173             : 
     174       85426 :     j4 = input[0];
     175       85426 :     j5 = input[1];
     176       85426 :     j6 = input[2];
     177       85426 :     j7 = input[3];
     178       85426 :     j8 = input[4];
     179       85426 :     j9 = input[5];
     180       85426 :     j10 = input[6];
     181       85426 :     j11 = input[7];
     182       85426 :     j12 = input[8];
     183       85426 :     j13 = input[9];
     184       85426 :     j14 = input[10];
     185       85426 :     j15 = input[11];
     186             : 
     187     4365480 :     for (;;) {
     188     4365480 :         x0 = 0x61707865;
     189     4365480 :         x1 = 0x3320646e;
     190     4365480 :         x2 = 0x79622d32;
     191     4365480 :         x3 = 0x6b206574;
     192     4365480 :         x4 = j4;
     193     4365480 :         x5 = j5;
     194     4365480 :         x6 = j6;
     195     4365480 :         x7 = j7;
     196     4365480 :         x8 = j8;
     197     4365480 :         x9 = j9;
     198     4365480 :         x10 = j10;
     199     4365480 :         x11 = j11;
     200     4365480 :         x12 = j12;
     201     4365480 :         x13 = j13;
     202     4365480 :         x14 = j14;
     203     4365480 :         x15 = j15;
     204             : 
     205             :         // The 20 inner ChaCha20 rounds are unrolled here for performance.
     206     4365480 :         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     4365480 :         x0 += 0x61707865;
     218     4365480 :         x1 += 0x3320646e;
     219     4365480 :         x2 += 0x79622d32;
     220     4365480 :         x3 += 0x6b206574;
     221     4365480 :         x4 += j4;
     222     4365480 :         x5 += j5;
     223     4365480 :         x6 += j6;
     224     4365480 :         x7 += j7;
     225     4365480 :         x8 += j8;
     226     4365480 :         x9 += j9;
     227     4365480 :         x10 += j10;
     228     4365480 :         x11 += j11;
     229     4365480 :         x12 += j12;
     230     4365480 :         x13 += j13;
     231     4365480 :         x14 += j14;
     232     4365480 :         x15 += j15;
     233             : 
     234     4365480 :         x0 ^= ReadLE32(m + 0);
     235     4365480 :         x1 ^= ReadLE32(m + 4);
     236     4365480 :         x2 ^= ReadLE32(m + 8);
     237     4365480 :         x3 ^= ReadLE32(m + 12);
     238     4365480 :         x4 ^= ReadLE32(m + 16);
     239     4365480 :         x5 ^= ReadLE32(m + 20);
     240     4365480 :         x6 ^= ReadLE32(m + 24);
     241     4365480 :         x7 ^= ReadLE32(m + 28);
     242     4365480 :         x8 ^= ReadLE32(m + 32);
     243     4365480 :         x9 ^= ReadLE32(m + 36);
     244     4365480 :         x10 ^= ReadLE32(m + 40);
     245     4365480 :         x11 ^= ReadLE32(m + 44);
     246     4365480 :         x12 ^= ReadLE32(m + 48);
     247     4365480 :         x13 ^= ReadLE32(m + 52);
     248     4365480 :         x14 ^= ReadLE32(m + 56);
     249     4365480 :         x15 ^= ReadLE32(m + 60);
     250             : 
     251     4365480 :         ++j12;
     252     4365480 :         if (!j12) ++j13;
     253             : 
     254     4365480 :         WriteLE32(c + 0, x0);
     255     4365480 :         WriteLE32(c + 4, x1);
     256     4365480 :         WriteLE32(c + 8, x2);
     257     4365480 :         WriteLE32(c + 12, x3);
     258     4365480 :         WriteLE32(c + 16, x4);
     259     4365480 :         WriteLE32(c + 20, x5);
     260     4365480 :         WriteLE32(c + 24, x6);
     261     4365480 :         WriteLE32(c + 28, x7);
     262     4365480 :         WriteLE32(c + 32, x8);
     263     4365480 :         WriteLE32(c + 36, x9);
     264     4365480 :         WriteLE32(c + 40, x10);
     265     4365480 :         WriteLE32(c + 44, x11);
     266     4365480 :         WriteLE32(c + 48, x12);
     267     4365480 :         WriteLE32(c + 52, x13);
     268     4365480 :         WriteLE32(c + 56, x14);
     269     4365480 :         WriteLE32(c + 60, x15);
     270             : 
     271     4365480 :         if (blocks == 1) {
     272       85426 :             input[8] = j12;
     273       85426 :             input[9] = j13;
     274       85426 :             return;
     275             :         }
     276     4280054 :         blocks -= 1;
     277     4280054 :         c += BLOCKLEN;
     278     4280054 :         m += BLOCKLEN;
     279             :     }
     280       85426 : }
     281             : 
     282    18137561 : void ChaCha20::Keystream(Span<std::byte> out) noexcept
     283             : {
     284    18137561 :     if (out.empty()) return;
     285    18137304 :     if (m_bufleft) {
     286    14181532 :         unsigned reuse = std::min<size_t>(m_bufleft, out.size());
     287    14181532 :         std::copy(m_buffer.end() - m_bufleft, m_buffer.end() - m_bufleft + reuse, out.begin());
     288    14181532 :         m_bufleft -= reuse;
     289    14181532 :         out = out.subspan(reuse);
     290    14181532 :     }
     291    18137304 :     if (out.size() >= m_aligned.BLOCKLEN) {
     292     1424260 :         size_t blocks = out.size() / m_aligned.BLOCKLEN;
     293     1424260 :         m_aligned.Keystream(out.first(blocks * m_aligned.BLOCKLEN));
     294     1424260 :         out = out.subspan(blocks * m_aligned.BLOCKLEN);
     295     1424260 :     }
     296    18137304 :     if (!out.empty()) {
     297     2689143 :         m_aligned.Keystream(m_buffer);
     298     2689143 :         std::copy(m_buffer.begin(), m_buffer.begin() + out.size(), out.begin());
     299     2689143 :         m_bufleft = m_aligned.BLOCKLEN - out.size();
     300     2689143 :     }
     301    18137561 : }
     302             : 
     303     1666309 : void ChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
     304             : {
     305     1666309 :     assert(input.size() == output.size());
     306             : 
     307     1666309 :     if (!input.size()) return;
     308      412230 :     if (m_bufleft) {
     309      262587 :         unsigned reuse = std::min<size_t>(m_bufleft, input.size());
     310     6839098 :         for (unsigned i = 0; i < reuse; i++) {
     311     6576511 :             output[i] = input[i] ^ m_buffer[m_aligned.BLOCKLEN - m_bufleft + i];
     312     6576511 :         }
     313      262587 :         m_bufleft -= reuse;
     314      262587 :         output = output.subspan(reuse);
     315      262587 :         input = input.subspan(reuse);
     316      262587 :     }
     317      412230 :     if (input.size() >= m_aligned.BLOCKLEN) {
     318       85426 :         size_t blocks = input.size() / m_aligned.BLOCKLEN;
     319       85426 :         m_aligned.Crypt(input.first(blocks * m_aligned.BLOCKLEN), output.first(blocks * m_aligned.BLOCKLEN));
     320       85426 :         output = output.subspan(blocks * m_aligned.BLOCKLEN);
     321       85426 :         input = input.subspan(blocks * m_aligned.BLOCKLEN);
     322       85426 :     }
     323      412230 :     if (!input.empty()) {
     324      244926 :         m_aligned.Keystream(m_buffer);
     325     3396921 :         for (unsigned i = 0; i < input.size(); i++) {
     326     3151995 :             output[i] = input[i] ^ m_buffer[i];
     327     3151995 :         }
     328      244926 :         m_bufleft = m_aligned.BLOCKLEN - input.size();
     329      244926 :     }
     330     1666309 : }
     331             : 
     332      989116 : ChaCha20::~ChaCha20()
     333      494558 : {
     334      494558 :     memory_cleanse(m_buffer.data(), m_buffer.size());
     335      989116 : }
     336             : 
     337      470348 : void ChaCha20::SetKey(Span<const std::byte> key) noexcept
     338             : {
     339      470348 :     m_aligned.SetKey(key);
     340      470348 :     m_bufleft = 0;
     341      470348 :     memory_cleanse(m_buffer.data(), m_buffer.size());
     342      470348 : }
     343             : 
     344        1006 : FSChaCha20::FSChaCha20(Span<const std::byte> key, uint32_t rekey_interval) noexcept :
     345         503 :     m_chacha20(key), m_rekey_interval(rekey_interval)
     346         503 : {
     347             :     assert(key.size() == KEYLEN);
     348         503 : }
     349             : 
     350      167671 : void FSChaCha20::Crypt(Span<const std::byte> input, Span<std::byte> output) noexcept
     351             : {
     352      167671 :     assert(input.size() == output.size());
     353             : 
     354             :     // Invoke internal stream cipher for actual encryption/decryption.
     355      167671 :     m_chacha20.Crypt(input, output);
     356             : 
     357             :     // Rekey after m_rekey_interval encryptions/decryptions.
     358      167671 :     if (++m_chunk_counter == m_rekey_interval) {
     359             :         // Get new key from the stream cipher.
     360             :         std::byte new_key[KEYLEN];
     361         674 :         m_chacha20.Keystream(new_key);
     362             :         // Update its key.
     363         674 :         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         674 :         memory_cleanse(new_key, sizeof(new_key));
     367             :         // Set the nonce for the new section of output.
     368         674 :         m_chacha20.Seek({0, ++m_rekey_counter}, 0);
     369             :         // Reset the chunk counter.
     370         674 :         m_chunk_counter = 0;
     371         674 :     }
     372      167671 : }

Generated by: LCOV version 1.16