v4.0.0: codec 2.60.4 security release, --pq-box sealed-box mode, F-16 fix
Some checks failed
CI / build-and-test (clang) (push) Has been cancelled
CI / build-and-test (gcc) (push) Has been cancelled
CI / strict-warnings (clang, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror) (push) Has been cancelled
CI / strict-warnings (gcc, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wformat-security -Wlogical-op -Wjump-misses-init -Wdouble-promotion -O2 -std=c11 -Werror) (push) Has been cancelled
CI / sanitizers (push) Has been cancelled
CI / pie-hardening (push) Has been cancelled
CI / cross-aarch64 (push) Has been cancelled
CI / dist-reproducibility (push) Has been cancelled
CI / packaging-syntax (push) Has been cancelled
CI / release (push) Has been cancelled
Some checks failed
CI / build-and-test (clang) (push) Has been cancelled
CI / build-and-test (gcc) (push) Has been cancelled
CI / strict-warnings (clang, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror) (push) Has been cancelled
CI / strict-warnings (gcc, -Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wformat-security -Wlogical-op -Wjump-misses-init -Wdouble-promotion -O2 -std=c11 -Werror) (push) Has been cancelled
CI / sanitizers (push) Has been cancelled
CI / pie-hardening (push) Has been cancelled
CI / cross-aarch64 (push) Has been cancelled
CI / dist-reproducibility (push) Has been cancelled
CI / packaging-syntax (push) Has been cancelled
CI / release (push) Has been cancelled
Major release. Highlights: - Codec: vendored VaptVupt codec moves to canonical 2.60.4 security release. Fixes a high-severity OOB heap write in the AVX2 decode fast path (reachable on a valid stream sized to exactly content_size, both tail variants). Brings CBMC-formally-verified BCJ filters with automatic ELF/PE/Mach-O detection. Compressed output stays byte-identical (ratio gate Δ 0.00%); wire format unchanged at v1.6. - New --pq-box sealed-box recipient mode (vendored libpqvaptvupt 0.6.0): ML-KEM-768 + X25519 combined via HKDF-SHA256 with domain separation, AES-256-CTR + HMAC-SHA256 EtM. Legacy --pq and --pq-sdk stay readable. - F-16: discloses and fixes a pre-existing data-loss defect in the <= 3.8.0 in-tree BCJ encoder. Full back-compat matrix decodes byte-exact under 4.0.0; every readable pre-4.0 archive remains readable. Repository hygiene: - Sync full 4.0.0 source tree (codec, crypto, SDK, GUI, packaging, tests). - Remove internal scratch files (PROMPT.md, FORMAL_AUDIT_PROMPT.md) and superseded version-specific docs (INTEGRATION_PROTOCOL_2.60.4.md, docs/FINDINGS-2.x.md) and a stray test binary. - Refresh README download/install section to real 4.0.0 release assets; bump version badge to 4.0.0. - Add .gitignore for build outputs (keeps vendored prebuilt libraries).
This commit is contained in:
parent
7619c4c577
commit
544a2cd647
98 changed files with 15615 additions and 1397 deletions
144
tests/test_hmac_incremental.c
Normal file
144
tests/test_hmac_incremental.c
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
* Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
*
|
||||
* Incremental HMAC-SHA256 equivalence test (v3.3.0).
|
||||
*
|
||||
* The per-block Encrypt-then-MAC hot path was changed from
|
||||
* one-shot HMAC over a malloc'd (aad || nonce || ciphertext || seq)
|
||||
* concat buffer
|
||||
* to
|
||||
* incremental HMAC streamed segment-by-segment (no concat, no copy).
|
||||
*
|
||||
* RFC 2104 + SHA-256's Merkle-Damgard update() guarantee these produce
|
||||
* identical tags, but that guarantee is load-bearing for wire-format
|
||||
* compatibility (old archives must still authenticate). This test pins
|
||||
* it down:
|
||||
* 1. zupt_hmac_sha256 one-shot == manual init/update/final, single seg.
|
||||
* 2. Streaming the message in arbitrary chunk splits == one-shot over
|
||||
* the whole message, across many lengths and split points.
|
||||
* 3. The exact per-block segment pattern used by the codec
|
||||
* (aad_extra || nonce || ciphertext || aad_seq) streamed in 4
|
||||
* updates == one-shot over the concatenation. This is the precise
|
||||
* invariant the encrypt/decrypt paths rely on.
|
||||
* 4. RFC 4231 Test Case 2 known-answer (sanity that the base HMAC is
|
||||
* still correct after the refactor).
|
||||
*/
|
||||
#include "zupt.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int pass = 0, fail = 0;
|
||||
static void ok(const char *m) { printf(" \xE2\x9C\x93 %s\n", m); pass++; }
|
||||
static void bad(const char *m) { printf(" \xE2\x9C\x97 %s\n", m); fail++; }
|
||||
|
||||
static int eq32(const uint8_t a[32], const uint8_t b[32]) {
|
||||
return memcmp(a, b, 32) == 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("Incremental HMAC-SHA256 equivalence\n");
|
||||
|
||||
uint8_t key[32];
|
||||
for (int i = 0; i < 32; i++) key[i] = (uint8_t)(i * 7 + 1);
|
||||
|
||||
/* 1. one-shot == manual init/update/final (single segment) */
|
||||
{
|
||||
const uint8_t msg[] = "the quick brown fox";
|
||||
uint8_t a[32], b[32];
|
||||
zupt_hmac_sha256(key, 32, msg, sizeof(msg) - 1, a);
|
||||
zupt_hmac_ctx c;
|
||||
zupt_hmac_sha256_init(&c, key, 32);
|
||||
zupt_hmac_sha256_update(&c, msg, sizeof(msg) - 1);
|
||||
zupt_hmac_sha256_final(&c, b);
|
||||
if (eq32(a, b)) ok("one-shot == init/update/final (single segment)");
|
||||
else bad("one-shot != incremental (single segment)");
|
||||
}
|
||||
|
||||
/* 2. arbitrary chunk splits == one-shot, many lengths */
|
||||
{
|
||||
size_t lens[] = {0, 1, 31, 32, 33, 63, 64, 65, 127, 128, 1000, 4096, 100000};
|
||||
int all_ok = 1;
|
||||
uint8_t *buf = (uint8_t *)malloc(100000);
|
||||
for (size_t i = 0; i < 100000; i++) buf[i] = (uint8_t)(i * 131 + 17);
|
||||
for (size_t li = 0; li < sizeof(lens)/sizeof(lens[0]); li++) {
|
||||
size_t n = lens[li];
|
||||
uint8_t ref[32];
|
||||
zupt_hmac_sha256(key, 32, buf, n, ref);
|
||||
/* split into 1, 2, and 3 pieces at varied points */
|
||||
for (int parts = 1; parts <= 3; parts++) {
|
||||
uint8_t got[32];
|
||||
zupt_hmac_ctx c;
|
||||
zupt_hmac_sha256_init(&c, key, 32);
|
||||
size_t off = 0;
|
||||
for (int p = 0; p < parts; p++) {
|
||||
size_t remain = n - off;
|
||||
size_t chunk = (p == parts - 1) ? remain : remain / (size_t)(parts - p);
|
||||
zupt_hmac_sha256_update(&c, buf + off, chunk);
|
||||
off += chunk;
|
||||
}
|
||||
zupt_hmac_sha256_final(&c, got);
|
||||
if (!eq32(ref, got)) { all_ok = 0; }
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
if (all_ok) ok("streamed splits (1/2/3 parts) == one-shot, lengths 0..100000");
|
||||
else bad("streamed split != one-shot for some length/split");
|
||||
}
|
||||
|
||||
/* 3. exact per-block segment pattern: aad || nonce || ct || seq */
|
||||
{
|
||||
uint8_t aad[29], nonce[16], seq[8];
|
||||
uint8_t ct[5000];
|
||||
for (int i = 0; i < 29; i++) aad[i] = (uint8_t)(i + 100);
|
||||
for (int i = 0; i < 16; i++) nonce[i] = (uint8_t)(i * 3);
|
||||
for (int i = 0; i < 8; i++) seq[i] = (uint8_t)(i + 200);
|
||||
for (int i = 0; i < 5000; i++) ct[i] = (uint8_t)(i * 53 + 9);
|
||||
|
||||
/* one-shot over the concatenation (the OLD method) */
|
||||
size_t total = 29 + 16 + 5000 + 8;
|
||||
uint8_t *concat = (uint8_t *)malloc(total);
|
||||
size_t o = 0;
|
||||
memcpy(concat + o, aad, 29); o += 29;
|
||||
memcpy(concat + o, nonce, 16); o += 16;
|
||||
memcpy(concat + o, ct, 5000); o += 5000;
|
||||
memcpy(concat + o, seq, 8); o += 8;
|
||||
uint8_t ref[32];
|
||||
zupt_hmac_sha256(key, 32, concat, total, ref);
|
||||
free(concat);
|
||||
|
||||
/* streamed (the NEW method) */
|
||||
uint8_t got[32];
|
||||
zupt_hmac_ctx c;
|
||||
zupt_hmac_sha256_init(&c, key, 32);
|
||||
zupt_hmac_sha256_update(&c, aad, 29);
|
||||
zupt_hmac_sha256_update(&c, nonce, 16);
|
||||
zupt_hmac_sha256_update(&c, ct, 5000);
|
||||
zupt_hmac_sha256_update(&c, seq, 8);
|
||||
zupt_hmac_sha256_final(&c, got);
|
||||
|
||||
if (eq32(ref, got)) ok("per-block pattern (aad||nonce||ct||seq) streamed == concat one-shot");
|
||||
else bad("per-block streamed pattern != concat one-shot");
|
||||
}
|
||||
|
||||
/* 4. RFC 4231 Test Case 2 known-answer */
|
||||
{
|
||||
/* Key = "Jefe", Data = "what do ya want for nothing?" */
|
||||
const uint8_t k[] = "Jefe";
|
||||
const uint8_t d[] = "what do ya want for nothing?";
|
||||
uint8_t mac[32];
|
||||
zupt_hmac_sha256(k, 4, d, 28, mac);
|
||||
char hx[65];
|
||||
for (int i = 0; i < 32; i++) sprintf(hx + i*2, "%02x", mac[i]);
|
||||
if (strcmp(hx, "5bdcc146bf60754e6a042426089575c7"
|
||||
"5a003f089d2739839dec58b964ec3843") == 0)
|
||||
ok("RFC 4231 TC2 known-answer correct");
|
||||
else { bad("RFC 4231 TC2 WRONG"); printf(" got %s\n", hx); }
|
||||
}
|
||||
|
||||
printf("\n ───────────────────────────────────────\n");
|
||||
printf(" Incremental HMAC: %d passed, %d failed\n", pass, fail);
|
||||
printf(" ───────────────────────────────────────\n");
|
||||
return fail ? 1 : 0;
|
||||
}
|
||||
Loading…
Reference in a new issue