aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bench/chacha_poly_aead.cpp4
-rw-r--r--src/bench/poly1305.cpp10
-rw-r--r--src/crypto/chacha_poly_aead.cpp20
-rw-r--r--src/crypto/poly1305.cpp326
-rw-r--r--src/crypto/poly1305.h61
-rw-r--r--src/test/crypto_tests.cpp80
-rw-r--r--src/test/fuzz/crypto_chacha20_poly1305_aead.cpp8
-rw-r--r--src/test/fuzz/crypto_poly1305.cpp37
8 files changed, 392 insertions, 154 deletions
diff --git a/src/bench/chacha_poly_aead.cpp b/src/bench/chacha_poly_aead.cpp
index db88841c32..9149eb683a 100644
--- a/src/bench/chacha_poly_aead.cpp
+++ b/src/bench/chacha_poly_aead.cpp
@@ -23,8 +23,8 @@ static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32);
static void CHACHA20_POLY1305_AEAD(benchmark::Bench& bench, 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);
+ 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;
diff --git a/src/bench/poly1305.cpp b/src/bench/poly1305.cpp
index f7d17dfa96..be0e5f2c2d 100644
--- a/src/bench/poly1305.cpp
+++ b/src/bench/poly1305.cpp
@@ -6,6 +6,8 @@
#include <bench/bench.h>
#include <crypto/poly1305.h>
+#include <span.h>
+
/* Number of bytes to process per iteration */
static constexpr uint64_t BUFFER_SIZE_TINY = 64;
static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
@@ -13,11 +15,11 @@ static constexpr uint64_t BUFFER_SIZE_LARGE = 1024*1024;
static void POLY1305(benchmark::Bench& bench, 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);
+ std::vector<std::byte> tag(Poly1305::TAGLEN, {});
+ std::vector<std::byte> key(Poly1305::KEYLEN, {});
+ std::vector<std::byte> in(buffersize, {});
bench.batch(in.size()).unit("byte").run([&] {
- poly1305_auth(tag.data(), in.data(), in.size(), key.data());
+ Poly1305{key}.Update(in).Finalize(tag);
});
}
diff --git a/src/crypto/chacha_poly_aead.cpp b/src/crypto/chacha_poly_aead.cpp
index d15f2f4ea6..0d82cf3d74 100644
--- a/src/crypto/chacha_poly_aead.cpp
+++ b/src/crypto/chacha_poly_aead.cpp
@@ -50,13 +50,13 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
// 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)) ||
+ (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))) {
+ (!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];
+ unsigned char expected_tag[Poly1305::TAGLEN], poly_key[Poly1305::KEYLEN];
memset(poly_key, 0, sizeof(poly_key));
// block counter 0 for the poly1305 key
@@ -67,18 +67,20 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
// 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);
+ const unsigned char* tag = src + src_len - Poly1305::TAGLEN;
+ Poly1305{MakeByteSpan(poly_key)}
+ .Update(AsBytes(Span{src, src_len - Poly1305::TAGLEN}))
+ .Finalize(MakeWritableByteSpan(expected_tag));
// constant time compare the calculated MAC with the provided MAC
- if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
+ 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 convert it in decryption
- src_len -= POLY1305_TAGLEN;
+ src_len -= Poly1305::TAGLEN;
}
// calculate and cache the next 64byte keystream block if requested sequence number is not yet the cache
@@ -99,7 +101,9 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
// 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);
+ Poly1305{MakeByteSpan(poly_key)}
+ .Update(AsBytes(Span{dest, src_len}))
+ .Finalize(AsWritableBytes(Span{dest + src_len, Poly1305::TAGLEN}));
}
// cleanse no longer required MAC and polykey
diff --git a/src/crypto/poly1305.cpp b/src/crypto/poly1305.cpp
index 8a86c9601c..7fcbab4108 100644
--- a/src/crypto/poly1305.cpp
+++ b/src/crypto/poly1305.cpp
@@ -2,140 +2,222 @@
// 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))
+namespace poly1305_donna {
-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;
+// Based on the public domain implementation by Andrew Moon
+// poly1305-donna-32.h from https://github.com/floodyberry/poly1305-donna
+
+void poly1305_init(poly1305_context *st, const unsigned char key[32]) noexcept {
+ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+ st->r[0] = (ReadLE32(&key[ 0]) ) & 0x3ffffff;
+ st->r[1] = (ReadLE32(&key[ 3]) >> 2) & 0x3ffff03;
+ st->r[2] = (ReadLE32(&key[ 6]) >> 4) & 0x3ffc0ff;
+ st->r[3] = (ReadLE32(&key[ 9]) >> 6) & 0x3f03fff;
+ st->r[4] = (ReadLE32(&key[12]) >> 8) & 0x00fffff;
+
+ /* h = 0 */
+ st->h[0] = 0;
+ st->h[1] = 0;
+ st->h[2] = 0;
+ st->h[3] = 0;
+ st->h[4] = 0;
+
+ /* save pad for later */
+ st->pad[0] = ReadLE32(&key[16]);
+ st->pad[1] = ReadLE32(&key[20]);
+ st->pad[2] = ReadLE32(&key[24]);
+ st->pad[3] = ReadLE32(&key[28]);
+
+ st->leftover = 0;
+ st->final = 0;
+}
+
+static void poly1305_blocks(poly1305_context *st, const unsigned char *m, size_t bytes) noexcept {
+ const uint32_t hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */
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;
+ uint32_t h0,h1,h2,h3,h4;
+ uint64_t d0,d1,d2,d3,d4;
+ uint32_t c;
+
+ r0 = st->r[0];
+ r1 = st->r[1];
+ r2 = st->r[2];
+ r3 = st->r[3];
+ r4 = st->r[4];
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);
+ h0 = st->h[0];
+ h1 = st->h[1];
+ h2 = st->h[2];
+ h3 = st->h[3];
+ h4 = st->h[4];
+
+ while (bytes >= POLY1305_BLOCK_SIZE) {
+ /* h += m[i] */
+ h0 += (ReadLE32(m+ 0) ) & 0x3ffffff;
+ h1 += (ReadLE32(m+ 3) >> 2) & 0x3ffffff;
+ h2 += (ReadLE32(m+ 6) >> 4) & 0x3ffffff;
+ h3 += (ReadLE32(m+ 9) >> 6) & 0x3ffffff;
+ h4 += (ReadLE32(m+12) >> 8) | hibit;
+
+ /* h *= r */
+ d0 = ((uint64_t)h0 * r0) + ((uint64_t)h1 * s4) + ((uint64_t)h2 * s3) + ((uint64_t)h3 * s2) + ((uint64_t)h4 * s1);
+ d1 = ((uint64_t)h0 * r1) + ((uint64_t)h1 * r0) + ((uint64_t)h2 * s4) + ((uint64_t)h3 * s3) + ((uint64_t)h4 * s2);
+ d2 = ((uint64_t)h0 * r2) + ((uint64_t)h1 * r1) + ((uint64_t)h2 * r0) + ((uint64_t)h3 * s4) + ((uint64_t)h4 * s3);
+ d3 = ((uint64_t)h0 * r3) + ((uint64_t)h1 * r2) + ((uint64_t)h2 * r1) + ((uint64_t)h3 * r0) + ((uint64_t)h4 * s4);
+ d4 = ((uint64_t)h0 * r4) + ((uint64_t)h1 * r3) + ((uint64_t)h2 * r2) + ((uint64_t)h3 * r1) + ((uint64_t)h4 * r0);
+
+ /* (partial) h %= p */
+ c = (uint32_t)(d0 >> 26); h0 = (uint32_t)d0 & 0x3ffffff;
+ d1 += c; c = (uint32_t)(d1 >> 26); h1 = (uint32_t)d1 & 0x3ffffff;
+ d2 += c; c = (uint32_t)(d2 >> 26); h2 = (uint32_t)d2 & 0x3ffffff;
+ d3 += c; c = (uint32_t)(d3 >> 26); h3 = (uint32_t)d3 & 0x3ffffff;
+ d4 += c; c = (uint32_t)(d4 >> 26); h4 = (uint32_t)d4 & 0x3ffffff;
+ h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ m += POLY1305_BLOCK_SIZE;
+ bytes -= POLY1305_BLOCK_SIZE;
+ }
+
+ st->h[0] = h0;
+ st->h[1] = h1;
+ st->h[2] = h2;
+ st->h[3] = h3;
+ st->h[4] = h4;
+}
+
+void poly1305_finish(poly1305_context *st, unsigned char mac[16]) noexcept {
+ uint32_t h0,h1,h2,h3,h4,c;
+ uint32_t g0,g1,g2,g3,g4;
+ uint64_t f;
+ uint32_t mask;
+
+ /* process the remaining block */
+ if (st->leftover) {
+ size_t i = st->leftover;
+ st->buffer[i++] = 1;
+ for (; i < POLY1305_BLOCK_SIZE; i++) {
+ st->buffer[i] = 0;
+ }
+ st->final = 1;
+ poly1305_blocks(st, st->buffer, POLY1305_BLOCK_SIZE);
+ }
+
+ /* fully carry h */
+ h0 = st->h[0];
+ h1 = st->h[1];
+ h2 = st->h[2];
+ h3 = st->h[3];
+ h4 = st->h[4];
+
+ c = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += c;
+
+ /* compute h + -p */
+ g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + c - (1UL << 26);
+
+ /* select h if h < p, or h + -p if h >= p */
+ mask = (g4 >> ((sizeof(uint32_t) * 8) - 1)) - 1;
+ g0 &= mask;
+ g1 &= mask;
+ g2 &= mask;
+ g3 &= mask;
+ g4 &= mask;
+ mask = ~mask;
+ h0 = (h0 & mask) | g0;
+ h1 = (h1 & mask) | g1;
+ h2 = (h2 & mask) | g2;
+ h3 = (h3 & mask) | g3;
+ h4 = (h4 & mask) | g4;
+
+ /* h = h % (2^128) */
+ h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
+ h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
+ h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
+ h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
+
+ /* mac = (h + pad) % (2^128) */
+ f = (uint64_t)h0 + st->pad[0] ; h0 = (uint32_t)f;
+ f = (uint64_t)h1 + st->pad[1] + (f >> 32); h1 = (uint32_t)f;
+ f = (uint64_t)h2 + st->pad[2] + (f >> 32); h2 = (uint32_t)f;
+ f = (uint64_t)h3 + st->pad[3] + (f >> 32); h3 = (uint32_t)f;
+
+ WriteLE32(mac + 0, h0);
+ WriteLE32(mac + 4, h1);
+ WriteLE32(mac + 8, h2);
+ WriteLE32(mac + 12, h3);
+
+ /* zero out the state */
+ st->h[0] = 0;
+ st->h[1] = 0;
+ st->h[2] = 0;
+ st->h[3] = 0;
+ st->h[4] = 0;
+ st->r[0] = 0;
+ st->r[1] = 0;
+ st->r[2] = 0;
+ st->r[3] = 0;
+ st->r[4] = 0;
+ st->pad[0] = 0;
+ st->pad[1] = 0;
+ st->pad[2] = 0;
+ st->pad[3] = 0;
}
+
+void poly1305_update(poly1305_context *st, const unsigned char *m, size_t bytes) noexcept {
+ size_t i;
+
+ /* handle leftover */
+ if (st->leftover) {
+ size_t want = (POLY1305_BLOCK_SIZE - st->leftover);
+ if (want > bytes) {
+ want = bytes;
+ }
+ for (i = 0; i < want; i++) {
+ st->buffer[st->leftover + i] = m[i];
+ }
+ bytes -= want;
+ m += want;
+ st->leftover += want;
+ if (st->leftover < POLY1305_BLOCK_SIZE) return;
+ poly1305_blocks(st, st->buffer, POLY1305_BLOCK_SIZE);
+ st->leftover = 0;
+ }
+
+ /* process full blocks */
+ if (bytes >= POLY1305_BLOCK_SIZE) {
+ size_t want = (bytes & ~(POLY1305_BLOCK_SIZE - 1));
+ poly1305_blocks(st, m, want);
+ m += want;
+ bytes -= want;
+ }
+
+ /* store leftover */
+ if (bytes) {
+ for (i = 0; i < bytes; i++) {
+ st->buffer[st->leftover + i] = m[i];
+ }
+ st->leftover += bytes;
+ }
+}
+
+} // namespace poly1305_donna
diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h
index 650e35bbca..592c090f57 100644
--- a/src/crypto/poly1305.h
+++ b/src/crypto/poly1305.h
@@ -5,13 +5,66 @@
#ifndef BITCOIN_CRYPTO_POLY1305_H
#define BITCOIN_CRYPTO_POLY1305_H
+#include <span.h>
+
+#include <cassert>
#include <cstdlib>
#include <stdint.h>
-#define POLY1305_KEYLEN 32
-#define POLY1305_TAGLEN 16
+#define POLY1305_BLOCK_SIZE 16
+
+namespace poly1305_donna {
+
+// Based on the public domain implementation by Andrew Moon
+// poly1305-donna-32.h from https://github.com/floodyberry/poly1305-donna
+
+typedef struct {
+ uint32_t r[5];
+ uint32_t h[5];
+ uint32_t pad[4];
+ size_t leftover;
+ unsigned char buffer[POLY1305_BLOCK_SIZE];
+ unsigned char final;
+} poly1305_context;
+
+void poly1305_init(poly1305_context *st, const unsigned char key[32]) noexcept;
+void poly1305_update(poly1305_context *st, const unsigned char *m, size_t bytes) noexcept;
+void poly1305_finish(poly1305_context *st, unsigned char mac[16]) noexcept;
+
+} // namespace poly1305_donna
+
+/** C++ wrapper with std::byte Span interface around poly1305_donna code. */
+class Poly1305
+{
+ poly1305_donna::poly1305_context m_ctx;
+
+public:
+ /** Length of the output produced by Finalize(). */
+ static constexpr unsigned TAGLEN{16};
+
+ /** Length of the keys expected by the constructor. */
+ static constexpr unsigned KEYLEN{32};
+
+ /** Construct a Poly1305 object with a given 32-byte key. */
+ Poly1305(Span<const std::byte> key) noexcept
+ {
+ assert(key.size() == KEYLEN);
+ poly1305_donna::poly1305_init(&m_ctx, UCharCast(key.data()));
+ }
+
+ /** Process message bytes. */
+ Poly1305& Update(Span<const std::byte> msg) noexcept
+ {
+ poly1305_donna::poly1305_update(&m_ctx, UCharCast(msg.data()), msg.size());
+ return *this;
+ }
-void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen,
- const unsigned char key[POLY1305_KEYLEN]);
+ /** Write authentication tag to 16-byte out. */
+ void Finalize(Span<std::byte> out) noexcept
+ {
+ assert(out.size() == TAGLEN);
+ poly1305_donna::poly1305_finish(&m_ctx, UCharCast(out.data()));
+ }
+};
#endif // BITCOIN_CRYPTO_POLY1305_H
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index b29df832b4..8332f54591 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -184,13 +184,28 @@ static void TestChaCha20(const std::string &hex_message, const std::string &hexk
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());
+ auto key = ParseHex<std::byte>(hexkey);
+ auto m = ParseHex<std::byte>(hexmessage);
+ auto tag = ParseHex<std::byte>(hextag);
+ std::vector<std::byte> tagres(Poly1305::TAGLEN);
+ Poly1305{key}.Update(m).Finalize(tagres);
BOOST_CHECK(tag == tagres);
+
+ // Test incremental interface
+ for (int splits = 0; splits < 10; ++splits) {
+ for (int iter = 0; iter < 10; ++iter) {
+ auto data = Span{m};
+ Poly1305 poly1305{key};
+ for (int chunk = 0; chunk < splits; ++chunk) {
+ size_t now = InsecureRandRange(data.size() + 1);
+ poly1305.Update(data.first(now));
+ data = data.subspan(now);
+ }
+ tagres.assign(Poly1305::TAGLEN, std::byte{});
+ poly1305.Update(data).Finalize(tagres);
+ BOOST_CHECK(tag == tagres);
+ }
+ }
}
static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &salt_hex, const std::string &info_hex, const std::string &okm_check_hex) {
@@ -751,6 +766,57 @@ BOOST_AUTO_TEST_CASE(poly1305_testvector)
TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000000000000000000000000000",
"0100000000000000040000000000000000000000000000000000000000000000",
"13000000000000000000000000000000");
+
+ // Tests from https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna.c
+ TestPoly1305("8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a"
+ "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738"
+ "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da"
+ "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74"
+ "e355a5",
+ "eea6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff880",
+ "f3ffc7703f9400e52a7dfb4b3d3305d9");
+ {
+ // mac of the macs of messages of length 0 to 256, where the key and messages have all
+ // their values set to the length.
+ auto total_key = ParseHex<std::byte>("01020304050607fffefdfcfbfaf9ffffffffffffffffffffffffffff00000000");
+ Poly1305 total_ctx(total_key);
+ for (unsigned i = 0; i < 256; ++i) {
+ std::vector<std::byte> key(32, std::byte{(uint8_t)i});
+ std::vector<std::byte> msg(i, std::byte{(uint8_t)i});
+ std::array<std::byte, Poly1305::TAGLEN> tag;
+ Poly1305{key}.Update(msg).Finalize(tag);
+ total_ctx.Update(tag);
+ }
+ std::vector<std::byte> total_tag(Poly1305::TAGLEN);
+ total_ctx.Finalize(total_tag);
+ BOOST_CHECK(total_tag == ParseHex<std::byte>("64afe2e8d6ad7bbdd287f97c44623d39"));
+ }
+
+ // Tests with sparse messages and random keys.
+ TestPoly1305("000000000000000000000094000000000000b07c4300000000002c002600d500"
+ "00000000000000000000000000bc58000000000000000000c9000000dd000000"
+ "00000000000000d34c000000000000000000000000f9009100000000000000c2"
+ "4b0000e900000000000000000000000000000000000e00000027000074000000"
+ "0000000003000000000000f1000000000000dce2000000000000003900000000"
+ "0000000000000000000000000000000000000000000000520000000000000000"
+ "000000000000000000000000009500000000000000000000000000cf00826700"
+ "000000a900000000000000000000000000000000000000000079000000000000"
+ "0000de0000004c000000000033000000000000000000000000002800aa000000"
+ "00003300860000e000000000",
+ "6e543496db3cf677592989891ab021f58390feb84fb419fbc7bb516a60bfa302",
+ "7ea80968354d40d9d790b45310caf7f3");
+ TestPoly1305("0000005900000000c40000002f00000000000000000000000000000029690000"
+ "0000e8000037000000000000000000000000000b000000000000000000000000"
+ "000000000000000000000000001800006e0000000000a4000000000000000000"
+ "00000000000000004d00000000000000b0000000000000000000005a00000000"
+ "0000000000b7c300000000000000540000000000000000000000000a00000000"
+ "00005b0000000000000000000000000000000000002d00e70000000000000000"
+ "000000000000003400006800d700000000000000000000360000000000000000"
+ "00eb000000000000000000000000000000000000000000000000000028000000"
+ "37000000000000000000000000000000000000000000000000000000008f0000"
+ "000000000000000000000000",
+ "f0b659a4f3143d8a1e1dacb9a409fe7e7cd501dfb58b16a2623046c5d337922a",
+ "0e410fa9d7a40ac582e77546be9a72bb");
}
BOOST_AUTO_TEST_CASE(hkdf_hmac_sha256_l32_tests)
@@ -791,7 +857,7 @@ static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aa
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> 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;
diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
index 596614a71b..84ac65dd88 100644
--- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
+++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp
@@ -26,16 +26,16 @@ FUZZ_TARGET(crypto_chacha20_poly1305_aead)
uint64_t seqnr_aad = 0;
int aad_pos = 0;
size_t buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
- std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
- std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ std::vector<uint8_t> in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
+ std::vector<uint8_t> out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
bool is_encrypt = fuzzed_data_provider.ConsumeBool();
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
CallOneOf(
fuzzed_data_provider,
[&] {
buffer_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(64, 4096);
- in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
- out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
+ in = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
+ out = std::vector<uint8_t>(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0);
},
[&] {
(void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt);
diff --git a/src/test/fuzz/crypto_poly1305.cpp b/src/test/fuzz/crypto_poly1305.cpp
index ac555ed68c..f49729a34b 100644
--- a/src/test/fuzz/crypto_poly1305.cpp
+++ b/src/test/fuzz/crypto_poly1305.cpp
@@ -14,9 +14,40 @@ FUZZ_TARGET(crypto_poly1305)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
- const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, POLY1305_KEYLEN);
+ const std::vector<uint8_t> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, Poly1305::KEYLEN);
const std::vector<uint8_t> in = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- std::vector<uint8_t> tag_out(POLY1305_TAGLEN);
- poly1305_auth(tag_out.data(), in.data(), in.size(), key.data());
+ std::vector<std::byte> tag_out(Poly1305::TAGLEN);
+ Poly1305{MakeByteSpan(key)}.Update(MakeByteSpan(in)).Finalize(tag_out);
+}
+
+
+FUZZ_TARGET(crypto_poly1305_split)
+{
+ FuzzedDataProvider provider{buffer.data(), buffer.size()};
+
+ // Read key and instantiate two Poly1305 objects with it.
+ auto key = provider.ConsumeBytes<std::byte>(Poly1305::KEYLEN);
+ key.resize(Poly1305::KEYLEN);
+ Poly1305 poly_full{key}, poly_split{key};
+
+ // Vector that holds all bytes processed so far.
+ std::vector<std::byte> total_input;
+
+ // Process input in pieces.
+ LIMITED_WHILE(provider.remaining_bytes(), 100) {
+ auto in = provider.ConsumeRandomLengthString();
+ poly_split.Update(MakeByteSpan(in));
+ // Update total_input to match what was processed.
+ total_input.insert(total_input.end(), MakeByteSpan(in).begin(), MakeByteSpan(in).end());
+ }
+
+ // Process entire input at once.
+ poly_full.Update(total_input);
+
+ // Verify both agree.
+ std::array<std::byte, Poly1305::TAGLEN> tag_split, tag_full;
+ poly_split.Finalize(tag_split);
+ poly_full.Finalize(tag_full);
+ assert(tag_full == tag_split);
}