Add Poly1305 and ChaCha20-Poly1305 AEAD with RFC 8439 tests

This commit is contained in:
Laan Tungir
2026-04-13 16:01:35 -04:00
parent 392b863292
commit 13ad24b676
4 changed files with 699 additions and 0 deletions

View File

@@ -500,6 +500,8 @@ detect_system_curl
SOURCES="nostr_core/crypto/nostr_secp256k1.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_aes.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_chacha20.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_poly1305.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_chacha20poly1305.c"
SOURCES="$SOURCES cjson/cJSON.c"
SOURCES="$SOURCES nostr_core/utils.c"
SOURCES="$SOURCES nostr_core/nostr_common.c"

View File

@@ -0,0 +1,192 @@
/*
* nostr_chacha20poly1305.c - ChaCha20-Poly1305 AEAD implementation
*
* RFC 8439 Section 2.8
*/
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// ChaCha20 private API (provided by nostr_chacha20.c)
int chacha20_block(const uint8_t key[32], uint32_t counter,
const uint8_t nonce[12], uint8_t output[64]);
int chacha20_encrypt(const uint8_t key[32], uint32_t counter,
const uint8_t nonce[12], const uint8_t* input,
uint8_t* output, size_t length);
// Poly1305 API (provided by nostr_poly1305.c)
int nostr_poly1305_mac(const unsigned char key[32], const unsigned char *msg,
size_t msg_len, unsigned char tag[16]);
static void store64_le(unsigned char out[8], uint64_t v) {
out[0] = (unsigned char)(v & 0xff);
out[1] = (unsigned char)((v >> 8) & 0xff);
out[2] = (unsigned char)((v >> 16) & 0xff);
out[3] = (unsigned char)((v >> 24) & 0xff);
out[4] = (unsigned char)((v >> 32) & 0xff);
out[5] = (unsigned char)((v >> 40) & 0xff);
out[6] = (unsigned char)((v >> 48) & 0xff);
out[7] = (unsigned char)((v >> 56) & 0xff);
}
static int constant_time_tag_eq(const unsigned char a[16], const unsigned char b[16]) {
unsigned char diff = 0;
size_t i;
for (i = 0; i < 16; i++) {
diff |= (unsigned char)(a[i] ^ b[i]);
}
return diff == 0;
}
static int build_poly1305_input(const unsigned char *aad, size_t aad_len,
const unsigned char *ciphertext, size_t ct_len,
unsigned char **out, size_t *out_len) {
size_t aad_pad = (16 - (aad_len % 16)) % 16;
size_t ct_pad = (16 - (ct_len % 16)) % 16;
size_t total = aad_len + aad_pad + ct_len + ct_pad + 16; // len(aad)||len(ct)
unsigned char *buf;
size_t pos = 0;
unsigned char len_block[16];
if (!out || !out_len) {
return -1;
}
buf = (unsigned char *)malloc(total);
if (!buf) {
return -1;
}
if (aad_len > 0 && aad) {
memcpy(buf + pos, aad, aad_len);
pos += aad_len;
}
if (aad_pad > 0) {
memset(buf + pos, 0, aad_pad);
pos += aad_pad;
}
if (ct_len > 0 && ciphertext) {
memcpy(buf + pos, ciphertext, ct_len);
pos += ct_len;
}
if (ct_pad > 0) {
memset(buf + pos, 0, ct_pad);
pos += ct_pad;
}
store64_le(len_block, (uint64_t)aad_len);
store64_le(len_block + 8, (uint64_t)ct_len);
memcpy(buf + pos, len_block, sizeof(len_block));
pos += sizeof(len_block);
*out = buf;
*out_len = pos;
return 0;
}
int nostr_chacha20poly1305_encrypt(const unsigned char key[32],
const unsigned char nonce[12],
const unsigned char *aad, size_t aad_len,
const unsigned char *plaintext, size_t pt_len,
unsigned char *ciphertext,
unsigned char tag[16]) {
unsigned char block0[64];
unsigned char poly_key[32];
unsigned char *poly_in = NULL;
size_t poly_in_len = 0;
int rc;
if (!key || !nonce || !ciphertext || !tag || (!plaintext && pt_len != 0) || (!aad && aad_len != 0)) {
return -1;
}
if (chacha20_block(key, 0, nonce, block0) != 0) {
return -1;
}
memcpy(poly_key, block0, sizeof(poly_key));
if (pt_len > 0) {
rc = chacha20_encrypt(key, 1, nonce, plaintext, ciphertext, pt_len);
if (rc != 0) {
memset(poly_key, 0, sizeof(poly_key));
return -1;
}
}
rc = build_poly1305_input(aad, aad_len, ciphertext, pt_len, &poly_in, &poly_in_len);
if (rc != 0) {
memset(poly_key, 0, sizeof(poly_key));
return -1;
}
rc = nostr_poly1305_mac(poly_key, poly_in, poly_in_len, tag);
memset(poly_key, 0, sizeof(poly_key));
memset(block0, 0, sizeof(block0));
memset(poly_in, 0, poly_in_len);
free(poly_in);
return rc;
}
int nostr_chacha20poly1305_decrypt(const unsigned char key[32],
const unsigned char nonce[12],
const unsigned char *aad, size_t aad_len,
const unsigned char *ciphertext, size_t ct_len,
const unsigned char tag[16],
unsigned char *plaintext) {
unsigned char block0[64];
unsigned char poly_key[32];
unsigned char expected_tag[16];
unsigned char *poly_in = NULL;
size_t poly_in_len = 0;
int rc;
if (!key || !nonce || !tag || !plaintext || (!ciphertext && ct_len != 0) || (!aad && aad_len != 0)) {
return -1;
}
if (chacha20_block(key, 0, nonce, block0) != 0) {
return -1;
}
memcpy(poly_key, block0, sizeof(poly_key));
rc = build_poly1305_input(aad, aad_len, ciphertext, ct_len, &poly_in, &poly_in_len);
if (rc != 0) {
memset(poly_key, 0, sizeof(poly_key));
return -1;
}
rc = nostr_poly1305_mac(poly_key, poly_in, poly_in_len, expected_tag);
if (rc != 0 || !constant_time_tag_eq(tag, expected_tag)) {
memset(poly_key, 0, sizeof(poly_key));
memset(block0, 0, sizeof(block0));
memset(expected_tag, 0, sizeof(expected_tag));
memset(poly_in, 0, poly_in_len);
free(poly_in);
return -1;
}
if (ct_len > 0) {
rc = chacha20_encrypt(key, 1, nonce, ciphertext, plaintext, ct_len);
if (rc != 0) {
memset(poly_key, 0, sizeof(poly_key));
memset(block0, 0, sizeof(block0));
memset(expected_tag, 0, sizeof(expected_tag));
memset(poly_in, 0, poly_in_len);
free(poly_in);
return -1;
}
}
memset(poly_key, 0, sizeof(poly_key));
memset(block0, 0, sizeof(block0));
memset(expected_tag, 0, sizeof(expected_tag));
memset(poly_in, 0, poly_in_len);
free(poly_in);
return 0;
}

View File

@@ -0,0 +1,262 @@
/*
* nostr_poly1305.c - Poly1305 message authentication code
*
* Public-domain style 32-bit implementation based on the poly1305-donna
* approach, adapted for nostr_core_lib naming and conventions.
*
* RFC 8439 Section 2.5
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
typedef struct {
uint32_t r0, r1, r2, r3, r4;
uint32_t s1, s2, s3, s4;
uint32_t h0, h1, h2, h3, h4;
uint32_t pad0, pad1, pad2, pad3;
size_t leftover;
unsigned char buffer[16];
unsigned char final;
} nostr_poly1305_ctx_t;
static uint32_t u8to32_le(const unsigned char *p) {
return ((uint32_t)p[0]) |
((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
}
static void u32to8_le(unsigned char *p, uint32_t v) {
p[0] = (unsigned char)(v & 0xff);
p[1] = (unsigned char)((v >> 8) & 0xff);
p[2] = (unsigned char)((v >> 16) & 0xff);
p[3] = (unsigned char)((v >> 24) & 0xff);
}
static void poly1305_blocks(nostr_poly1305_ctx_t *st, const unsigned char *m, size_t bytes) {
const uint32_t r0 = st->r0;
const uint32_t r1 = st->r1;
const uint32_t r2 = st->r2;
const uint32_t r3 = st->r3;
const uint32_t r4 = st->r4;
const uint32_t s1 = st->s1;
const uint32_t s2 = st->s2;
const uint32_t s3 = st->s3;
const uint32_t s4 = st->s4;
uint32_t h0 = st->h0;
uint32_t h1 = st->h1;
uint32_t h2 = st->h2;
uint32_t h3 = st->h3;
uint32_t h4 = st->h4;
const uint32_t hibit = st->final ? 0 : (1U << 24);
while (bytes >= 16) {
uint32_t t0 = u8to32_le(m + 0);
uint32_t t1 = u8to32_le(m + 4);
uint32_t t2 = u8to32_le(m + 8);
uint32_t t3 = u8to32_le(m + 12);
uint64_t d0, d1, d2, d3, d4;
uint32_t c;
h0 += ( t0 ) & 0x3ffffff;
h1 += (((t1 << 6) | (t0 >> 26)) ) & 0x3ffffff;
h2 += (((t2 << 12) | (t1 >> 20))) & 0x3ffffff;
h3 += (((t3 << 18) | (t2 >> 14))) & 0x3ffffff;
h4 += (( t3 >> 8) ) & 0x00ffffff;
h4 += hibit;
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);
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 &= 0x3ffffff;
h1 += c;
m += 16;
bytes -= 16;
}
st->h0 = h0;
st->h1 = h1;
st->h2 = h2;
st->h3 = h3;
st->h4 = h4;
}
void nostr_poly1305_init(nostr_poly1305_ctx_t *st, const unsigned char key[32]) {
uint32_t t0, t1, t2, t3;
t0 = u8to32_le(key + 0);
t1 = u8to32_le(key + 4);
t2 = u8to32_le(key + 8);
t3 = u8to32_le(key + 12);
st->r0 = ( t0 ) & 0x3ffffff;
st->r1 = (((t1 << 6) | (t0 >> 26)) ) & 0x3ffff03;
st->r2 = (((t2 << 12) | (t1 >> 20))) & 0x3ffc0ff;
st->r3 = (((t3 << 18) | (t2 >> 14))) & 0x3f03fff;
st->r4 = (( t3 >> 8) ) & 0x00fffff;
st->s1 = st->r1 * 5;
st->s2 = st->r2 * 5;
st->s3 = st->r3 * 5;
st->s4 = st->r4 * 5;
st->h0 = 0;
st->h1 = 0;
st->h2 = 0;
st->h3 = 0;
st->h4 = 0;
st->pad0 = u8to32_le(key + 16);
st->pad1 = u8to32_le(key + 20);
st->pad2 = u8to32_le(key + 24);
st->pad3 = u8to32_le(key + 28);
st->leftover = 0;
st->final = 0;
}
void nostr_poly1305_update(nostr_poly1305_ctx_t *st, const unsigned char *m, size_t bytes) {
size_t i;
if (st->leftover) {
size_t want = (size_t)(16 - 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 < 16) {
return;
}
poly1305_blocks(st, st->buffer, 16);
st->leftover = 0;
}
if (bytes >= 16) {
size_t want = bytes & ~(size_t)0xf;
poly1305_blocks(st, m, want);
m += want;
bytes -= want;
}
if (bytes) {
for (i = 0; i < bytes; i++) {
st->buffer[st->leftover + i] = m[i];
}
st->leftover += bytes;
}
}
void nostr_poly1305_final(nostr_poly1305_ctx_t *st, unsigned char tag[16]) {
uint32_t h0, h1, h2, h3, h4, c;
uint32_t g0, g1, g2, g3, g4;
uint64_t f;
uint32_t mask;
if (st->leftover) {
size_t i = st->leftover;
st->buffer[i++] = 1;
for (; i < 16; i++) {
st->buffer[i] = 0;
}
st->final = 1;
poly1305_blocks(st, st->buffer, 16);
}
h0 = st->h0;
h1 = st->h1;
h2 = st->h2;
h3 = st->h3;
h4 = st->h4;
c = h1 >> 26; h1 &= 0x3ffffff;
h2 += c;
c = h2 >> 26; h2 &= 0x3ffffff;
h3 += c;
c = h3 >> 26; h3 &= 0x3ffffff;
h4 += c;
c = h4 >> 26; h4 &= 0x3ffffff;
h0 += c * 5;
c = h0 >> 26; h0 &= 0x3ffffff;
h1 += c;
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 - (1U << 26);
mask = (g4 >> 31) - 1U;
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;
h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
h1 = ((h1 >> 6 ) | (h2 << 20)) & 0xffffffff;
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
h3 = ((h3 >> 18) | (h4 << 8 )) & 0xffffffff;
f = (uint64_t)h0 + st->pad0;
h0 = (uint32_t)f;
f = (uint64_t)h1 + st->pad1 + (f >> 32);
h1 = (uint32_t)f;
f = (uint64_t)h2 + st->pad2 + (f >> 32);
h2 = (uint32_t)f;
f = (uint64_t)h3 + st->pad3 + (f >> 32);
h3 = (uint32_t)f;
u32to8_le(tag + 0, h0);
u32to8_le(tag + 4, h1);
u32to8_le(tag + 8, h2);
u32to8_le(tag + 12, h3);
memset(st, 0, sizeof(*st));
}
int nostr_poly1305_mac(const unsigned char key[32], const unsigned char *msg,
size_t msg_len, unsigned char tag[16]) {
nostr_poly1305_ctx_t ctx;
if (!key || (!msg && msg_len != 0) || !tag) {
return -1;
}
nostr_poly1305_init(&ctx, key);
nostr_poly1305_update(&ctx, msg, msg_len);
nostr_poly1305_final(&ctx, tag);
return 0;
}

View File

@@ -0,0 +1,243 @@
/*
* ChaCha20-Poly1305 / Poly1305 Test Suite - RFC 8439 vectors
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
int nostr_poly1305_mac(const unsigned char key[32], const unsigned char *msg,
size_t msg_len, unsigned char tag[16]);
int nostr_chacha20poly1305_encrypt(const unsigned char key[32],
const unsigned char nonce[12],
const unsigned char *aad, size_t aad_len,
const unsigned char *plaintext, size_t pt_len,
unsigned char *ciphertext,
unsigned char tag[16]);
int nostr_chacha20poly1305_decrypt(const unsigned char key[32],
const unsigned char nonce[12],
const unsigned char *aad, size_t aad_len,
const unsigned char *ciphertext, size_t ct_len,
const unsigned char tag[16],
unsigned char *plaintext);
static int hex_to_bytes(const char *hex, uint8_t *out, size_t out_len) {
size_t i;
if (!hex || !out) return -1;
for (i = 0; i < out_len; i++) {
if (sscanf(hex + (i * 2), "%2hhx", &out[i]) != 1) {
return -1;
}
}
return 0;
}
static int bytes_equal(const uint8_t *a, const uint8_t *b, size_t len) {
return memcmp(a, b, len) == 0;
}
static void print_hex(const uint8_t *buf, size_t len) {
size_t i;
for (i = 0; i < len; i++) {
printf("%02x", buf[i]);
}
}
static int test_poly1305_rfc8439_2_5_2(void) {
const char *key_hex =
"85d6be7857556d337f4452fe42d506a8"
"0103808afb0db2fd4abff6af4149f51b";
const char *msg = "Cryptographic Forum Research Group";
const char *expected_tag_hex = "a8061dc1305136c6c22b8baf0c0127a9";
uint8_t key[32];
uint8_t tag[16];
uint8_t expected[16];
printf("=== Poly1305 RFC 8439 §2.5.2 ===\n");
if (hex_to_bytes(key_hex, key, sizeof(key)) != 0 ||
hex_to_bytes(expected_tag_hex, expected, sizeof(expected)) != 0) {
printf("❌ hex parse failed\n\n");
return 0;
}
if (nostr_poly1305_mac(key, (const unsigned char *)msg, strlen(msg), tag) != 0) {
printf("❌ nostr_poly1305_mac failed\n\n");
return 0;
}
if (!bytes_equal(tag, expected, sizeof(tag))) {
printf("❌ tag mismatch\nExpected: ");
print_hex(expected, sizeof(expected));
printf("\nGot: ");
print_hex(tag, sizeof(tag));
printf("\n\n");
return 0;
}
printf("✅ passed\n\n");
return 1;
}
static int test_aead_rfc8439_appendix_a_5(void) {
const char *key_hex =
"1c9240a5eb55d38af333888604f6b5f0"
"473917c1402b80099dca5cbc207075c0";
const char *nonce_hex = "000000000102030405060708";
const char *aad_hex = "f33388860000000000004e91";
const char *plaintext_hex =
"496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420"
"666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d61792062652075706461"
"7465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d65"
"6e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f20757365"
"20496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f722074"
"6f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f6772"
"6573732e2fe2809d";
const char *expected_ct_hex =
"64a0861575861af460f062c79be643bd"
"5e805cfd345cf389f108670ac76c8cb2"
"4c6cfc18755d43eea09ee94e382d26b0"
"bdb7b73c321b0100d4f03b7f355894cf"
"332f830e710b97ce98c8a84abd0b9481"
"14ad176e008d33bd60f982b1ff37c855"
"9797a06ef4f0ef61c186324e2b350638"
"3606907b6a7c02b0f9f6157b53c867e4"
"b9166c767b804d46a59b5216cde7a4e9"
"9040c5a40433225ee282a1b0a06c523e"
"af4534d7f83fa1155b0047718cbc546a"
"0d072b04b3564eea1b422273f548271a"
"0bb2316053fa76991955ebd63159434e"
"cebb4e466dae5a1073a6727627097a10"
"49e617d91d361094fa68f0ff77987130"
"305beaba2eda04df997b714d6c6f2c29"
"a6ad5cb4022b02709b";
const char *expected_tag_hex = "eead9d67890cbb22392336fea1851f38";
uint8_t key[32], nonce[12], aad[12], expected_tag[16];
uint8_t plaintext[1024], ciphertext[1024], decrypted[1024], expected_ct[1024];
uint8_t tag[16];
size_t pt_len = strlen(plaintext_hex) / 2;
size_t aad_len = strlen(aad_hex) / 2;
size_t ct_len = strlen(expected_ct_hex) / 2;
printf("=== ChaCha20-Poly1305 RFC 8439 Appendix A.5 ===\n");
if (hex_to_bytes(key_hex, key, sizeof(key)) != 0 ||
hex_to_bytes(nonce_hex, nonce, sizeof(nonce)) != 0 ||
hex_to_bytes(aad_hex, aad, aad_len) != 0 ||
hex_to_bytes(plaintext_hex, plaintext, pt_len) != 0 ||
hex_to_bytes(expected_ct_hex, expected_ct, ct_len) != 0 ||
hex_to_bytes(expected_tag_hex, expected_tag, sizeof(expected_tag)) != 0) {
printf("❌ hex parse failed\n\n");
return 0;
}
if (nostr_chacha20poly1305_encrypt(key, nonce, aad, aad_len,
plaintext, pt_len, ciphertext, tag) != 0) {
printf("❌ encrypt failed\n\n");
return 0;
}
if (!bytes_equal(ciphertext, expected_ct, ct_len)) {
size_t i;
size_t mismatch_count = 0;
printf("❌ ciphertext mismatch\n");
for (i = 0; i < ct_len; i++) {
if (ciphertext[i] != expected_ct[i]) {
if (mismatch_count == 0) {
size_t start = (i > 8) ? (i - 8) : 0;
size_t end = (i + 8 < ct_len) ? (i + 8) : (ct_len - 1);
size_t j;
printf("first mismatch at byte %zu: got=%02x expected=%02x\n", i, ciphertext[i], expected_ct[i]);
printf("context (got): ");
for (j = start; j <= end; j++) printf("%02x", ciphertext[j]);
printf("\ncontext (expected): ");
for (j = start; j <= end; j++) printf("%02x", expected_ct[j]);
printf("\n");
}
mismatch_count++;
}
}
printf("total mismatched bytes: %zu\n\n", mismatch_count);
return 0;
}
if (!bytes_equal(tag, expected_tag, sizeof(tag))) {
printf("❌ tag mismatch\n\n");
return 0;
}
if (nostr_chacha20poly1305_decrypt(key, nonce, aad, aad_len,
ciphertext, ct_len, tag, decrypted) != 0) {
printf("❌ decrypt failed\n\n");
return 0;
}
if (!bytes_equal(decrypted, plaintext, pt_len)) {
printf("❌ decrypted plaintext mismatch\n\n");
return 0;
}
printf("✅ passed\n\n");
return 1;
}
static int test_aead_tamper_rejects(void) {
uint8_t key[32] = {0};
uint8_t nonce[12] = {0};
uint8_t aad[8] = {1,2,3,4,5,6,7,8};
uint8_t pt[32] = "hello chacha20-poly1305 world";
uint8_t ct[64] = {0};
uint8_t tag[16] = {0};
uint8_t out[64] = {0};
printf("=== AEAD tamper rejection ===\n");
if (nostr_chacha20poly1305_encrypt(key, nonce, aad, sizeof(aad), pt, strlen((char *)pt), ct, tag) != 0) {
printf("❌ encrypt failed\n\n");
return 0;
}
ct[0] ^= 0x01;
if (nostr_chacha20poly1305_decrypt(key, nonce, aad, sizeof(aad), ct, strlen((char *)pt), tag, out) == 0) {
printf("❌ tampered ciphertext accepted\n\n");
return 0;
}
ct[0] ^= 0x01;
tag[0] ^= 0x80;
if (nostr_chacha20poly1305_decrypt(key, nonce, aad, sizeof(aad), ct, strlen((char *)pt), tag, out) == 0) {
printf("❌ tampered tag accepted\n\n");
return 0;
}
tag[0] ^= 0x80;
aad[0] ^= 0x01;
if (nostr_chacha20poly1305_decrypt(key, nonce, aad, sizeof(aad), ct, strlen((char *)pt), tag, out) == 0) {
printf("❌ tampered AAD accepted\n\n");
return 0;
}
printf("✅ passed\n\n");
return 1;
}
int main(void) {
int pass = 1;
pass &= test_poly1305_rfc8439_2_5_2();
pass &= test_aead_rfc8439_appendix_a_5();
pass &= test_aead_tamper_rejects();
if (pass) {
printf("🎉 All ChaCha20-Poly1305/Poly1305 tests PASSED\n");
return 0;
}
printf("💥 Some tests FAILED\n");
return 1;
}