Bitcoin Core  22.99.0
P2P Digital Currency
chacha_poly_aead.cpp
Go to the documentation of this file.
1 // Copyright (c) 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 
6 
7 #include <crypto/poly1305.h>
8 #include <support/cleanse.h>
9 
10 #include <assert.h>
11 #include <string.h>
12 
13 #include <cstdio>
14 #include <limits>
15 
16 #ifndef HAVE_TIMINGSAFE_BCMP
17 
18 int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n)
19 {
20  const unsigned char *p1 = b1, *p2 = b2;
21  int ret = 0;
22 
23  for (; n > 0; n--)
24  ret |= *p1++ ^ *p2++;
25  return (ret != 0);
26 }
27 
28 #endif // TIMINGSAFE_BCMP
29 
30 ChaCha20Poly1305AEAD::ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len)
31 {
34 
37 
38  // set the cached sequence number to uint64 max which hints for an unset cache.
39  // we can't hit uint64 max since the rekey rule (which resets the sequence number) is 1GB
40  m_cached_aad_seqnr = std::numeric_limits<uint64_t>::max();
41 }
42 
43 bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char* dest, size_t dest_len /* length of the output buffer for sanity checks */, const unsigned char* src, size_t src_len, bool is_encrypt)
44 {
45  // check buffer boundaries
46  if (
47  // if we encrypt, make sure the source contains at least the expected AAD and the destination has at least space for the source + MAC
48  (is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + POLY1305_TAGLEN)) ||
49  // if we decrypt, make sure the source contains at least the expected AAD+MAC and the destination has at least space for the source - MAC
50  (!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN || dest_len < src_len - POLY1305_TAGLEN))) {
51  return false;
52  }
53 
54  unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
55  memset(poly_key, 0, sizeof(poly_key));
56  m_chacha_main.SetIV(seqnr_payload);
57 
58  // block counter 0 for the poly1305 key
59  // use lower 32bytes for the poly1305 key
60  // (throws away 32 unused bytes (upper 32) from this ChaCha20 round)
62  m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key));
63 
64  // if decrypting, verify the tag prior to decryption
65  if (!is_encrypt) {
66  const unsigned char* tag = src + src_len - POLY1305_TAGLEN;
67  poly1305_auth(expected_tag, src, src_len - POLY1305_TAGLEN, poly_key);
68 
69  // constant time compare the calculated MAC with the provided MAC
70  if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
71  memory_cleanse(expected_tag, sizeof(expected_tag));
72  memory_cleanse(poly_key, sizeof(poly_key));
73  return false;
74  }
75  memory_cleanse(expected_tag, sizeof(expected_tag));
76  // MAC has been successfully verified, make sure we don't covert it in decryption
77  src_len -= POLY1305_TAGLEN;
78  }
79 
80  // calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache
81  if (m_cached_aad_seqnr != seqnr_aad) {
82  m_cached_aad_seqnr = seqnr_aad;
83  m_chacha_header.SetIV(seqnr_aad);
86  }
87  // crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream
88  dest[0] = src[0] ^ m_aad_keystream_buffer[aad_pos];
89  dest[1] = src[1] ^ m_aad_keystream_buffer[aad_pos + 1];
90  dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2];
91 
92  // Set the playload ChaCha instance block counter to 1 and crypt the payload
95 
96  // If encrypting, calculate and append tag
97  if (is_encrypt) {
98  // the poly1305 tag expands over the AAD (3 bytes length) & encrypted payload
99  poly1305_auth(dest + src_len, dest, src_len, poly_key);
100  }
101 
102  // cleanse no longer required MAC and polykey
103  memory_cleanse(poly_key, sizeof(poly_key));
104  return true;
105 }
106 
107 bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext)
108 {
109  // enforce valid aad position to avoid accessing outside of the 64byte keystream cache
110  // (there is space for 21 times 3 bytes)
111  assert(aad_pos >= 0 && aad_pos < CHACHA20_ROUND_OUTPUT - CHACHA20_POLY1305_AEAD_AAD_LEN);
112  if (m_cached_aad_seqnr != seqnr_aad) {
113  // we need to calculate the 64 keystream bytes since we reached a new aad sequence number
114  m_cached_aad_seqnr = seqnr_aad;
115  m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce
116  m_chacha_header.Seek(0); // block counter 0
117  m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache
118  }
119 
120  // decrypt the ciphertext length by XORing the right position of the 64byte keystream cache with the ciphertext
121  *len24_out = (ciphertext[0] ^ m_aad_keystream_buffer[aad_pos + 0]) |
122  (ciphertext[1] ^ m_aad_keystream_buffer[aad_pos + 1]) << 8 |
123  (ciphertext[2] ^ m_aad_keystream_buffer[aad_pos + 2]) << 16;
124 
125  return true;
126 }
chacha_poly_aead.h
poly1305_auth
void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN])
Definition: poly1305.cpp:15
assert
assert(!tx.IsCoinBase())
ChaCha20::SetKey
void SetKey(const unsigned char *key, size_t keylen)
set key with flexible keylength; 256bit recommended *‍/
Definition: chacha20.cpp:24
string.h
ChaCha20Poly1305AEAD::m_cached_aad_seqnr
uint64_t m_cached_aad_seqnr
Definition: chacha_poly_aead.h:123
ChaCha20Poly1305AEAD::m_aad_keystream_buffer
unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]
Definition: chacha_poly_aead.h:122
memory_cleanse
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
Definition: cleanse.cpp:14
ChaCha20::Keystream
void Keystream(unsigned char *c, size_t bytes)
outputs the keystream of size <bytes> into
Definition: chacha20.cpp:74
CHACHA20_ROUND_OUTPUT
static constexpr int CHACHA20_ROUND_OUTPUT
Definition: chacha_poly_aead.h:14
poly1305.h
ChaCha20::Crypt
void Crypt(const unsigned char *input, unsigned char *output, size_t bytes)
enciphers the message <input> of length <bytes> and write the enciphered representation into <output>...
Definition: chacha20.cpp:182
ChaCha20::Seek
void Seek(uint64_t pos)
Definition: chacha20.cpp:68
cleanse.h
ChaCha20Poly1305AEAD::ChaCha20Poly1305AEAD
ChaCha20Poly1305AEAD(const unsigned char *K_1, size_t K_1_len, const unsigned char *K_2, size_t K_2_len)
Definition: chacha_poly_aead.cpp:30
ChaCha20Poly1305AEAD::m_chacha_header
ChaCha20 m_chacha_header
Definition: chacha_poly_aead.h:121
ChaCha20Poly1305AEAD::m_chacha_main
ChaCha20 m_chacha_main
Definition: chacha_poly_aead.h:120
POLY1305_TAGLEN
#define POLY1305_TAGLEN
Definition: poly1305.h:12
ChaCha20Poly1305AEAD::GetLength
bool GetLength(uint32_t *len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t *ciphertext)
decrypts the 3 bytes AAD data and decodes it into a uint32_t field
Definition: chacha_poly_aead.cpp:107
ChaCha20::SetIV
void SetIV(uint64_t iv)
Definition: chacha20.cpp:62
POLY1305_KEYLEN
#define POLY1305_KEYLEN
Definition: poly1305.h:11
CHACHA20_POLY1305_AEAD_KEY_LEN
static constexpr int CHACHA20_POLY1305_AEAD_KEY_LEN
Definition: chacha_poly_aead.h:12
ChaCha20Poly1305AEAD::Crypt
bool Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int aad_pos, unsigned char *dest, size_t dest_len, const unsigned char *src, size_t src_len, bool is_encrypt)
Encrypts/decrypts a packet seqnr_payload, the message sequence number seqnr_aad, the messages AAD seq...
Definition: chacha_poly_aead.cpp:43
timingsafe_bcmp
int timingsafe_bcmp(const unsigned char *b1, const unsigned char *b2, size_t n)
Definition: chacha_poly_aead.cpp:18
CHACHA20_POLY1305_AEAD_AAD_LEN
static constexpr int CHACHA20_POLY1305_AEAD_AAD_LEN
Definition: chacha_poly_aead.h:13