Merge pull request #3032 from PastaPastaPasta/backport-chacha-poly1305

Backport chacha-poly1305, prepare for V2 P2P Encrypted Messaging
This commit is contained in:
UdjinM6 2019-08-01 17:49:40 +03:00 committed by GitHub
commit 51c84248ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1077 additions and 19 deletions

View File

@ -368,6 +368,8 @@ crypto_libdash_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(PIC_FLAGS)
crypto_libdash_crypto_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
crypto/chacha_poly_aead.h \
crypto/chacha_poly_aead.cpp \
crypto/chacha20.h \
crypto/chacha20.cpp \
crypto/common.h \
@ -375,6 +377,8 @@ crypto_libdash_crypto_a_SOURCES = \
crypto/hmac_sha256.h \
crypto/hmac_sha512.cpp \
crypto/hmac_sha512.h \
crypto/poly1305.h \
crypto/poly1305.cpp \
crypto/ripemd160.cpp \
crypto/aes_helper.c \
crypto/ripemd160.h \
@ -591,7 +595,7 @@ dash_tx_LDADD += $(BACKTRACE_LIB) $(BOOST_LIBS) $(CRYPTO_LIBS) $(BLS_LIBS)
# dashconsensus library #
if BUILD_BITCOIN_LIBS
include_HEADERS = script/dashconsensus.h
libdashconsensus_la_SOURCES = $(crypto_libdash_crypto_a_SOURCES) $(libdash_consensus_a_SOURCES)
libdashconsensus_la_SOURCES = support/cleanse.cpp $(crypto_libdash_crypto_a_SOURCES) $(libdash_consensus_a_SOURCES)
if GLIBC_BACK_COMPAT
libdashconsensus_la_SOURCES += compat/glibc_compat.cpp

View File

@ -22,11 +22,14 @@ bench_bench_dash_SOURCES = \
bench/ecdsa.cpp \
bench/Examples.cpp \
bench/rollingbloom.cpp \
bench/chacha20.cpp \
bench/chacha_poly_aead.cpp \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/mempool_eviction.cpp \
bench/base58.cpp \
bench/lockedpool.cpp \
bench/poly1305.cpp \
bench/perf.cpp \
bench/perf.h \
bench/prevector_destructor.cpp \

51
src/bench/chacha20.cpp Normal file
View File

@ -0,0 +1,51 @@
// 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.
#include <iostream>
#include <bench/bench.h>
#include <hash.h>
#include <crypto/chacha20.h>
/* Number of bytes to process per iteration */
static const uint64_t BUFFER_SIZE_TINY = 64;
static const uint64_t BUFFER_SIZE_SMALL = 256;
static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;
static void CHACHA20(benchmark::State& state, size_t buffersize)
{
std::vector<uint8_t> key(32,0);
ChaCha20 ctx(key.data(), key.size());
ctx.SetIV(0);
ctx.Seek(0);
std::vector<uint8_t> in(buffersize,0);
std::vector<uint8_t> out(buffersize,0);
while (state.KeepRunning()) {
ctx.Crypt(in.data(), out.data(), in.size());
}
}
static void CHACHA20_64BYTES(benchmark::State& state)
{
CHACHA20(state, BUFFER_SIZE_TINY);
}
static void CHACHA20_256BYTES(benchmark::State& state)
{
CHACHA20(state, BUFFER_SIZE_SMALL);
}
static void CHACHA20_1MB(benchmark::State& state)
{
CHACHA20(state, BUFFER_SIZE_LARGE);
}
//TODO add back below once benchmarking backports are done
//BENCHMARK(CHACHA20_64BYTES, 500000);
//BENCHMARK(CHACHA20_256BYTES, 250000);
//BENCHMARK(CHACHA20_1MB, 340);
BENCHMARK(CHACHA20_64BYTES);
BENCHMARK(CHACHA20_256BYTES);
BENCHMARK(CHACHA20_1MB);

View File

@ -0,0 +1,124 @@
// 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.
#include <iostream>
#include <bench/bench.h>
#include <crypto/chacha_poly_aead.h>
#include <crypto/poly1305.h> // for the POLY1305_TAGLEN constant
#include <hash.h>
#include <limits>
#include <assert.h>
/* Number of bytes to process per iteration */
static constexpr uint64_t BUFFER_SIZE_TINY = 64;
static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
static constexpr uint64_t BUFFER_SIZE_LARGE = 1024 * 1024;
static const unsigned char k1[32] = {0};
static const unsigned char k2[32] = {0};
static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32);
static void CHACHA20_POLY1305_AEAD(benchmark::State& state, size_t buffersize, bool include_decryption)
{
std::vector<unsigned char> in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
std::vector<unsigned char> out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
uint64_t seqnr_payload = 0;
uint64_t seqnr_aad = 0;
int aad_pos = 0;
uint32_t len = 0;
while (state.KeepRunning()) {
// encrypt or decrypt the buffer with a static key
assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true));
if (include_decryption) {
// if we decrypt, include the GetLength
assert(aead.GetLength(&len, seqnr_aad, aad_pos, in.data()));
assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true));
}
// increase main sequence number
seqnr_payload++;
// increase aad position (position in AAD keystream)
aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
aad_pos = 0;
seqnr_aad++;
}
if (seqnr_payload + 1 == std::numeric_limits<uint64_t>::max()) {
// reuse of nonce+key is okay while benchmarking.
seqnr_payload = 0;
seqnr_aad = 0;
aad_pos = 0;
}
}
}
static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::State& state)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, false);
}
static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::State& state)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, false);
}
static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::State& state)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, false);
}
static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::State& state)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, true);
}
static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::State& state)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, true);
}
static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::State& state)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, true);
}
// Add Hash() (dbl-sha256) bench for comparison
static void HASH(benchmark::State& state, size_t buffersize)
{
uint8_t hash[CHash256::OUTPUT_SIZE];
std::vector<uint8_t> in(buffersize,0);
while (state.KeepRunning())
CHash256().Write(in.data(), in.size()).Finalize(hash);
}
static void HASH_64BYTES(benchmark::State& state)
{
HASH(state, BUFFER_SIZE_TINY);
}
static void HASH_256BYTES(benchmark::State& state)
{
HASH(state, BUFFER_SIZE_SMALL);
}
static void HASH_1MB(benchmark::State& state)
{
HASH(state, BUFFER_SIZE_LARGE);
}
//TODO add back below once benchmark backports are done
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT/*, 500000*/);
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT/*, 250000*/);
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT/*, 340*/);
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT/*, 500000*/);
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT/*, 250000*/);
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT/*, 340*/);
BENCHMARK(HASH_64BYTES/*, 500000*/);
BENCHMARK(HASH_256BYTES/*, 250000*/);
BENCHMARK(HASH_1MB/*, 340*/);

43
src/bench/poly1305.cpp Normal file
View File

@ -0,0 +1,43 @@
// 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.
#include <iostream>
#include <vector>
#include <bench/bench.h>
#include <crypto/poly1305.h>
/* Number of bytes to process per iteration */
static constexpr uint64_t BUFFER_SIZE_TINY = 64;
static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
static constexpr uint64_t BUFFER_SIZE_LARGE = 1024*1024;
static void POLY1305(benchmark::State& state, size_t buffersize)
{
std::vector<unsigned char> tag(POLY1305_TAGLEN, 0);
std::vector<unsigned char> key(POLY1305_KEYLEN, 0);
std::vector<unsigned char> in(buffersize, 0);
while (state.KeepRunning())
poly1305_auth(tag.data(), in.data(), in.size(), key.data());
}
static void POLY1305_64BYTES(benchmark::State& state)
{
POLY1305(state, BUFFER_SIZE_TINY);
}
static void POLY1305_256BYTES(benchmark::State& state)
{
POLY1305(state, BUFFER_SIZE_SMALL);
}
static void POLY1305_1MB(benchmark::State& state)
{
POLY1305(state, BUFFER_SIZE_LARGE);
}
//TODO add back below once benchmarking backports are done
BENCHMARK(POLY1305_64BYTES/*, 500000*/);
BENCHMARK(POLY1305_256BYTES/*, 250000*/);
BENCHMARK(POLY1305_1MB/*, 340*/);

View File

@ -71,7 +71,7 @@ void ChaCha20::Seek(uint64_t pos)
input[13] = pos >> 32;
}
void ChaCha20::Output(unsigned char* c, size_t bytes)
void ChaCha20::Keystream(unsigned char* c, size_t bytes)
{
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
@ -178,3 +178,133 @@ void ChaCha20::Output(unsigned char* c, size_t bytes)
c += 64;
}
}
void ChaCha20::Crypt(const unsigned char* m, unsigned char* c, size_t bytes)
{
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
unsigned char *ctarget = nullptr;
unsigned char tmp[64];
unsigned int i;
if (!bytes) return;
j0 = input[0];
j1 = input[1];
j2 = input[2];
j3 = input[3];
j4 = input[4];
j5 = input[5];
j6 = input[6];
j7 = input[7];
j8 = input[8];
j9 = input[9];
j10 = input[10];
j11 = input[11];
j12 = input[12];
j13 = input[13];
j14 = input[14];
j15 = input[15];
for (;;) {
if (bytes < 64) {
// if m has fewer than 64 bytes available, copy m to tmp and
// read from tmp instead
for (i = 0;i < bytes;++i) tmp[i] = m[i];
m = tmp;
ctarget = c;
c = tmp;
}
x0 = j0;
x1 = j1;
x2 = j2;
x3 = j3;
x4 = j4;
x5 = j5;
x6 = j6;
x7 = j7;
x8 = j8;
x9 = j9;
x10 = j10;
x11 = j11;
x12 = j12;
x13 = j13;
x14 = j14;
x15 = j15;
for (i = 20;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
QUARTERROUND( x3, x7,x11,x15)
QUARTERROUND( x0, x5,x10,x15)
QUARTERROUND( x1, x6,x11,x12)
QUARTERROUND( x2, x7, x8,x13)
QUARTERROUND( x3, x4, x9,x14)
}
x0 += j0;
x1 += j1;
x2 += j2;
x3 += j3;
x4 += j4;
x5 += j5;
x6 += j6;
x7 += j7;
x8 += j8;
x9 += j9;
x10 += j10;
x11 += j11;
x12 += j12;
x13 += j13;
x14 += j14;
x15 += j15;
x0 ^= ReadLE32(m + 0);
x1 ^= ReadLE32(m + 4);
x2 ^= ReadLE32(m + 8);
x3 ^= ReadLE32(m + 12);
x4 ^= ReadLE32(m + 16);
x5 ^= ReadLE32(m + 20);
x6 ^= ReadLE32(m + 24);
x7 ^= ReadLE32(m + 28);
x8 ^= ReadLE32(m + 32);
x9 ^= ReadLE32(m + 36);
x10 ^= ReadLE32(m + 40);
x11 ^= ReadLE32(m + 44);
x12 ^= ReadLE32(m + 48);
x13 ^= ReadLE32(m + 52);
x14 ^= ReadLE32(m + 56);
x15 ^= ReadLE32(m + 60);
++j12;
if (!j12) ++j13;
WriteLE32(c + 0, x0);
WriteLE32(c + 4, x1);
WriteLE32(c + 8, x2);
WriteLE32(c + 12, x3);
WriteLE32(c + 16, x4);
WriteLE32(c + 20, x5);
WriteLE32(c + 24, x6);
WriteLE32(c + 28, x7);
WriteLE32(c + 32, x8);
WriteLE32(c + 36, x9);
WriteLE32(c + 40, x10);
WriteLE32(c + 44, x11);
WriteLE32(c + 48, x12);
WriteLE32(c + 52, x13);
WriteLE32(c + 56, x14);
WriteLE32(c + 60, x15);
if (bytes <= 64) {
if (bytes < 64) {
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
}
input[12] = j12;
input[13] = j13;
return;
}
bytes -= 64;
c += 64;
m += 64;
}
}

View File

@ -8,7 +8,8 @@
#include <stdint.h>
#include <stdlib.h>
/** A PRNG class for ChaCha20. */
/** A class for ChaCha20 256-bit stream cipher developed by Daniel J. Bernstein
https://cr.yp.to/chacha/chacha-20080128.pdf */
class ChaCha20
{
private:
@ -17,10 +18,17 @@ private:
public:
ChaCha20();
ChaCha20(const unsigned char* key, size_t keylen);
void SetKey(const unsigned char* key, size_t keylen);
void SetIV(uint64_t iv);
void Seek(uint64_t pos);
void Output(unsigned char* output, size_t bytes);
void SetKey(const unsigned char* key, size_t keylen); //!< set key with flexible keylength; 256bit recommended */
void SetIV(uint64_t iv); // set the 64bit nonce
void Seek(uint64_t pos); // set the 64bit block counter
/** outputs the keystream of size <bytes> into <c> */
void Keystream(unsigned char* c, size_t bytes);
/** enciphers the message <input> of length <bytes> and write the enciphered representation into <output>
* Used for encryption and decryption (XOR)
*/
void Crypt(const unsigned char* input, unsigned char* output, size_t bytes);
};
#endif // BITCOIN_CRYPTO_CHACHA20_H

View File

@ -0,0 +1,126 @@
// 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.
#include <crypto/chacha_poly_aead.h>
#include <crypto/common.h>
#include <crypto/poly1305.h>
#include <support/cleanse.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include <limits>
#ifndef HAVE_TIMINGSAFE_BCMP
int timingsafe_bcmp(const unsigned char* b1, const unsigned char* b2, size_t n)
{
const unsigned char *p1 = b1, *p2 = b2;
int ret = 0;
for (; n > 0; n--)
ret |= *p1++ ^ *p2++;
return (ret != 0);
}
#endif // TIMINGSAFE_BCMP
ChaCha20Poly1305AEAD::ChaCha20Poly1305AEAD(const unsigned char* K_1, size_t K_1_len, const unsigned char* K_2, size_t K_2_len)
{
assert(K_1_len == CHACHA20_POLY1305_AEAD_KEY_LEN);
assert(K_2_len == CHACHA20_POLY1305_AEAD_KEY_LEN);
m_chacha_main.SetKey(K_1, CHACHA20_POLY1305_AEAD_KEY_LEN);
m_chacha_header.SetKey(K_2, CHACHA20_POLY1305_AEAD_KEY_LEN);
// set the cached sequence number to uint64 max which hints for an unset cache.
// we can't hit uint64 max since the rekey rule (which resets the sequence number) is 1GB
m_cached_aad_seqnr = std::numeric_limits<uint64_t>::max();
}
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)
{
// check buffer boundaries
if (
// if we encrypt, make sure the source contains at least the expected AAD and the destination has at least space for the source + MAC
(is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN || dest_len < src_len + POLY1305_TAGLEN)) ||
// 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
(!is_encrypt && (src_len < CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN || dest_len < src_len - POLY1305_TAGLEN))) {
return false;
}
unsigned char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
memset(poly_key, 0, sizeof(poly_key));
m_chacha_main.SetIV(seqnr_payload);
// block counter 0 for the poly1305 key
// use lower 32bytes for the poly1305 key
// (throws away 32 unused bytes (upper 32) from this ChaCha20 round)
m_chacha_main.Seek(0);
m_chacha_main.Crypt(poly_key, poly_key, sizeof(poly_key));
// if decrypting, verify the tag prior to decryption
if (!is_encrypt) {
const unsigned char* tag = src + src_len - POLY1305_TAGLEN;
poly1305_auth(expected_tag, src, src_len - POLY1305_TAGLEN, poly_key);
// constant time compare the calculated MAC with the provided MAC
if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
memory_cleanse(expected_tag, sizeof(expected_tag));
memory_cleanse(poly_key, sizeof(poly_key));
return false;
}
memory_cleanse(expected_tag, sizeof(expected_tag));
// MAC has been successfully verified, make sure we don't covert it in decryption
src_len -= POLY1305_TAGLEN;
}
// calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache
if (m_cached_aad_seqnr != seqnr_aad) {
m_cached_aad_seqnr = seqnr_aad;
m_chacha_header.SetIV(seqnr_aad);
m_chacha_header.Seek(0);
m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT);
}
// crypt the AAD (3 bytes message length) with given position in AAD cipher instance keystream
dest[0] = src[0] ^ m_aad_keystream_buffer[aad_pos];
dest[1] = src[1] ^ m_aad_keystream_buffer[aad_pos + 1];
dest[2] = src[2] ^ m_aad_keystream_buffer[aad_pos + 2];
// Set the playload ChaCha instance block counter to 1 and crypt the payload
m_chacha_main.Seek(1);
m_chacha_main.Crypt(src + CHACHA20_POLY1305_AEAD_AAD_LEN, dest + CHACHA20_POLY1305_AEAD_AAD_LEN, src_len - CHACHA20_POLY1305_AEAD_AAD_LEN);
// If encrypting, calculate and append tag
if (is_encrypt) {
// the poly1305 tag expands over the AAD (3 bytes length) & encrypted payload
poly1305_auth(dest + src_len, dest, src_len, poly_key);
}
// cleanse no longer required MAC and polykey
memory_cleanse(poly_key, sizeof(poly_key));
return true;
}
bool ChaCha20Poly1305AEAD::GetLength(uint32_t* len24_out, uint64_t seqnr_aad, int aad_pos, const uint8_t* ciphertext)
{
// enforce valid aad position to avoid accessing outside of the 64byte keystream cache
// (there is space for 21 times 3 bytes)
assert(aad_pos >= 0 && aad_pos < CHACHA20_ROUND_OUTPUT - CHACHA20_POLY1305_AEAD_AAD_LEN);
if (m_cached_aad_seqnr != seqnr_aad) {
// we need to calculate the 64 keystream bytes since we reached a new aad sequence number
m_cached_aad_seqnr = seqnr_aad;
m_chacha_header.SetIV(seqnr_aad); // use LE for the nonce
m_chacha_header.Seek(0); // block counter 0
m_chacha_header.Keystream(m_aad_keystream_buffer, CHACHA20_ROUND_OUTPUT); // write keystream to the cache
}
// decrypt the ciphertext length by XORing the right position of the 64byte keystream cache with the ciphertext
*len24_out = (ciphertext[0] ^ m_aad_keystream_buffer[aad_pos + 0]) |
(ciphertext[1] ^ m_aad_keystream_buffer[aad_pos + 1]) << 8 |
(ciphertext[2] ^ m_aad_keystream_buffer[aad_pos + 2]) << 16;
return true;
}

View File

@ -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

141
src/crypto/poly1305.cpp Normal file
View File

@ -0,0 +1,141 @@
// 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.
// Based on the public domain implementation by Andrew Moon
// poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
#include <crypto/common.h>
#include <crypto/poly1305.h>
#include <string.h>
#define mul32x32_64(a,b) ((uint64_t)(a) * (b))
void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
uint32_t t0,t1,t2,t3;
uint32_t h0,h1,h2,h3,h4;
uint32_t r0,r1,r2,r3,r4;
uint32_t s1,s2,s3,s4;
uint32_t b, nb;
size_t j;
uint64_t t[5];
uint64_t f0,f1,f2,f3;
uint64_t g0,g1,g2,g3,g4;
uint64_t c;
unsigned char mp[16];
/* clamp key */
t0 = ReadLE32(key+0);
t1 = ReadLE32(key+4);
t2 = ReadLE32(key+8);
t3 = ReadLE32(key+12);
/* precompute multipliers */
r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
r3 = t2 & 0x3f03fff; t3 >>= 8;
r4 = t3 & 0x00fffff;
s1 = r1 * 5;
s2 = r2 * 5;
s3 = r3 * 5;
s4 = r4 * 5;
/* init state */
h0 = 0;
h1 = 0;
h2 = 0;
h3 = 0;
h4 = 0;
/* full blocks */
if (inlen < 16) goto poly1305_donna_atmost15bytes;
poly1305_donna_16bytes:
m += 16;
inlen -= 16;
t0 = ReadLE32(m-16);
t1 = ReadLE32(m-12);
t2 = ReadLE32(m-8);
t3 = ReadLE32(m-4);
h0 += t0 & 0x3ffffff;
h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff;
h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff;
h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff;
h4 += (t3 >> 8) | (1 << 24);
poly1305_donna_mul:
t[0] = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
t[1] = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
t[2] = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
t[3] = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
t[4] = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
h0 = (uint32_t)t[0] & 0x3ffffff; c = (t[0] >> 26);
t[1] += c; h1 = (uint32_t)t[1] & 0x3ffffff; b = (uint32_t)(t[1] >> 26);
t[2] += b; h2 = (uint32_t)t[2] & 0x3ffffff; b = (uint32_t)(t[2] >> 26);
t[3] += b; h3 = (uint32_t)t[3] & 0x3ffffff; b = (uint32_t)(t[3] >> 26);
t[4] += b; h4 = (uint32_t)t[4] & 0x3ffffff; b = (uint32_t)(t[4] >> 26);
h0 += b * 5;
if (inlen >= 16) goto poly1305_donna_16bytes;
/* final bytes */
poly1305_donna_atmost15bytes:
if (!inlen) goto poly1305_donna_finish;
for (j = 0; j < inlen; j++) mp[j] = m[j];
mp[j++] = 1;
for (; j < 16; j++) mp[j] = 0;
inlen = 0;
t0 = ReadLE32(mp+0);
t1 = ReadLE32(mp+4);
t2 = ReadLE32(mp+8);
t3 = ReadLE32(mp+12);
h0 += t0 & 0x3ffffff;
h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff;
h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff;
h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff;
h4 += (t3 >> 8);
goto poly1305_donna_mul;
poly1305_donna_finish:
b = h0 >> 26; h0 = h0 & 0x3ffffff;
h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff;
h1 += b;
g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
g4 = h4 + b - (1 << 26);
b = (g4 >> 31) - 1;
nb = ~b;
h0 = (h0 & nb) | (g0 & b);
h1 = (h1 & nb) | (g1 & b);
h2 = (h2 & nb) | (g2 & b);
h3 = (h3 & nb) | (g3 & b);
h4 = (h4 & nb) | (g4 & b);
f0 = ((h0 ) | (h1 << 26)) + (uint64_t)ReadLE32(&key[16]);
f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)ReadLE32(&key[20]);
f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)ReadLE32(&key[24]);
f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)ReadLE32(&key[28]);
WriteLE32(&out[ 0], f0); f1 += (f0 >> 32);
WriteLE32(&out[ 4], f1); f2 += (f1 >> 32);
WriteLE32(&out[ 8], f2); f3 += (f2 >> 32);
WriteLE32(&out[12], f3);
}

17
src/crypto/poly1305.h Normal file
View File

@ -0,0 +1,17 @@
// 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_POLY1305_H
#define BITCOIN_CRYPTO_POLY1305_H
#include <stdint.h>
#include <stdlib.h>
#define POLY1305_KEYLEN 32
#define POLY1305_TAGLEN 16
void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen,
const unsigned char key[POLY1305_KEYLEN]);
#endif // BITCOIN_CRYPTO_POLY1305_H

View File

@ -397,7 +397,7 @@ std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
{
std::vector<unsigned char> ret(len);
if (len > 0) {
rng.Output(&ret[0], len);
rng.Keystream(&ret[0], len);
}
return ret;
}

View File

@ -61,7 +61,7 @@ private:
if (requires_seed) {
RandomSeed();
}
rng.Output(bytebuf, sizeof(bytebuf));
rng.Keystream(bytebuf, sizeof(bytebuf));
bytebuf_size = sizeof(bytebuf);
}

View File

@ -5,9 +5,35 @@
#include "cleanse.h"
#include <openssl/crypto.h>
#include <cstring>
/* Compilers have a bad habit of removing "superfluous" memset calls that
* are trying to zero memory. For example, when memset()ing a buffer and
* then free()ing it, the compiler might decide that the memset is
* unobservable and thus can be removed.
*
* Previously we used OpenSSL which tried to stop this by a) implementing
* memset in assembly on x86 and b) putting the function in its own file
* for other platforms.
*
* This change removes those tricks in favour of using asm directives to
* scare the compiler away. As best as our compiler folks can tell, this is
* sufficient and will continue to be so.
*
* Adam Langley <agl@google.com>
* Commit: ad1907fe73334d6c696c8539646c21b11178f20f
* BoringSSL (LICENSE: ISC)
*/
void memory_cleanse(void *ptr, size_t len)
{
OPENSSL_cleanse(ptr, len);
std::memset(ptr, 0, len);
/* As best as we can tell, this is sufficient to break any optimisations that
might try to eliminate "superfluous" memsets. If there's an easy way to
detect memset_s, it would be better to use that. */
#if defined(_MSC_VER)
__asm;
#else
__asm__ __volatile__("" : : "r"(ptr) : "memory");
#endif
}

View File

@ -4,6 +4,8 @@
#include "crypto/aes.h"
#include "crypto/chacha20.h"
#include "crypto/chacha_poly_aead.h"
#include "crypto/poly1305.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
@ -187,17 +189,47 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
}
}
void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
void TestChaCha20(const std::string &hex_message, const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
std::vector<unsigned char> m = ParseHex(hex_message);
ChaCha20 rng(key.data(), key.size());
rng.SetIV(nonce);
rng.Seek(seek);
std::vector<unsigned char> out = ParseHex(hexout);
std::vector<unsigned char> outres;
outres.resize(out.size());
rng.Output(outres.data(), outres.size());
assert(hex_message.empty() || m.size() == out.size());
// perform the ChaCha20 round(s), if message is provided it will output the encrypted ciphertext otherwise the keystream
if (!hex_message.empty()) {
rng.Crypt(m.data(), outres.data(), outres.size());
} else {
rng.Keystream(outres.data(), outres.size());
}
BOOST_CHECK(out == outres);
if (!hex_message.empty()) {
// Manually XOR with the keystream and compare the output
rng.SetIV(nonce);
rng.Seek(seek);
std::vector<unsigned char> only_keystream(outres.size());
rng.Keystream(only_keystream.data(), only_keystream.size());
for (size_t i = 0; i != m.size(); i++) {
outres[i] = m[i] ^ only_keystream[i];
}
BOOST_CHECK(out == outres);
}
}
static void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string& hextag)
{
std::vector<unsigned char> key = ParseHex(hexkey);
std::vector<unsigned char> m = ParseHex(hexmessage);
std::vector<unsigned char> tag = ParseHex(hextag);
std::vector<unsigned char> tagres;
tagres.resize(POLY1305_TAGLEN);
poly1305_auth(tagres.data(), m.data(), m.size(), key.data());
BOOST_CHECK(tag == tagres);
}
std::string LongTestString(void) {
@ -479,25 +511,37 @@ BOOST_AUTO_TEST_CASE(pbkdf2_hmac_sha512_test) {
BOOST_AUTO_TEST_CASE(chacha20_testvector)
{
// Test vector from RFC 7539
TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
// test encryption
TestChaCha20("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756"
"c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e"
"20776f756c642062652069742e",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
"6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d"
"624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74"
"a35be6b40b8eedf2785e42874d"
);
// test keystream output
TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
"224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb"
"a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a"
"832c89c167eacd901d7e2bf363");
// Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b"
"8f41518a11cc387b669b2ee6586");
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79"
"2b1c43fea817e9ad275ae546963");
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770"
"62eb7a0433e445f41e3");
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
TestChaCha20("", "0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4"
"97a0b466e7d6bbdb0041b2f586b");
TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
TestChaCha20("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b"
"e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1"
"18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5"
@ -506,6 +550,201 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector)
"fab78c9");
}
BOOST_AUTO_TEST_CASE(poly1305_testvector)
{
// RFC 7539, section 2.5.2.
TestPoly1305("43727970746f6772617068696320466f72756d2052657365617263682047726f7570",
"85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b",
"a8061dc1305136c6c22b8baf0c0127a9");
// RFC 7539, section A.3.
TestPoly1305("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000");
TestPoly1305("416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
"5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
"726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
"520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
"4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
"56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
"6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
"768696368206172652061646472657373656420746f",
"0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e",
"36e5f6b5c5e06070f0efca96227a863e");
TestPoly1305("416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
"5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
"726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
"520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
"4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
"56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
"6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
"768696368206172652061646472657373656420746f",
"36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000",
"f3477e7cd95417af89a6b8794c310cf0");
TestPoly1305("2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e6420676"
"96d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e"
"6420746865206d6f6d65207261746873206f757467726162652e",
"1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
"4541669a7eaaee61e708dc7cbcc5eb62");
TestPoly1305("ffffffffffffffffffffffffffffffff",
"0200000000000000000000000000000000000000000000000000000000000000",
"03000000000000000000000000000000");
TestPoly1305("02000000000000000000000000000000",
"02000000000000000000000000000000ffffffffffffffffffffffffffffffff",
"03000000000000000000000000000000");
TestPoly1305("fffffffffffffffffffffffffffffffff0ffffffffffffffffffffffffffffff11000000000000000000000000000000",
"0100000000000000000000000000000000000000000000000000000000000000",
"05000000000000000000000000000000");
TestPoly1305("fffffffffffffffffffffffffffffffffbfefefefefefefefefefefefefefefe01010101010101010101010101010101",
"0100000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000");
TestPoly1305("fdffffffffffffffffffffffffffffff",
"0200000000000000000000000000000000000000000000000000000000000000",
"faffffffffffffffffffffffffffffff");
TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd01000000000000000000000000000000000000000000000001000000000000000000000000000000",
"0100000000000000040000000000000000000000000000000000000000000000",
"14000000000000005500000000000000");
TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000000000000000000000000000",
"0100000000000000040000000000000000000000000000000000000000000000",
"13000000000000000000000000000000");
}
static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aad_length, const std::string& hex_m, const std::string& hex_k1, const std::string& hex_k2, const std::string& hex_aad_keystream, const std::string& hex_encrypted_message, const std::string& hex_encrypted_message_seq_999)
{
// we need two sequence numbers, one for the payload cipher instance...
uint32_t seqnr_payload = 0;
// ... and one for the AAD (length) cipher instance
uint32_t seqnr_aad = 0;
// we need to keep track of the position in the AAD cipher instance
// keystream since we use the same 64byte output 21 times
// (21 times 3 bytes length < 64)
int aad_pos = 0;
std::vector<unsigned char> aead_K_1 = ParseHex(hex_k1);
std::vector<unsigned char> aead_K_2 = ParseHex(hex_k2);
std::vector<unsigned char> plaintext_buf = ParseHex(hex_m);
std::vector<unsigned char> expected_aad_keystream = ParseHex(hex_aad_keystream);
std::vector<unsigned char> expected_ciphertext_and_mac = ParseHex(hex_encrypted_message);
std::vector<unsigned char> expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999);
std::vector<unsigned char> ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0);
std::vector<unsigned char> plaintext_buf_new(plaintext_buf.size(), 0);
std::vector<unsigned char> cmp_ctx_buffer(64);
uint32_t out_len = 0;
// create the AEAD instance
ChaCha20Poly1305AEAD aead(aead_K_1.data(), aead_K_1.size(), aead_K_2.data(), aead_K_2.size());
// create a chacha20 instance to compare against
ChaCha20 cmp_ctx(aead_K_2.data(), 32);
// encipher
bool res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true);
// make sure the operation succeeded if expected to succeed
BOOST_CHECK_EQUAL(res, must_succeed);
if (!res) return;
// verify ciphertext & mac against the test vector
BOOST_CHECK_EQUAL(expected_ciphertext_and_mac.size(), ciphertext_buf.size());
BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac.data(), ciphertext_buf.size()) == 0);
// manually construct the AAD keystream
cmp_ctx.SetIV(seqnr_aad);
cmp_ctx.Seek(0);
cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64);
BOOST_CHECK(memcmp(expected_aad_keystream.data(), cmp_ctx_buffer.data(), expected_aad_keystream.size()) == 0);
// crypt the 3 length bytes and compare the length
uint32_t len_cmp = 0;
len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) |
(ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 |
(ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16;
BOOST_CHECK_EQUAL(len_cmp, expected_aad_length);
// encrypt / decrypt 1000 packets
for (size_t i = 0; i < 1000; ++i) {
res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, ciphertext_buf.data(), ciphertext_buf.size(), plaintext_buf.data(), plaintext_buf.size(), true);
BOOST_CHECK(res);
BOOST_CHECK(aead.GetLength(&out_len, seqnr_aad, aad_pos, ciphertext_buf.data()));
BOOST_CHECK_EQUAL(out_len, expected_aad_length);
res = aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, plaintext_buf_new.data(), plaintext_buf_new.size(), ciphertext_buf.data(), ciphertext_buf.size(), false);
BOOST_CHECK(res);
// make sure we repetitive get the same plaintext
BOOST_CHECK(memcmp(plaintext_buf.data(), plaintext_buf_new.data(), plaintext_buf.size()) == 0);
// compare sequence number 999 against the test vector
if (seqnr_payload == 999) {
BOOST_CHECK(memcmp(ciphertext_buf.data(), expected_ciphertext_and_mac_sequence999.data(), expected_ciphertext_and_mac_sequence999.size()) == 0);
}
// set nonce and block counter, output the keystream
cmp_ctx.SetIV(seqnr_aad);
cmp_ctx.Seek(0);
cmp_ctx.Keystream(cmp_ctx_buffer.data(), 64);
// crypt the 3 length bytes and compare the length
len_cmp = 0;
len_cmp = (ciphertext_buf[0] ^ cmp_ctx_buffer[aad_pos + 0]) |
(ciphertext_buf[1] ^ cmp_ctx_buffer[aad_pos + 1]) << 8 |
(ciphertext_buf[2] ^ cmp_ctx_buffer[aad_pos + 2]) << 16;
BOOST_CHECK_EQUAL(len_cmp, expected_aad_length);
// increment the sequence number(s)
// always increment the payload sequence number
// increment the AAD keystream position by its size (3)
// increment the AAD sequence number if we would hit the 64 byte limit
seqnr_payload++;
aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN;
if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) {
aad_pos = 0;
seqnr_aad++;
}
}
}
BOOST_AUTO_TEST_CASE(chacha20_poly1305_aead_testvector)
{
/* test chacha20poly1305@bitcoin AEAD */
// must fail with no message
TestChaCha20Poly1305AEAD(false, 0,
"",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000", "", "", "");
TestChaCha20Poly1305AEAD(true, 0,
/* m */ "0000000000000000000000000000000000000000000000000000000000000000",
/* k1 (payload) */ "0000000000000000000000000000000000000000000000000000000000000000",
/* k2 (AAD) */ "0000000000000000000000000000000000000000000000000000000000000000",
/* AAD keystream */ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586",
/* encrypted message & MAC */ "76b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32d2fc11829c1b6c1df1f551cd6131ff08",
/* encrypted message & MAC at sequence 999 */ "b0a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3aaa7aa16ec62c5e24f040c08bb20c3598");
TestChaCha20Poly1305AEAD(true, 1,
"0100000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586",
"77b8e09f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32baf0c85b6dff8602b06cf52a6aefc62e",
"b1a03d5bd2855d60699e7d3a3133fa47be740fe4e4c1f967555e2d9271f31c3a8bd94d54b5ecabbc41ffbb0c90924080");
TestChaCha20Poly1305AEAD(true, 255,
"ff0000f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"ff0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"c640c1711e3ee904ac35c57ab9791c8a1c408603a90b77a83b54f6c844cb4b06d94e7fc6c800e165acd66147e80ec45a567f6ce66d05ec0cae679dceeb890017",
"3940c1e92da4582ff6f92a776aeb14d014d384eeb30f660dacf70a14a23fd31e91212701334e2ce1acf5199dc84f4d61ddbe6571bca5af874b4c9226c26e650995d157644e1848b96ed6c2102d5489a050e71d29a5a66ece11de5fb5c9558d54da28fe45b0bc4db4e5b88030bfc4a352b4b7068eccf656bae7ad6a35615315fc7c49d4200388d5eca67c2e822e069336c69b40db67e0f3c81209c50f3216a4b89fb3ae1b984b7851a2ec6f68ab12b101ab120e1ea7313bb93b5a0f71185c7fea017ddb92769861c29dba4fbc432280d5dff21b36d1c4c790128b22699950bb18bf74c448cdfe547d8ed4f657d8005fdc0cd7a050c2d46050a44c4376355858981fbe8b184288276e7a93eabc899c4a",
"f039c6689eaeef0456685200feaab9d54bbd9acde4410a3b6f4321296f4a8ca2604b49727d8892c57e005d799b2a38e85e809f20146e08eec75169691c8d4f54a0d51a1e1c7b381e0474eb02f994be9415ef3ffcbd2343f0601e1f3b172a1d494f838824e4df570f8e3b0c04e27966e36c82abd352d07054ef7bd36b84c63f9369afe7ed79b94f953873006b920c3fa251a771de1b63da927058ade119aa898b8c97e42a606b2f6df1e2d957c22f7593c1e2002f4252f4c9ae4bf773499e5cfcfe14dfc1ede26508953f88553bf4a76a802f6a0068d59295b01503fd9a600067624203e880fdf53933b96e1f4d9eb3f4e363dd8165a278ff667a41ee42b9892b077cefff92b93441f7be74cf10e6cd");
}
BOOST_AUTO_TEST_CASE(countbits_tests)
{
FastRandomContext ctx;