|
| 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 | + |
| 5 | +#ifndef BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H |
| 6 | +#define BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H |
| 7 | + |
| 8 | +#include <crypto/chacha20.h> |
| 9 | + |
| 10 | +#include <cmath> |
| 11 | + |
| 12 | +static constexpr int CHACHA20_POLY1305_AEAD_KEY_LEN = 32; |
| 13 | +static constexpr int CHACHA20_POLY1305_AEAD_AAD_LEN = 3; /* 3 bytes length */ |
| 14 | +static constexpr int CHACHA20_ROUND_OUTPUT = 64; /* 64 bytes per round */ |
| 15 | +static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/ |
| 16 | + |
| 17 | +/* A AEAD class for ChaCha20-Poly1305@bitcoin. |
| 18 | + * |
| 19 | + * ChaCha20 is a stream cipher designed by Daniel Bernstein and described in |
| 20 | + * <ref>[http://cr.yp.to/chacha/chacha-20080128.pdf ChaCha20]</ref>. It operates |
| 21 | + * by permuting 128 fixed bits, 128 or 256 bits of key, a 64 bit nonce and a 64 |
| 22 | + * bit counter into 64 bytes of output. This output is used as a keystream, with |
| 23 | + * any unused bytes simply discarded. |
| 24 | + * |
| 25 | + * Poly1305 <ref>[http://cr.yp.to/mac/poly1305-20050329.pdf Poly1305]</ref>, also |
| 26 | + * by Daniel Bernstein, is a one-time Carter-Wegman MAC that computes a 128 bit |
| 27 | + * integrity tag given a message and a single-use 256 bit secret key. |
| 28 | + * |
| 29 | + * The chacha20-poly1305@bitcoin combines these two primitives into an |
| 30 | + * authenticated encryption mode. The construction used is based on that proposed |
| 31 | + * for TLS by Adam Langley in |
| 32 | + * <ref>[http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03 "ChaCha20 |
| 33 | + * and Poly1305 based Cipher Suites for TLS", Adam Langley]</ref>, but differs in |
| 34 | + * the layout of data passed to the MAC and in the addition of encryption of the |
| 35 | + * packet lengths. |
| 36 | + * |
| 37 | + * ==== Detailed Construction ==== |
| 38 | + * |
| 39 | + * The chacha20-poly1305@bitcoin cipher requires two 256 bits of key material as |
| 40 | + * output from the key exchange. Each key (K_1 and K_2) are used by two separate |
| 41 | + * instances of chacha20. |
| 42 | + * |
| 43 | + * The instance keyed by K_1 is a stream cipher that is used only to encrypt the 3 |
| 44 | + * byte packet length field and has its own sequence number. The second instance, |
| 45 | + * keyed by K_2, is used in conjunction with poly1305 to build an AEAD |
| 46 | + * (Authenticated Encryption with Associated Data) that is used to encrypt and |
| 47 | + * authenticate the entire packet. |
| 48 | + * |
| 49 | + * Two separate cipher instances are used here so as to keep the packet lengths |
| 50 | + * confidential but not create an oracle for the packet payload cipher by |
| 51 | + * decrypting and using the packet length prior to checking the MAC. By using an |
| 52 | + * independently-keyed cipher instance to encrypt the length, an active attacker |
| 53 | + * seeking to exploit the packet input handling as a decryption oracle can learn |
| 54 | + * nothing about the payload contents or its MAC (assuming key derivation, |
| 55 | + * ChaCha20 and Poly1305 are secure). |
| 56 | + * |
| 57 | + * The AEAD is constructed as follows: for each packet, generate a Poly1305 key by |
| 58 | + * taking the first 256 bits of ChaCha20 stream output generated using K_2, an IV |
| 59 | + * consisting of the packet sequence number encoded as an LE uint64 and a ChaCha20 |
| 60 | + * block counter of zero. The K_2 ChaCha20 block counter is then set to the |
| 61 | + * little-endian encoding of 1 (i.e. {1, 0, 0, 0, 0, 0, 0, 0}) and this instance |
| 62 | + * is used for encryption of the packet payload. |
| 63 | + * |
| 64 | + * ==== Packet Handling ==== |
| 65 | + * |
| 66 | + * When receiving a packet, the length must be decrypted first. When 3 bytes of |
| 67 | + * ciphertext length have been received, they may be decrypted. |
| 68 | + * |
| 69 | + * A ChaCha20 round always calculates 64bytes which is sufficient to crypt 21 |
| 70 | + * times a 3 bytes length field (21*3 = 63). The length field sequence number can |
| 71 | + * thus be used 21 times (keystream caching). |
| 72 | + * |
| 73 | + * The length field must be enc-/decrypted with the ChaCha20 keystream keyed with |
| 74 | + * K_1 defined by block counter 0, the length field sequence number in little |
| 75 | + * endian and a keystream position from 0 to 60. |
| 76 | + * |
| 77 | + * Once the entire packet has been received, the MAC MUST be checked before |
| 78 | + * decryption. A per-packet Poly1305 key is generated as described above and the |
| 79 | + * MAC tag calculated using Poly1305 with this key over the ciphertext of the |
| 80 | + * packet length and the payload together. The calculated MAC is then compared in |
| 81 | + * constant time with the one appended to the packet and the packet decrypted |
| 82 | + * using ChaCha20 as described above (with K_2, the packet sequence number as |
| 83 | + * nonce and a starting block counter of 1). |
| 84 | + * |
| 85 | + * Detection of an invalid MAC MUST lead to immediate connection termination. |
| 86 | + * |
| 87 | + * To send a packet, first encode the 3 byte length and encrypt it using K_1 as |
| 88 | + * described above. Encrypt the packet payload (using K_2) and append it to the |
| 89 | + * encrypted length. Finally, calculate a MAC tag and append it. |
| 90 | + * |
| 91 | + * The initiating peer MUST use <code>K_1_A, K_2_A</code> to encrypt messages on |
| 92 | + * the send channel, <code>K_1_B, K_2_B</code> MUST be used to decrypt messages on |
| 93 | + * the receive channel. |
| 94 | + * |
| 95 | + * The responding peer MUST use <code>K_1_A, K_2_A</code> to decrypt messages on |
| 96 | + * the receive channel, <code>K_1_B, K_2_B</code> MUST be used to encrypt messages |
| 97 | + * on the send channel. |
| 98 | + * |
| 99 | + * Optimized implementations of ChaCha20-Poly1305@bitcoin are relatively fast in |
| 100 | + * general, therefore it is very likely that encrypted messages require not more |
| 101 | + * CPU cycles per bytes then the current unencrypted p2p message format |
| 102 | + * (ChaCha20/Poly1305 versus double SHA256). |
| 103 | + * |
| 104 | + * The initial packet sequence numbers are 0. |
| 105 | + * |
| 106 | + * K_2 ChaCha20 cipher instance (payload) must never reuse a {key, nonce} for |
| 107 | + * encryption nor may it be used to encrypt more than 2^70 bytes under the same |
| 108 | + * {key, nonce}. |
| 109 | + * |
| 110 | + * K_1 ChaCha20 cipher instance (length field/AAD) must never reuse a {key, nonce, |
| 111 | + * position-in-keystream} for encryption nor may it be used to encrypt more than |
| 112 | + * 2^70 bytes under the same {key, nonce}. |
| 113 | + * |
| 114 | + * We use message sequence numbers for both communication directions. |
| 115 | + */ |
| 116 | + |
| 117 | +class ChaCha20Poly1305AEAD |
| 118 | +{ |
| 119 | +private: |
| 120 | + ChaCha20 m_chacha_main; // payload and poly1305 key-derivation cipher instance |
| 121 | + ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length) |
| 122 | + unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]; // aad keystream cache |
| 123 | + uint64_t m_cached_aad_seqnr; // aad keystream cache hint |
| 124 | + |
| 125 | +public: |
| 126 | + ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len); |
| 127 | + |
| 128 | + explicit ChaCha20Poly1305AEAD(const ChaCha20Poly1305AEAD&) = delete; |
| 129 | + |
| 130 | + /** Encrypts/decrypts a packet |
| 131 | + seqnr_payload, the message sequence number |
| 132 | + seqnr_aad, the messages AAD sequence number which allows reuse of the AAD keystream |
| 133 | + aad_pos, position to use in the AAD keystream to encrypt the AAD |
| 134 | + dest, output buffer, must be of a size equal or larger then CHACHA20_POLY1305_AEAD_AAD_LEN + payload (+ POLY1305_TAG_LEN in encryption) bytes |
| 135 | + destlen, length of the destination buffer |
| 136 | + src, the AAD+payload to encrypt or the AAD+payload+MAC to decrypt |
| 137 | + src_len, the length of the source buffer |
| 138 | + is_encrypt, set to true if we encrypt (creates and appends the MAC instead of verifying it) |
| 139 | + */ |
| 140 | + 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); |
| 141 | + |
| 142 | + /** decrypts the 3 bytes AAD data and decodes it into a uint32_t field */ |
| 143 | + bool GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext); |
| 144 | +}; |
| 145 | + |
| 146 | +#endif // BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H |
0 commit comments