diff options
author | Jonas Schnelli <dev@jonasschnelli.ch> | 2019-03-11 16:15:45 +0100 |
---|---|---|
committer | Jonas Schnelli <dev@jonasschnelli.ch> | 2019-06-25 15:13:02 +0200 |
commit | af5d1b5f4a7b56628a76af21284c258d845894f0 (patch) | |
tree | 1a86118f130acfed029545110e685b7dc1bdd4e1 /src/crypto/chacha_poly_aead.h | |
parent | 332c6134bb15384e5b91c631e821fe52a591d3bc (diff) | |
download | bitcoin-af5d1b5f4a7b56628a76af21284c258d845894f0.tar.xz |
Add ChaCha20Poly1305@Bitcoin AEAD implementation
Diffstat (limited to 'src/crypto/chacha_poly_aead.h')
-rw-r--r-- | src/crypto/chacha_poly_aead.h | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/crypto/chacha_poly_aead.h b/src/crypto/chacha_poly_aead.h new file mode 100644 index 0000000000..b3ba781cdd --- /dev/null +++ b/src/crypto/chacha_poly_aead.h @@ -0,0 +1,146 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H +#define BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H + +#include <crypto/chacha20.h> + +#include <cmath> + +static constexpr int CHACHA20_POLY1305_AEAD_KEY_LEN = 32; +static constexpr int CHACHA20_POLY1305_AEAD_AAD_LEN = 3; /* 3 bytes length */ +static constexpr int CHACHA20_ROUND_OUTPUT = 64; /* 64 bytes per round */ +static constexpr int AAD_PACKAGES_PER_ROUND = 21; /* 64 / 3 round down*/ + +/* A AEAD class for ChaCha20-Poly1305@bitcoin. + * + * ChaCha20 is a stream cipher designed by Daniel Bernstein and described in + * <ref>[http://cr.yp.to/chacha/chacha-20080128.pdf ChaCha20]</ref>. It operates + * by permuting 128 fixed bits, 128 or 256 bits of key, a 64 bit nonce and a 64 + * bit counter into 64 bytes of output. This output is used as a keystream, with + * any unused bytes simply discarded. + * + * Poly1305 <ref>[http://cr.yp.to/mac/poly1305-20050329.pdf Poly1305]</ref>, also + * by Daniel Bernstein, is a one-time Carter-Wegman MAC that computes a 128 bit + * integrity tag given a message and a single-use 256 bit secret key. + * + * The chacha20-poly1305@bitcoin combines these two primitives into an + * authenticated encryption mode. The construction used is based on that proposed + * for TLS by Adam Langley in + * <ref>[http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03 "ChaCha20 + * and Poly1305 based Cipher Suites for TLS", Adam Langley]</ref>, but differs in + * the layout of data passed to the MAC and in the addition of encryption of the + * packet lengths. + * + * ==== Detailed Construction ==== + * + * The chacha20-poly1305@bitcoin cipher requires two 256 bits of key material as + * output from the key exchange. Each key (K_1 and K_2) are used by two separate + * instances of chacha20. + * + * The instance keyed by K_1 is a stream cipher that is used only to encrypt the 3 + * byte packet length field and has its own sequence number. The second instance, + * keyed by K_2, is used in conjunction with poly1305 to build an AEAD + * (Authenticated Encryption with Associated Data) that is used to encrypt and + * authenticate the entire packet. + * + * Two separate cipher instances are used here so as to keep the packet lengths + * confidential but not create an oracle for the packet payload cipher by + * decrypting and using the packet length prior to checking the MAC. By using an + * independently-keyed cipher instance to encrypt the length, an active attacker + * seeking to exploit the packet input handling as a decryption oracle can learn + * nothing about the payload contents or its MAC (assuming key derivation, + * ChaCha20 and Poly1305 are secure). + * + * The AEAD is constructed as follows: for each packet, generate a Poly1305 key by + * taking the first 256 bits of ChaCha20 stream output generated using K_2, an IV + * consisting of the packet sequence number encoded as an LE uint64 and a ChaCha20 + * block counter of zero. The K_2 ChaCha20 block counter is then set to the + * little-endian encoding of 1 (i.e. {1, 0, 0, 0, 0, 0, 0, 0}) and this instance + * is used for encryption of the packet payload. + * + * ==== Packet Handling ==== + * + * When receiving a packet, the length must be decrypted first. When 3 bytes of + * ciphertext length have been received, they may be decrypted. + * + * A ChaCha20 round always calculates 64bytes which is sufficient to crypt 21 + * times a 3 bytes length field (21*3 = 63). The length field sequence number can + * thus be used 21 times (keystream caching). + * + * The length field must be enc-/decrypted with the ChaCha20 keystream keyed with + * K_1 defined by block counter 0, the length field sequence number in little + * endian and a keystream position from 0 to 60. + * + * Once the entire packet has been received, the MAC MUST be checked before + * decryption. A per-packet Poly1305 key is generated as described above and the + * MAC tag calculated using Poly1305 with this key over the ciphertext of the + * packet length and the payload together. The calculated MAC is then compared in + * constant time with the one appended to the packet and the packet decrypted + * using ChaCha20 as described above (with K_2, the packet sequence number as + * nonce and a starting block counter of 1). + * + * Detection of an invalid MAC MUST lead to immediate connection termination. + * + * To send a packet, first encode the 3 byte length and encrypt it using K_1 as + * described above. Encrypt the packet payload (using K_2) and append it to the + * encrypted length. Finally, calculate a MAC tag and append it. + * + * The initiating peer MUST use <code>K_1_A, K_2_A</code> to encrypt messages on + * the send channel, <code>K_1_B, K_2_B</code> MUST be used to decrypt messages on + * the receive channel. + * + * The responding peer MUST use <code>K_1_A, K_2_A</code> to decrypt messages on + * the receive channel, <code>K_1_B, K_2_B</code> MUST be used to encrypt messages + * on the send channel. + * + * Optimized implementations of ChaCha20-Poly1305@bitcoin are relatively fast in + * general, therefore it is very likely that encrypted messages require not more + * CPU cycles per bytes then the current unencrypted p2p message format + * (ChaCha20/Poly1305 versus double SHA256). + * + * The initial packet sequence numbers are 0. + * + * K_2 ChaCha20 cipher instance (payload) must never reuse a {key, nonce} for + * encryption nor may it be used to encrypt more than 2^70 bytes under the same + * {key, nonce}. + * + * K_1 ChaCha20 cipher instance (length field/AAD) must never reuse a {key, nonce, + * position-in-keystream} for encryption nor may it be used to encrypt more than + * 2^70 bytes under the same {key, nonce}. + * + * We use message sequence numbers for both communication directions. + */ + +class ChaCha20Poly1305AEAD +{ +private: + ChaCha20 m_chacha_main; // payload and poly1305 key-derivation cipher instance + ChaCha20 m_chacha_header; // AAD cipher instance (encrypted length) + unsigned char m_aad_keystream_buffer[CHACHA20_ROUND_OUTPUT]; // aad keystream cache + uint64_t m_cached_aad_seqnr; // aad keystream cache hint + +public: + ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len); + + explicit ChaCha20Poly1305AEAD(const ChaCha20Poly1305AEAD&) = delete; + + /** Encrypts/decrypts a packet + seqnr_payload, the message sequence number + seqnr_aad, the messages AAD sequence number which allows reuse of the AAD keystream + aad_pos, position to use in the AAD keystream to encrypt the AAD + dest, output buffer, must be of a size equal or larger then CHACHA20_POLY1305_AEAD_AAD_LEN + payload (+ POLY1305_TAG_LEN in encryption) bytes + destlen, length of the destination buffer + src, the AAD+payload to encrypt or the AAD+payload+MAC to decrypt + src_len, the length of the source buffer + is_encrypt, set to true if we encrypt (creates and appends the MAC instead of verifying it) + */ + 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); + + /** decrypts the 3 bytes AAD data and decodes it into a uint32_t field */ + bool GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext); +}; + +#endif // BITCOIN_CRYPTO_CHACHA_POLY_AEAD_H |