mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
Merge bitcoin/bitcoin#27993: Make poly1305 support incremental computation + modernize
4e5c933f6a
Switch all callers from poly1305_auth to Poly1305 class (Pieter Wuille)8871f7d1ae
tests: add more Poly1305 test vectors (Pieter Wuille)40e6c5b9fc
crypto: add Poly1305 class with std::byte Span interface (Pieter Wuille)50269b391f
crypto: switch poly1305 to incremental implementation (Pieter Wuille) Pull request description: Our current Poly1305 code (src/crypto/poly1305.*) only supports computing the entire tag in one go (the `poly1305_auth` function takes a key and message, and outputs the tag). However, the RFC8439 authenticated encryption (as used in BIP324, see #27634) scheme makes use of Poly1305 in a way where the message consists of 3 different pieces: * The additionally authenticated data (AAD), padded to 16 bytes. * The ciphertext, padded to 16 bytes. * The length of the AAD and the length of the ciphertext, together another 16 bytes. Implementing RFC8439 using the existing `poly1305_auth` function requires creating a temporary copy with all these pieces of data concatenated just for the purpose of computing the tag (the approach used in #25361). This PR replaces the poly1305 code with new code from https://github.com/floodyberry/poly1305-donna (with minor adjustments to make it match our coding style and use our utility functions, documented in the commit) which supports incremental operation, and then adds a C++ wrapper interface using std::byte Spans around it, and adds tests that incremental and all-at-once computation match. ACKs for top commit: achow101: ACK4e5c933f6a
theStack: ACK4e5c933f6a
stratospher: tested ACK4e5c933
. Tree-SHA512: df6e9a2a4a38a480f9e4360d3e3def5311673a727a4a85b008a084cf6843b260dc82cec7c73e1cecaaccbf10f3521a0ae7dba388b65d0b086770f7fbc5223e2a
This commit is contained in:
commit
306157ae92
8 changed files with 386 additions and 148 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen,
|
||||
const unsigned char key[POLY1305_KEYLEN]);
|
||||
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;
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue