Initial skeleton: README, CVE inventory, roadmap, ARCH, ethics + copy_fail_family module absorbed from DIRTYFAIL

This commit is contained in:
2026-05-16 19:26:24 -04:00
commit cf30b249de
45 changed files with 10336 additions and 0 deletions
@@ -0,0 +1,101 @@
/*
* tests/test_aes_ecb.c
*
* Verifies that the kernel's AF_ALG `ecb(aes)` implementation produces
* the expected outputs for known AES-128-ECB test vectors. This is the
* primitive that copyfail_gcm.c uses to compute GCM keystream byte 0
* via the J0+1 counter block trick.
*
* If this test passes, the GCM exploit's brute-force loop is sound.
* If it fails, the kernel's AES implementation differs from spec — no
* exploit will produce the right STORE values.
*
* Linux-only. Uses the same AF_ALG primitives as copyfail_gcm.c.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
static int failures = 0;
#define ASSERT(cond, msg, ...) do { \
if (!(cond)) { fprintf(stderr, "FAIL: " msg "\n", ##__VA_ARGS__); failures++; } \
else { fprintf(stderr, " ok: " msg "\n", ##__VA_ARGS__); } \
} while (0)
static int alg_open_ecb_aes(const unsigned char key[16])
{
int s = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (s < 0) return -1;
struct sockaddr_alg sa = { .salg_family = AF_ALG };
strcpy((char *)sa.salg_type, "skcipher");
strcpy((char *)sa.salg_name, "ecb(aes)");
if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) { close(s); return -1; }
if (setsockopt(s, SOL_ALG, ALG_SET_KEY, key, 16) < 0) { close(s); return -1; }
return s;
}
static int aes_ecb_encrypt(int s, const unsigned char in[16], unsigned char out[16])
{
int op = accept(s, NULL, NULL);
if (op < 0) return -1;
char cbuf[CMSG_SPACE(sizeof(int))] = {0};
struct msghdr msg = { .msg_control = cbuf, .msg_controllen = sizeof(cbuf) };
struct cmsghdr *c = CMSG_FIRSTHDR(&msg);
c->cmsg_level = SOL_ALG; c->cmsg_type = ALG_SET_OP; c->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(c) = ALG_OP_ENCRYPT;
struct iovec iov = { .iov_base = (void *)in, .iov_len = 16 };
msg.msg_iov = &iov; msg.msg_iovlen = 1;
if (sendmsg(op, &msg, 0) != 16) { close(op); return -1; }
int n = read(op, out, 16);
close(op);
return n == 16 ? 0 : -1;
}
int main(void)
{
/* NIST test vector: AES-128 ECB
* key = 000102030405060708090a0b0c0d0e0f
* pt = 000102030405060708090a0b0c0d0e0f
* ct = 0a940bb5416ef045f1c39458c653ea5a
*/
unsigned char key[16], in[16], out[16];
for (int i = 0; i < 16; i++) { key[i] = i; in[i] = i; }
static const unsigned char expected[16] = {
0x0a,0x94,0x0b,0xb5,0x41,0x6e,0xf0,0x45,
0xf1,0xc3,0x94,0x58,0xc6,0x53,0xea,0x5a
};
int s = alg_open_ecb_aes(key);
ASSERT(s >= 0, "AF_ALG skcipher ecb(aes) bindable + keyable");
if (s < 0) return 1;
ASSERT(aes_ecb_encrypt(s, in, out) == 0, "single-block ECB encrypt completes");
ASSERT(memcmp(out, expected, 16) == 0,
"ECB(K=0..15, P=0..15) = 0a940bb5416ef045f1c39458c653ea5a");
if (memcmp(out, expected, 16) != 0) {
fprintf(stderr, " got: ");
for (int i = 0; i < 16; i++) fprintf(stderr, "%02x", out[i]);
fprintf(stderr, "\n");
}
/* GCM J0+1 counter block sanity: nonce(12) || 0x00000002. byte 0 of
* the encrypted block is the keystream byte that XORs onto plaintext
* byte 0 in GCM. We don't verify against a specific GCM vector here
* (no canonical short test for this), just that the operation runs. */
unsigned char counter[16];
memset(counter, 0xab, 12);
counter[12] = 0; counter[13] = 0; counter[14] = 0; counter[15] = 2;
ASSERT(aes_ecb_encrypt(s, counter, out) == 0,
"GCM J0+1 counter block encrypt (keystream byte computation)");
close(s);
fprintf(stderr, "\n%d failure(s)\n", failures);
return failures > 0 ? 1 : 0;
}
@@ -0,0 +1,84 @@
/*
* tests/test_fcrypt.c
*
* Selftest for the rxkad fcrypt cipher implementation in src/fcrypt.c.
* Built standalone via `make test`. No DIRTYFAIL runtime needed.
*
* Verifies:
* - All-zero key vector (catches gross structural bugs)
* - Non-zero key vector from kernel testmgr.h (catches subtle bugs
* in 7-bit packing or 11-bit ROR key schedule)
* - Brute-force harness convergence (sanity-checks predicate gating)
*/
#include "../src/fcrypt.h"
#include "../src/common.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
static int failures = 0;
#define ASSERT(cond, msg, ...) do { \
if (!(cond)) { \
fprintf(stderr, "FAIL: " msg "\n", ##__VA_ARGS__); \
failures++; \
} else { \
fprintf(stderr, " ok: " msg "\n", ##__VA_ARGS__); \
} \
} while (0)
static bool predicate_match_first_byte(const uint8_t p[8])
{
return p[0] == 0xAB;
}
int main(void)
{
fcrypt_init();
/* Selftest covers both vectors. */
ASSERT(fcrypt_selftest(),
"fcrypt_selftest passes (covers k=0 and k=1144...66 vectors)");
/* Spot-check vector 1 directly */
fcrypt_ctx ctx;
uint8_t out[8];
static const uint8_t k1[8] = {0,0,0,0,0,0,0,0};
static const uint8_t c1[8] = {0x0E,0x09,0x00,0xC7,0x3E,0xF7,0xED,0x41};
fcrypt_setkey(&ctx, k1);
fcrypt_decrypt(&ctx, out, c1);
ASSERT(memcmp(out, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) == 0,
"vector 1: decrypt(k=0, ct=0E0900C73EF7ED41) = 0000000000000000");
/* Spot-check vector 2 directly */
static const uint8_t k2[8] = {0x11,0x44,0x77,0xAA,0xDD,0x00,0x33,0x66};
static const uint8_t c2[8] = {0xD8,0xED,0x78,0x74,0x77,0xEC,0x06,0x80};
static const uint8_t p2[8] = {0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0};
fcrypt_setkey(&ctx, k2);
fcrypt_decrypt(&ctx, out, c2);
ASSERT(memcmp(out, p2, 8) == 0,
"vector 2: decrypt(k=11447 7AAD D003 366, ct=D8ED787477EC0680) = 123456789ABCDEF0");
/* Brute-force smoke test: search for K such that decrypt(C=0..7) starts with 0xAB.
* Predicate hit rate = 1/256, so ~256 iters expected. Hard cap at 1<<20. */
uint8_t key_out[8], pt_out[8];
static const uint8_t test_ct[8] = {0,1,2,3,4,5,6,7};
bool found = fcrypt_brute_force(test_ct, predicate_match_first_byte,
1 << 20, (uint64_t)time(NULL),
"smoke", key_out, pt_out);
ASSERT(found,
"brute force converges on first-byte=0xAB predicate within 1M iters");
if (found) {
/* Verify the discovered key actually produces the claimed plaintext */
fcrypt_setkey(&ctx, key_out);
fcrypt_decrypt(&ctx, out, test_ct);
ASSERT(memcmp(out, pt_out, 8) == 0 && out[0] == 0xAB,
"discovered key produces claimed plaintext (roundtrip OK)");
}
fprintf(stderr, "\n%d failure(s)\n", failures);
return failures > 0 ? 1 : 0;
}