LCOV - code coverage report
Current view: top level - src/crypto - chacha20.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 114 267 42.7 %
Date: 2023-09-26 12:08:55 Functions: 8 13 61.5 %

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

Generated by: LCOV version 1.14