/root/bitcoin/src/crypto/chacha20poly1305.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2023 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 | | #include <crypto/chacha20poly1305.h> |
6 | | |
7 | | #include <crypto/common.h> |
8 | | #include <crypto/chacha20.h> |
9 | | #include <crypto/poly1305.h> |
10 | | #include <span.h> |
11 | | #include <support/cleanse.h> |
12 | | |
13 | | #include <assert.h> |
14 | | #include <cstddef> |
15 | | |
16 | 0 | AEADChaCha20Poly1305::AEADChaCha20Poly1305(Span<const std::byte> key) noexcept : m_chacha20(key) |
17 | 0 | { |
18 | 0 | assert(key.size() == KEYLEN); |
19 | 0 | } |
20 | | |
21 | | void AEADChaCha20Poly1305::SetKey(Span<const std::byte> key) noexcept |
22 | 0 | { |
23 | 0 | assert(key.size() == KEYLEN); |
24 | 0 | m_chacha20.SetKey(key); |
25 | 0 | } |
26 | | |
27 | | namespace { |
28 | | |
29 | | int timingsafe_bcmp_internal(const unsigned char* b1, const unsigned char* b2, size_t n) noexcept |
30 | 0 | { |
31 | 0 | const unsigned char *p1 = b1, *p2 = b2; |
32 | 0 | int ret = 0; |
33 | 0 | for (; n > 0; n--) |
34 | 0 | ret |= *p1++ ^ *p2++; |
35 | 0 | return (ret != 0); |
36 | 0 | } |
37 | | |
38 | | /** Compute poly1305 tag. chacha20 must be set to the right nonce, block 0. Will be at block 1 after. */ |
39 | | void ComputeTag(ChaCha20& chacha20, Span<const std::byte> aad, Span<const std::byte> cipher, Span<std::byte> tag) noexcept |
40 | 0 | { |
41 | 0 | static const std::byte PADDING[16] = {{}}; |
42 | | |
43 | | // Get block of keystream (use a full 64 byte buffer to avoid the need for chacha20's own buffering). |
44 | 0 | std::byte first_block[ChaCha20Aligned::BLOCKLEN]; |
45 | 0 | chacha20.Keystream(first_block); |
46 | | |
47 | | // Use the first 32 bytes of the first keystream block as poly1305 key. |
48 | 0 | Poly1305 poly1305{Span{first_block}.first(Poly1305::KEYLEN)}; |
49 | | |
50 | | // Compute tag: |
51 | | // - Process the padded AAD with Poly1305. |
52 | 0 | const unsigned aad_padding_length = (16 - (aad.size() % 16)) % 16; |
53 | 0 | poly1305.Update(aad).Update(Span{PADDING}.first(aad_padding_length)); |
54 | | // - Process the padded ciphertext with Poly1305. |
55 | 0 | const unsigned cipher_padding_length = (16 - (cipher.size() % 16)) % 16; |
56 | 0 | poly1305.Update(cipher).Update(Span{PADDING}.first(cipher_padding_length)); |
57 | | // - Process the AAD and plaintext length with Poly1305. |
58 | 0 | std::byte length_desc[Poly1305::TAGLEN]; |
59 | 0 | WriteLE64(UCharCast(length_desc), aad.size()); |
60 | 0 | WriteLE64(UCharCast(length_desc + 8), cipher.size()); |
61 | 0 | poly1305.Update(length_desc); |
62 | | |
63 | | // Output tag. |
64 | 0 | poly1305.Finalize(tag); |
65 | 0 | } |
66 | | |
67 | | } // namespace |
68 | | |
69 | | void AEADChaCha20Poly1305::Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> cipher) noexcept |
70 | 0 | { |
71 | 0 | assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION); |
72 | | |
73 | | // Encrypt using ChaCha20 (starting at block 1). |
74 | 0 | m_chacha20.Seek(nonce, 1); |
75 | 0 | m_chacha20.Crypt(plain1, cipher.first(plain1.size())); |
76 | 0 | m_chacha20.Crypt(plain2, cipher.subspan(plain1.size()).first(plain2.size())); |
77 | | |
78 | | // Seek to block 0, and compute tag using key drawn from there. |
79 | 0 | m_chacha20.Seek(nonce, 0); |
80 | 0 | ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), cipher.last(EXPANSION)); |
81 | 0 | } |
82 | | |
83 | | bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Nonce96 nonce, Span<std::byte> plain1, Span<std::byte> plain2) noexcept |
84 | 0 | { |
85 | 0 | assert(cipher.size() == plain1.size() + plain2.size() + EXPANSION); |
86 | | |
87 | | // Verify tag (using key drawn from block 0). |
88 | 0 | m_chacha20.Seek(nonce, 0); |
89 | 0 | std::byte expected_tag[EXPANSION]; |
90 | 0 | ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag); |
91 | 0 | if (timingsafe_bcmp_internal(UCharCast(expected_tag), UCharCast(cipher.last(EXPANSION).data()), EXPANSION)) return false; |
92 | | |
93 | | // Decrypt (starting at block 1). |
94 | 0 | m_chacha20.Crypt(cipher.first(plain1.size()), plain1); |
95 | 0 | m_chacha20.Crypt(cipher.subspan(plain1.size()).first(plain2.size()), plain2); |
96 | 0 | return true; |
97 | 0 | } |
98 | | |
99 | | void AEADChaCha20Poly1305::Keystream(Nonce96 nonce, Span<std::byte> keystream) noexcept |
100 | 0 | { |
101 | | // Skip the first output block, as it's used for generating the poly1305 key. |
102 | 0 | m_chacha20.Seek(nonce, 1); |
103 | 0 | m_chacha20.Keystream(keystream); |
104 | 0 | } |
105 | | |
106 | | void FSChaCha20Poly1305::NextPacket() noexcept |
107 | 0 | { |
108 | 0 | if (++m_packet_counter == m_rekey_interval) { |
109 | | // Generate a full block of keystream, to avoid needing the ChaCha20 buffer, even though |
110 | | // we only need KEYLEN (32) bytes. |
111 | 0 | std::byte one_block[ChaCha20Aligned::BLOCKLEN]; |
112 | 0 | m_aead.Keystream({0xFFFFFFFF, m_rekey_counter}, one_block); |
113 | | // Switch keys. |
114 | 0 | m_aead.SetKey(Span{one_block}.first(KEYLEN)); |
115 | | // Wipe the generated keystream (a copy remains inside m_aead, which will be cleaned up |
116 | | // once it cycles again, or is destroyed). |
117 | 0 | memory_cleanse(one_block, sizeof(one_block)); |
118 | | // Update counters. |
119 | 0 | m_packet_counter = 0; |
120 | 0 | ++m_rekey_counter; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | void FSChaCha20Poly1305::Encrypt(Span<const std::byte> plain1, Span<const std::byte> plain2, Span<const std::byte> aad, Span<std::byte> cipher) noexcept |
125 | 0 | { |
126 | 0 | m_aead.Encrypt(plain1, plain2, aad, {m_packet_counter, m_rekey_counter}, cipher); |
127 | 0 | NextPacket(); |
128 | 0 | } |
129 | | |
130 | | bool FSChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std::byte> aad, Span<std::byte> plain1, Span<std::byte> plain2) noexcept |
131 | 0 | { |
132 | 0 | bool ret = m_aead.Decrypt(cipher, aad, {m_packet_counter, m_rekey_counter}, plain1, plain2); |
133 | 0 | NextPacket(); |
134 | 0 | return ret; |
135 | 0 | } |