From 50269b391fa18556bad72dc8c2fb4e2493a6a054 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 10 Jul 2023 14:02:14 -0400 Subject: [PATCH 1/4] crypto: switch poly1305 to incremental implementation This code is taken from poly1305-donna-32.h, poly1305-donna.h, poly1305-donna.c from https://github.com/floodyberry/poly1305-donna, commit e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781, with the following modifications: * Coding style (braces around one-line indented if/for loops). * Rename unsigned long (long) to uint32_t and uint64_t. * Rename poly1305_block_size to POLY1305_BLOCK_SIZE. * Adding noexcept to functions. * Merging poly1305_state_internal_t and poly1305_context types. * Merging code from multiple files. * Place all imported code in the poly1305_donna namespace. --- src/crypto/poly1305.cpp | 322 +++++++++++++++++++++++++--------------- src/crypto/poly1305.h | 21 +++ 2 files changed, 227 insertions(+), 116 deletions(-) diff --git a/src/crypto/poly1305.cpp b/src/crypto/poly1305.cpp index 8a86c9601c..c89383c73a 100644 --- a/src/crypto/poly1305.cpp +++ b/src/crypto/poly1305.cpp @@ -2,140 +2,230 @@ // 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 #include #include -#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]; + uint32_t h0,h1,h2,h3,h4; + uint64_t d0,d1,d2,d3,d4; + uint32_t c; - /* 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; + 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; + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; - /* full blocks */ - if (inlen < 16) goto poly1305_donna_atmost15bytes; -poly1305_donna_16bytes: - m += 16; - inlen -= 16; + 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; - t0 = ReadLE32(m-16); - t1 = ReadLE32(m-12); - t2 = ReadLE32(m-8); - t3 = ReadLE32(m-4); + /* 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); - 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); + /* (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; + } -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); + 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 + +void poly1305_auth(unsigned char mac[16], const unsigned char *m, size_t bytes, const unsigned char key[32]) { + using namespace poly1305_donna; + poly1305_context ctx; + poly1305_init(&ctx, key); + poly1305_update(&ctx, m, bytes); + poly1305_finish(&ctx, mac); } diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h index 650e35bbca..39b69e1cd4 100644 --- a/src/crypto/poly1305.h +++ b/src/crypto/poly1305.h @@ -10,6 +10,27 @@ #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 void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]); From 40e6c5b9fce92ffe64e91c2aba38bb2ed57bfbfb Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 10 Jul 2023 14:32:17 -0400 Subject: [PATCH 2/4] crypto: add Poly1305 class with std::byte Span interface --- src/crypto/poly1305.h | 37 +++++++++++++++++++++++++++++++ src/test/crypto_tests.cpp | 16 +++++++++++++ src/test/fuzz/crypto_poly1305.cpp | 31 ++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h index 39b69e1cd4..080543ffee 100644 --- a/src/crypto/poly1305.h +++ b/src/crypto/poly1305.h @@ -5,6 +5,9 @@ #ifndef BITCOIN_CRYPTO_POLY1305_H #define BITCOIN_CRYPTO_POLY1305_H +#include + +#include #include #include @@ -32,6 +35,40 @@ 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 = POLY1305_TAGLEN; + + /** Length of the keys expected by the constructor. */ + static constexpr unsigned KEYLEN = POLY1305_KEYLEN; + + /** Construct a Poly1305 object with a given 32-byte key. */ + Poly1305(Span key) noexcept + { + assert(key.size() == KEYLEN); + poly1305_donna::poly1305_init(&m_ctx, UCharCast(key.data())); + } + + /** Process message bytes. */ + Poly1305& Update(Span msg) noexcept + { + poly1305_donna::poly1305_update(&m_ctx, UCharCast(msg.data()), msg.size()); + return *this; + } + + /** Write authentication tag to 16-byte out. */ + void Finalize(Span out) noexcept + { + assert(out.size() == TAGLEN); + poly1305_donna::poly1305_finish(&m_ctx, UCharCast(out.data())); + } +}; + void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index b29df832b4..85494e1b20 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -191,6 +191,22 @@ static void TestPoly1305(const std::string &hexmessage, const std::string &hexke tagres.resize(POLY1305_TAGLEN); poly1305_auth(tagres.data(), m.data(), m.size(), key.data()); BOOST_CHECK(tag == tagres); + + // Test incremental interface + for (int splits = 0; splits < 10; ++splits) { + for (int iter = 0; iter < 10; ++iter) { + auto data = MakeByteSpan(m); + Poly1305 poly1305{MakeByteSpan(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, 0); + poly1305.Update(data).Finalize(MakeWritableByteSpan(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) { diff --git a/src/test/fuzz/crypto_poly1305.cpp b/src/test/fuzz/crypto_poly1305.cpp index ac555ed68c..94a7ed06e9 100644 --- a/src/test/fuzz/crypto_poly1305.cpp +++ b/src/test/fuzz/crypto_poly1305.cpp @@ -20,3 +20,34 @@ FUZZ_TARGET(crypto_poly1305) std::vector tag_out(POLY1305_TAGLEN); poly1305_auth(tag_out.data(), in.data(), in.size(), key.data()); } + + +FUZZ_TARGET(crypto_poly1305_split) +{ + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + // Read key and instantiate two Poly1305 objects with it. + auto key = provider.ConsumeBytes(Poly1305::KEYLEN); + key.resize(Poly1305::KEYLEN); + Poly1305 poly_full{key}, poly_split{key}; + + // Vector that holds all bytes processed so far. + std::vector 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 tag_split, tag_full; + poly_split.Finalize(tag_split); + poly_full.Finalize(tag_full); + assert(tag_full == tag_split); +} From 8871f7d1ae096839abcbf25a548319185acc01a2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 10 Jul 2023 22:26:25 -0400 Subject: [PATCH 3/4] tests: add more Poly1305 test vectors --- src/test/crypto_tests.cpp | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 85494e1b20..e147439632 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -767,6 +767,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("01020304050607fffefdfcfbfaf9ffffffffffffffffffffffffffff00000000"); + Poly1305 total_ctx(total_key); + for (unsigned i = 0; i < 256; ++i) { + std::vector key(32, std::byte{(uint8_t)i}); + std::vector msg(i, std::byte{(uint8_t)i}); + std::array tag; + Poly1305{key}.Update(msg).Finalize(tag); + total_ctx.Update(tag); + } + std::vector total_tag(Poly1305::TAGLEN); + total_ctx.Finalize(total_tag); + BOOST_CHECK(total_tag == ParseHex("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) From 4e5c933f6a40c07d1c68115f7979b89a5b2ba580 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 28 Jun 2023 10:52:12 -0400 Subject: [PATCH 4/4] Switch all callers from poly1305_auth to Poly1305 class This also removes the old poly1305_auth interface, as it no longer serves any function. The new Poly1305 class based interface is more modern and safe. --- src/bench/chacha_poly_aead.cpp | 4 ++-- src/bench/poly1305.cpp | 10 +++++---- src/crypto/chacha_poly_aead.cpp | 20 +++++++++++------- src/crypto/poly1305.cpp | 8 ------- src/crypto/poly1305.h | 9 ++------ src/test/crypto_tests.cpp | 21 +++++++++---------- .../fuzz/crypto_chacha20_poly1305_aead.cpp | 8 +++---- src/test/fuzz/crypto_poly1305.cpp | 6 +++--- 8 files changed, 39 insertions(+), 47 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 in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - std::vector out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + std::vector in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); + std::vector 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 #include +#include + /* 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 tag(POLY1305_TAGLEN, 0); - std::vector key(POLY1305_KEYLEN, 0); - std::vector in(buffersize, 0); + std::vector tag(Poly1305::TAGLEN, {}); + std::vector key(Poly1305::KEYLEN, {}); + std::vector 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 c89383c73a..7fcbab4108 100644 --- a/src/crypto/poly1305.cpp +++ b/src/crypto/poly1305.cpp @@ -221,11 +221,3 @@ void poly1305_update(poly1305_context *st, const unsigned char *m, size_t bytes) } } // namespace poly1305_donna - -void poly1305_auth(unsigned char mac[16], const unsigned char *m, size_t bytes, const unsigned char key[32]) { - using namespace poly1305_donna; - poly1305_context ctx; - poly1305_init(&ctx, key); - poly1305_update(&ctx, m, bytes); - poly1305_finish(&ctx, mac); -} diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h index 080543ffee..592c090f57 100644 --- a/src/crypto/poly1305.h +++ b/src/crypto/poly1305.h @@ -11,8 +11,6 @@ #include #include -#define POLY1305_KEYLEN 32 -#define POLY1305_TAGLEN 16 #define POLY1305_BLOCK_SIZE 16 namespace poly1305_donna { @@ -42,10 +40,10 @@ class Poly1305 public: /** Length of the output produced by Finalize(). */ - static constexpr unsigned TAGLEN = POLY1305_TAGLEN; + static constexpr unsigned TAGLEN{16}; /** Length of the keys expected by the constructor. */ - static constexpr unsigned KEYLEN = POLY1305_KEYLEN; + static constexpr unsigned KEYLEN{32}; /** Construct a Poly1305 object with a given 32-byte key. */ Poly1305(Span key) noexcept @@ -69,7 +67,4 @@ public: } }; -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 diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index e147439632..8332f54591 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -184,26 +184,25 @@ 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 key = ParseHex(hexkey); - std::vector m = ParseHex(hexmessage); - std::vector tag = ParseHex(hextag); - std::vector tagres; - tagres.resize(POLY1305_TAGLEN); - poly1305_auth(tagres.data(), m.data(), m.size(), key.data()); + auto key = ParseHex(hexkey); + auto m = ParseHex(hexmessage); + auto tag = ParseHex(hextag); + std::vector 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 = MakeByteSpan(m); - Poly1305 poly1305{MakeByteSpan(key)}; + 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, 0); - poly1305.Update(data).Finalize(MakeWritableByteSpan(tagres)); + tagres.assign(Poly1305::TAGLEN, std::byte{}); + poly1305.Update(data).Finalize(tagres); BOOST_CHECK(tag == tagres); } } @@ -858,7 +857,7 @@ static void TestChaCha20Poly1305AEAD(bool must_succeed, unsigned int expected_aa std::vector expected_ciphertext_and_mac = ParseHex(hex_encrypted_message); std::vector expected_ciphertext_and_mac_sequence999 = ParseHex(hex_encrypted_message_seq_999); - std::vector ciphertext_buf(plaintext_buf.size() + POLY1305_TAGLEN, 0); + std::vector ciphertext_buf(plaintext_buf.size() + Poly1305::TAGLEN, 0); std::vector plaintext_buf_new(plaintext_buf.size(), 0); std::vector 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(0, 4096); - std::vector in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - std::vector out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + std::vector in(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); + std::vector 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(64, 4096); - in = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - out = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + in = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + Poly1305::TAGLEN, 0); + out = std::vector(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 94a7ed06e9..f49729a34b 100644 --- a/src/test/fuzz/crypto_poly1305.cpp +++ b/src/test/fuzz/crypto_poly1305.cpp @@ -14,11 +14,11 @@ FUZZ_TARGET(crypto_poly1305) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, POLY1305_KEYLEN); + const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, Poly1305::KEYLEN); const std::vector in = ConsumeRandomLengthByteVector(fuzzed_data_provider); - std::vector tag_out(POLY1305_TAGLEN); - poly1305_auth(tag_out.data(), in.data(), in.size(), key.data()); + std::vector tag_out(Poly1305::TAGLEN); + Poly1305{MakeByteSpan(key)}.Update(MakeByteSpan(in)).Finalize(tag_out); }