LCOV - code coverage report
Current view: top level - src/crypto - chacha20.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 100.0 % 258 258
Test Date: 2024-08-28 04:44:32 Functions: 100.0 % 12 12
Branches: 76.6 % 64 49

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

Generated by: LCOV version 2.0-1