Gigabit+ throughput on CC20-P1305
Juniper Beatitudes [email protected]
Mon, 05 Jan 2026 18:08:34 -0600
6 files changed,
539 insertions(+),
90 deletions(-)
M
meson.build
→
meson.build
@@ -3,7 +3,6 @@
incdir = include_directories('tinycrypt') sources = [ - 'tinycrypt/chacha20-poly1305.c', 'tinycrypt/kangarootwelve128.c', 'tinycrypt/sha2.c', 'tinycrypt/x25519.c',@@ -13,6 +12,12 @@ if meson.get_compiler('c').has_define('__SIZEOF_INT128__')
sources += 'tinycrypt/ed25519_64bit.c' else sources += 'tinycrypt/ed25519.c' +endif + +if meson.get_compiler('c').has_define('__SIZEOF_INT128__') + sources += 'tinycrypt/chacha20_poly1305_64bit.c' +else + sources += 'tinycrypt/chacha20_poly1305.c' endif build_args = [@@ -42,6 +47,10 @@ 'tests/src/ed25519/verify_happy.cpp',
'tests/src/ed25519/verify_sad.cpp', 'tests/src/ed25519/sign.cpp', 'tests/src/ed25519/keygen.cpp', +] + +cc20_p1305_test_sources = [ + 'tests/src/chacha20-poly1305/all.cpp', ] if not meson.is_subproject() and not meson.is_cross_build()@@ -63,6 +72,17 @@ 'ed25519_tests',
executable( 'ed25519_tests', ed25519_test_sources, + include_directories: incdir, + link_with: target, + dependencies: test_dep, + install: false + ) + ) + test( + 'cc20_p1305_tests', + executable( + 'cc20_p1305_tests', + cc20_p1305_test_sources, include_directories: incdir, link_with: target, dependencies: test_dep,
A
tests/src/chacha20-poly1305/all.cpp
@@ -0,0 +1,30 @@
+extern "C" +{ +#include "tinycrypt/chacha20_poly1305.h" +} +#include "gtest/gtest.h" +#include <cstdint> +#include <cstring> + +TEST (ChaCha20_Poly1305, cc20_p1305_full) +{ + const uint8_t KEY[] + = { 0x42, 0x90, 0xbc, 0xb1, 0x54, 0x17, 0x35, 0x31, 0xf3, 0x14, 0xaf, + 0x57, 0xf3, 0xbe, 0x3b, 0x50, 0x06, 0xda, 0x37, 0x1e, 0xce, 0x27, + 0x2a, 0xfa, 0x1b, 0x5d, 0xbd, 0xd1, 0x10, 0x0a, 0x10, 0x07 }; + const uint8_t INPUT[] + = { 0x86, 0xd0, 0x99, 0x74, 0x84, 0x0b, 0xde, 0xd2, 0xa5, 0xca }; + const uint8_t NONCE[] = { 0xcd, 0x7c, 0xf6, 0x7b, 0xe3, 0x9c, 0x79, 0x4a }; + const uint8_t AAD[] + = { 0x87, 0xe2, 0x29, 0xd4, 0x50, 0x08, 0x45, 0xa0, 0x79, 0xc0 }; + const uint8_t EXPECTED_OUTPUT[] + = { 0xe3, 0xe4, 0x46, 0xf7, 0xed, 0xe9, 0xa1, 0x9b, 0x62, + 0xa4, 0x67, 0x7d, 0xab, 0xf4, 0xe3, 0xd2, 0x4b, 0x87, + 0x6b, 0xb2, 0x84, 0x75, 0x38, 0x96, 0xe1, 0xd6 }; + uint8_t actual_output[sizeof (EXPECTED_OUTPUT)]; + tct_aead_chacha20_poly1305_encrypt (AAD, sizeof (AAD), KEY, NONCE, INPUT, + sizeof (INPUT), actual_output, + actual_output + sizeof (INPUT)); + EXPECT_EQ (memcmp (actual_output, EXPECTED_OUTPUT, sizeof (EXPECTED_OUTPUT)), + 0); +}
M
tinycrypt/chacha20-poly1305.c
→
tinycrypt/chacha20_poly1305.c
@@ -1,12 +1,14 @@
#include <stdbool.h> #include <stdint.h> -#include "tinycrypt/chacha20-poly1305.h" +#include "tinycrypt/chacha20_poly1305.h" -static uint32_t -rotl_32 (uint32_t x, int c) +typedef uint32_t dwpacked __attribute__ ((vector_size (16))); + +static dwpacked +rotl_32 (dwpacked x, int c) { - return (x << c) | ((x & 0xffffffff) >> (32 - c)); + return (x << c) | (x >> (32 - c)); } static uint32_t@@ -47,44 +49,41 @@ } \
while (false); static void -cc20_block (const uint8_t *key, const uint32_t counter, const uint8_t *nonce, +cc20_block (const uint8_t *key, const uint16_t counter, const uint8_t *nonce, uint8_t *state_out) { - uint32_t initial_state[16] - = { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 }; - for (uint32_t i = 0; i < 8; ++i) - { - initial_state[i + 4] = from_le32 (key + i * 4); - } - initial_state[12] = counter; - for (uint32_t i = 0; i < 3; ++i) - { - initial_state[i + 13] = from_le32 (nonce + i * 4); - } - uint32_t state[16]; - for (uint32_t i = 0; i < 16; ++i) + dwpacked initial_state[4] + = { { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 } }; + initial_state[1] = (dwpacked){ from_le32 (key), from_le32 (key + 4), + from_le32 (key + 8), from_le32 (key + 12) }; + initial_state[2] = (dwpacked){ from_le32 (key + 16), from_le32 (key + 20), + from_le32 (key + 24), from_le32 (key + 28) }; + initial_state[3] = (dwpacked){ counter & 0xff, (counter >> 8) & 0xff, + from_le32 (nonce), from_le32 (nonce + 4) }; + dwpacked state[4]; + for (uint32_t i = 0; i < 4; ++i) { state[i] = initial_state[i]; } for (uint32_t i = 0; i < 10; ++i) { - CC20_QR (state, 0, 4, 8, 12); - CC20_QR (state, 1, 5, 9, 13); - CC20_QR (state, 2, 6, 10, 14); - CC20_QR (state, 3, 7, 11, 15); - CC20_QR (state, 0, 5, 10, 15); - CC20_QR (state, 1, 6, 11, 12); - CC20_QR (state, 2, 7, 8, 13); - CC20_QR (state, 3, 4, 9, 14); + CC20_QR (state, 0, 1, 2, 3); + state[1] = __builtin_shufflevector (state[1], state[1], 1, 2, 3, 0); + state[2] = __builtin_shufflevector (state[2], state[2], 2, 3, 0, 1); + state[3] = __builtin_shufflevector (state[3], state[3], 3, 0, 1, 2); + CC20_QR (state, 0, 1, 2, 3); + state[1] = __builtin_shufflevector (state[1], state[1], 3, 0, 1, 2); + state[2] = __builtin_shufflevector (state[2], state[2], 2, 3, 0, 1); + state[3] = __builtin_shufflevector (state[3], state[3], 1, 2, 3, 0); } - for (uint32_t i = 0; i < 16; ++i) + for (uint32_t i = 0; i < 4; ++i) { state[i] += initial_state[i]; } for (unsigned int i = 0; i < 16; ++i) { - to_le32 (state[i], state_out + 4 * i); + to_le32 (state[i / 4][i % 4], state_out + 4 * i); } }@@ -294,7 +293,8 @@ }
} static void -poly1305_mac_rolling (const uint8_t *msg, const uint8_t *key, uint8_t *a) +poly1305_mac_rolling (const uint8_t *msg, const uint8_t *key, + const uint8_t blocklen, uint8_t *a) { uint8_t r[32]; for (unsigned int i = 0; i < 32; ++i)@@ -308,15 +308,15 @@ }
poly1305_clamp (r); uint8_t n[32]; - for (unsigned int i = 0; i < 32; ++i) + for (unsigned int i = blocklen; i < 32; ++i) { n[i] = 0x0; } - for (unsigned int i = 0; i < 16; ++i) + for (unsigned int i = 0; i < 16 && i < blocklen; ++i) { n[i] = msg[i]; } - n[16] = 0x1; + n[blocklen] = 0x1; add256 (a, n); uint8_t intermediate[33]; mult256 (r, a, intermediate);@@ -347,12 +347,6 @@ }
add256 (a, s); } -static void -poly1305_keygen (const uint8_t *key, const uint8_t *nonce, uint8_t *out) -{ - cc20_block (key, 0, nonce, out); -} - void tct_aead_chacha20_poly1305 (const uint8_t *aad, const uint64_t aad_len, const uint8_t *key, const uint8_t *nonce,@@ -360,53 +354,41 @@ const uint8_t *plaintext,
const uint64_t plaintext_len, uint8_t *cipher_out, uint8_t *mac_out) { - uint8_t otk[32]; - poly1305_keygen (key, nonce, otk); + uint8_t otk[64]; + cc20_block (key, 0, nonce, otk); cc20_encrypt (key, 1, nonce, plaintext, plaintext_len, cipher_out); poly1305_mac_init (mac_out); uint8_t buf[16]; - for (uint64_t i = 0; i < aad_len / 16; ++i) + uint64_t total_len = 16 + aad_len + plaintext_len; + uint8_t aad_len_le[8], plaintext_len_le[8]; + to_le64 (aad_len, aad_len_le); + to_le64 (plaintext_len, plaintext_len_le); + for (unsigned int i = 0; i < total_len; ++i) { - for (unsigned int j = 0; j < 16; ++j) + if (i < aad_len) + { + buf[i % 16] = aad[i]; + } + else if (i < aad_len + 8) + { + buf[i % 16] = aad_len_le[i - aad_len]; + } + else if (i < aad_len + 8 + plaintext_len) + { + buf[i % 16] = cipher_out[i - aad_len - 8]; + } + else if (i < aad_len + 16 + plaintext_len) { - buf[j] = aad[i * 16 + j]; + buf[i % 16] = plaintext_len_le[i - aad_len - 16 - plaintext_len]; } - poly1305_mac_rolling (buf, otk, mac_out); - } - for (unsigned int i = 0; i < aad_len % 16; ++i) - { - buf[i] = aad[(aad_len / 16) * 16 + i]; - } - for (unsigned int i = aad_len % 16; i < 16; ++i) - { - buf[i] = 0x0; - } - if (aad_len % 16 != 0) - { - poly1305_mac_rolling (buf, otk, mac_out); - } - for (uint64_t i = 0; i < plaintext_len / 16; ++i) - { - for (unsigned int j = 0; j < 16; ++j) + if (i % 16 == 15) { - buf[j] = cipher_out[i * 16 + j]; + poly1305_mac_rolling (buf, otk, 16, mac_out); } - poly1305_mac_rolling (buf, otk, mac_out); } - for (unsigned int i = 0; i < plaintext_len % 16; ++i) + if (total_len % 16 != 0) { - buf[i] = cipher_out[(plaintext_len / 16) * 16 + i]; + poly1305_mac_rolling (buf, otk, total_len % 16, mac_out); } - for (unsigned int i = plaintext_len % 16; i < 16; ++i) - { - buf[i] = 0x0; - } - if (plaintext_len % 16 != 0) - { - poly1305_mac_rolling (buf, otk, mac_out); - } - to_le64 (aad_len, buf); - to_le64 (plaintext_len, buf + 8); - poly1305_mac_rolling (buf, otk, mac_out); poly1305_mac_finish (mac_out, otk); }
D
tinycrypt/chacha20-poly1305.h
@@ -1,14 +0,0 @@
-#ifndef TNT_CC20_P1305_H -#define TNT_CC20_P1305_H - -#include <stdint.h> - -/// Implements the AEAD-ChaCha20-Poly1305 encryption and MAC algorithm defined -/// in RFC 8439 -void tct_aead_chacha20_poly1305 (const uint8_t *aad, const uint64_t aad_len, - const uint8_t *key, const uint8_t *nonce, - const uint8_t *plaintext, - const uint64_t plaintext_len, - uint8_t *cipher_out, uint8_t *mac_out); - -#endif
A
tinycrypt/chacha20_poly1305.h
@@ -0,0 +1,20 @@
+#ifndef TNT_CC20_P1305_H +#define TNT_CC20_P1305_H + +#include <stdbool.h> +#include <stdint.h> + +/// Implements the AEAD-ChaCha20-Poly1305 encryption and MAC algorithm defined +/// in RFC 8439 +void tct_aead_chacha20_poly1305_encrypt ( + const uint8_t *aad, const uint64_t aad_len, const uint8_t *key, + const uint8_t *nonce, const uint8_t *plaintext, + const uint64_t plaintext_len, uint8_t *cipher_out, uint8_t *mac_out); + +/// Decrypts and verifies an AEAD-ChaCha20-Poly1305 frame +bool tct_aead_chacha20_poly1305_decrypt_and_verify ( + const uint8_t *aad, const uint64_t aad_len, const uint8_t *key, + const uint8_t *nonce, const uint8_t *frame, const uint64_t ciphertext_len, + uint8_t *plaintext_out); + +#endif
A
tinycrypt/chacha20_poly1305_64bit.c
@@ -0,0 +1,411 @@
+#include <stdbool.h> +#include <stdint.h> + +#include "tinycrypt/chacha20_poly1305.h" + +typedef uint32_t dwpacked __attribute__ ((vector_size (16))); + +static dwpacked +rotl_32 (dwpacked x, int c) +{ + return (x << c) | (x >> (32 - c)); +} + +static uint32_t +from_le32 (const uint8_t *x) +{ + uint32_t u = x[3]; + u = (u << 8) | x[2]; + u = (u << 8) | x[1]; + return (u << 8) | x[0]; +} + +static void +to_le32 (uint32_t u, uint8_t *x) +{ + for (unsigned int i = 0; i < 4; ++i) + { + x[i] = u & 0xFF; + u >>= 8; + } +} + +#define CC20_QR(state, a, b, c, d) \ + do \ + { \ + state[a] += state[b]; \ + state[d] ^= state[a]; \ + state[d] = rotl_32 (state[d], 16); \ + state[c] += state[d]; \ + state[b] ^= state[c]; \ + state[b] = rotl_32 (state[b], 12); \ + state[a] += state[b]; \ + state[d] ^= state[a]; \ + state[d] = rotl_32 (state[d], 8); \ + state[c] += state[d]; \ + state[b] ^= state[c]; \ + state[b] = rotl_32 (state[b], 7); \ + } \ + while (false); + +static void +cc20_block (const uint8_t *key, const uint16_t counter, const uint8_t *nonce, + uint8_t *state_out) +{ + dwpacked initial_state[4] + = { { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 } }; + initial_state[1] = (dwpacked){ from_le32 (key), from_le32 (key + 4), + from_le32 (key + 8), from_le32 (key + 12) }; + initial_state[2] = (dwpacked){ from_le32 (key + 16), from_le32 (key + 20), + from_le32 (key + 24), from_le32 (key + 28) }; + initial_state[3] = (dwpacked){ counter & 0xff, (counter >> 8) & 0xff, + from_le32 (nonce), from_le32 (nonce + 4) }; + dwpacked state[4]; + for (uint32_t i = 0; i < 4; ++i) + { + state[i] = initial_state[i]; + } + for (uint32_t i = 0; i < 10; ++i) + { + CC20_QR (state, 0, 1, 2, 3); + state[1] = __builtin_shufflevector (state[1], state[1], 1, 2, 3, 0); + state[2] = __builtin_shufflevector (state[2], state[2], 2, 3, 0, 1); + state[3] = __builtin_shufflevector (state[3], state[3], 3, 0, 1, 2); + CC20_QR (state, 0, 1, 2, 3); + state[1] = __builtin_shufflevector (state[1], state[1], 3, 0, 1, 2); + state[2] = __builtin_shufflevector (state[2], state[2], 2, 3, 0, 1); + state[3] = __builtin_shufflevector (state[3], state[3], 1, 2, 3, 0); + } + + for (uint32_t i = 0; i < 4; ++i) + { + state[i] += initial_state[i]; + } + for (unsigned int i = 0; i < 16; ++i) + { + to_le32 (state[i / 4][i % 4], state_out + 4 * i); + } +} + +static void +cc20_encrypt (const uint8_t *key, const uint32_t counter, const uint8_t *nonce, + const uint8_t *plaintext, const uint32_t plaintext_len, + uint8_t *encrypted_out) +{ + uint8_t key_stream[64]; + for (uint32_t i = 0; i < plaintext_len / 64; ++i) + { + cc20_block (key, counter + i, nonce, key_stream); + for (uint32_t j = 0; j < 64; ++j) + { + encrypted_out[i * 64 + j] = plaintext[i * 64 + j] ^ key_stream[j]; + } + } + if ((plaintext_len % 64) != 0) + { + uint32_t i = plaintext_len / 64; + cc20_block (key, counter + i, nonce, key_stream); + for (uint32_t j = 0; j < plaintext_len % 64; ++j) + { + encrypted_out[i * 64 + j] = plaintext[i * 64 + j] ^ key_stream[j]; + } + } +} + +static void +to_le64 (uint64_t u, uint8_t *x) +{ + for (unsigned int i = 0; i < 8; ++i) + { + x[i] = u & 0xff; + u >>= 8; + } +} + +static uint64_t +from_le64 (const uint8_t *x) +{ + uint64_t u = 0; + for (unsigned int i = 0; i < 8; ++i) + { + u <<= 8; + u |= x[7 - i]; + } + return u; +} + +static void +poly1305_clamp (uint8_t *r) +{ + r[3] &= 15; + r[7] &= 15; + r[11] &= 15; + r[15] &= 15; + r[4] &= 252; + r[8] &= 252; + r[12] &= 252; +} + +static void +add256 (uint64_t *h, const uint64_t *c) +{ + __uint128_t accumulator = 0; + for (unsigned int i = 0; i < 4; ++i) + { + accumulator += (__uint128_t)h[i] + (__uint128_t)c[i]; + h[i] = accumulator & 0xffffffffffffffff; + accumulator >>= 64; + } +} + +static void +sub320 (uint64_t *h, const uint64_t *c) +{ + __uint128_t acc = 0; + for (unsigned int i = 0; i < 5; ++i) + { + acc += (__uint128_t)(0xffffffffffffffff - h[i]) + (__uint128_t)c[i]; + h[i] = 0xffffffffffffffff - (acc & 0xffffffffffffffff); + acc >>= 64; + } +} + +static void +mult192 (const uint64_t *a, const uint64_t *b, uint64_t *out) +{ + uint64_t a_int[3], b_int[3]; + __uint128_t intermediate[6] = { + 0, 0, 0, 0, 0, 0, + }; + for (unsigned int i = 0; i < 3; ++i) + { + a_int[i] = a[i]; + b_int[i] = b[i]; + out[i] = 0x0; + out[3 + i] = 0x0; + } + for (unsigned int i = 0; i < 3; ++i) + { + for (unsigned int j = 0; j < 3; ++j) + { + intermediate[i + j] += (__uint128_t)a_int[i] * (__uint128_t)b_int[j]; + intermediate[i + j + 1] += intermediate[i + j] >> 64; + intermediate[i + j] &= 0xffffffffffffffff; + } + } + for (unsigned int k = 0; k < 5; ++k) + { + intermediate[k + 1] += intermediate[k] >> 64; + out[k] = intermediate[k] & 0xffffffffffffffff; + } + out[5] = intermediate[5] & 0xffffffffffffffff; +} + +static void +shr320_by_130 (const uint64_t *in, uint64_t *out) +{ + out[0] = (in[2] >> 2) | (in[3] << 62); + out[1] = (in[3] >> 2) | (in[4] << 62); + out[2] = in[4] >> 2; +} + +static bool +greater320 (const uint64_t *a, const uint64_t *b) +{ + uint64_t a_int[5] = { a[0], a[1], a[2], a[3], a[4] }; + sub320 (a_int, b); + return !(a_int[4] & (1ULL << 63)); +} + +static void +modp320 (uint64_t *in, uint64_t *out) +{ + const uint64_t P[5] + = { 0xfffffffffffffffb, 0xffffffffffffffff, 0x3, 0x0, 0x0 }; + const uint64_t ZERO[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 }; + uint64_t approx_quotient[3]; + uint64_t approx_dividend[6]; + uint64_t accumulator[5]; + for (unsigned int i = 0; i < 5; ++i) + { + accumulator[i] = in[i]; + } + for (unsigned int i = 0; i < 2; ++i) + { + shr320_by_130 (accumulator, approx_quotient); + mult192 (P, approx_quotient, approx_dividend); + sub320 (accumulator, approx_dividend); + } + const uint64_t *dummy = greater320 (accumulator, P) ? P : ZERO; + sub320 (accumulator, dummy); + for (unsigned int i = 0; i < 4; ++i) + { + out[i] = accumulator[i]; + } +} + +static void +poly1305_mac_rolling (const uint8_t *msg, const uint8_t *key, + const uint8_t blocklen, uint64_t *a) +{ + uint8_t r[32]; + for (unsigned int i = 0; i < 32; ++i) + { + r[i] = 0; + } + for (unsigned int i = 0; i < 16; ++i) + { + r[i] = key[i]; + } + + poly1305_clamp (r); + uint8_t n[32]; + for (unsigned int i = blocklen; i < 32; ++i) + { + n[i] = 0x0; + } + for (unsigned int i = 0; i < 16 && i < blocklen; ++i) + { + n[i] = msg[i]; + } + n[blocklen] = 0x1; + uint64_t n_chunked[4]; + uint64_t r_chunked[4]; + for (unsigned int i = 0; i < 4; ++i) + { + n_chunked[i] = from_le64 (n + 8 * i); + r_chunked[i] = from_le64 (r + 8 * i); + } + add256 (a, n_chunked); + uint64_t intermediate[6]; + mult192 (r_chunked, a, intermediate); + modp320 (intermediate, a); +} + +static void +poly1305_mac_init (uint64_t *a) +{ + for (unsigned int i = 0; i < 4; ++i) + { + a[i] = 0x0; + } +} + +static void +poly1305_mac_finish (uint64_t *a, const uint8_t *key) +{ + uint64_t s[4]; + for (unsigned int i = 2; i < 4; ++i) + { + s[i] = 0; + } + for (unsigned int i = 0; i < 2; ++i) + { + s[i] = from_le64 (key + 16 + 8 * i); + } + add256 (a, s); +} + +bool +tct_aead_chacha20_poly1305_decrypt_and_verify ( + const uint8_t *aad, const uint64_t aad_len, const uint8_t *key, + const uint8_t *nonce, const uint8_t *frame, const uint64_t ciphertext_len, + uint8_t *plaintext_out) +{ + uint8_t otk[64]; + cc20_block (key, 0, nonce, otk); + uint64_t mac_chunked[4]; + poly1305_mac_init (mac_chunked); + uint8_t buf[16]; + uint64_t total_len = 16 + aad_len + ciphertext_len; + uint8_t aad_len_le[8], ciphertext_len_le[8]; + to_le64 (aad_len, aad_len_le); + to_le64 (ciphertext_len, ciphertext_len_le); + for (unsigned int i = 0; i < total_len; ++i) + { + if (i < aad_len) + { + buf[i % 16] = aad[i]; + } + else if (i < aad_len + 8) + { + buf[i % 16] = aad_len_le[i - aad_len]; + } + else if (i < aad_len + 8 + ciphertext_len) + { + buf[i % 16] = frame[i - aad_len - 8]; + } + else if (i < aad_len + 16 + ciphertext_len) + { + buf[i % 16] = ciphertext_len_le[i - aad_len - 16 - ciphertext_len]; + } + if (i % 16 == 15) + { + poly1305_mac_rolling (buf, otk, 16, mac_chunked); + } + } + if (total_len % 16 != 0) + { + poly1305_mac_rolling (buf, otk, total_len % 16, mac_chunked); + } + poly1305_mac_finish (mac_chunked, otk); + if (((from_le64 (frame + ciphertext_len) ^ mac_chunked[0]) + | (from_le64 (frame + ciphertext_len + 8) ^ mac_chunked[1])) + != 0) + { + return false; + } + cc20_encrypt (key, 1, nonce, frame, ciphertext_len, plaintext_out); + return true; +} + +void +tct_aead_chacha20_poly1305_encrypt (const uint8_t *aad, const uint64_t aad_len, + const uint8_t *key, const uint8_t *nonce, + const uint8_t *plaintext, + const uint64_t plaintext_len, + uint8_t *cipher_out, uint8_t *mac_out) +{ + uint8_t otk[64]; + cc20_block (key, 0, nonce, otk); + cc20_encrypt (key, 1, nonce, plaintext, plaintext_len, cipher_out); + uint64_t mac_chunked[4]; + poly1305_mac_init (mac_chunked); + uint8_t buf[16]; + uint64_t total_len = 16 + aad_len + plaintext_len; + uint8_t aad_len_le[8], plaintext_len_le[8]; + to_le64 (aad_len, aad_len_le); + to_le64 (plaintext_len, plaintext_len_le); + for (unsigned int i = 0; i < total_len; ++i) + { + if (i < aad_len) + { + buf[i % 16] = aad[i]; + } + else if (i < aad_len + 8) + { + buf[i % 16] = aad_len_le[i - aad_len]; + } + else if (i < aad_len + 8 + plaintext_len) + { + buf[i % 16] = cipher_out[i - aad_len - 8]; + } + else if (i < aad_len + 16 + plaintext_len) + { + buf[i % 16] = plaintext_len_le[i - aad_len - 16 - plaintext_len]; + } + if (i % 16 == 15) + { + poly1305_mac_rolling (buf, otk, 16, mac_chunked); + } + } + if (total_len % 16 != 0) + { + poly1305_mac_rolling (buf, otk, total_len % 16, mac_chunked); + } + poly1305_mac_finish (mac_chunked, otk); + for (unsigned int i = 0; i < 2; ++i) + { + to_le64 (mac_chunked[i], mac_out + 8 * i); + } +}