⬢ DragonFlyBSD Kernel Audit
DF-3559 / camtest.c
← back to finding ↓ download raw
/*
 * camtest.c -- adversarial correctness verification of
 * sys/crypto/camellia/camellia.c (DFly kernel, NTT reference 1.1.0).
 *
 * 1. Table integrity: all 4x256 sp* entries decoded against RFC 3713 SBOX1
 *    (SBOX2[x]=SBOX1[x]<<<1, SBOX3[x]=SBOX1[x]<<<7, SBOX4[x]=SBOX1[x<<<1]),
 *    each verified a permutation.
 * 2. RFC 3713 Appendix A KATs for 128/192/256-bit keys, encrypt + decrypt.
 * 3. camellia-api.c wrapper round-trips.
 * 4. Random cross-check vs OpenSSL (driver script feeds openssl enc -ecb).
 *
 * Build: cc -O2 -o camtest camtest.c camellia-api.c -I<dir with camellia.h>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include "camellia.c"		/* pull in the kernel source + static tables */

/* RFC 3713 SBOX1 (decimal) */
static const unsigned int sbox1[256] = {
112,130,44,236,179,39,192,229,228,133,87,53,234,12,174,65,
35,239,107,147,69,25,165,33,237,14,79,78,29,101,146,189,
134,184,175,143,124,235,31,206,62,48,220,95,94,197,11,26,
166,225,57,202,213,71,93,61,217,1,90,214,81,86,108,77,
139,13,154,102,251,204,176,45,116,18,43,32,240,177,132,153,
223,76,203,194,52,126,118,5,109,183,169,49,209,23,4,215,
20,88,58,97,222,27,17,28,50,15,156,22,83,24,242,34,
254,68,207,178,195,181,122,145,36,8,232,168,96,252,105,80,
170,208,160,125,161,137,98,151,84,91,30,149,224,255,100,210,
16,196,0,72,163,247,117,219,138,3,230,218,9,63,221,148,
135,92,131,2,205,74,144,51,115,103,246,243,157,127,191,226,
82,155,216,38,200,55,198,59,129,150,111,75,19,190,99,46,
233,121,167,140,159,110,188,142,41,245,249,182,47,253,180,89,
120,152,6,106,231,70,113,186,212,37,171,66,136,162,141,250,
114,7,185,85,248,238,172,10,54,73,42,104,60,56,241,164,
64,40,211,123,187,201,67,193,21,227,173,244,119,199,128,158
};

static int failures = 0;

#define CHECK(cond, ...) do { if (!(cond)) { \
	printf("FAIL: "); printf(__VA_ARGS__); printf("\n"); failures++; } } while (0)

static void
hex2bin(const char *h, unsigned char *out, size_t n)
{
	for (size_t i = 0; i < n; i++)
		sscanf(h + 2 * i, "%2hhx", &out[i]);
}

static void
bin2hex(const unsigned char *b, char *out, size_t n)
{
	for (size_t i = 0; i < n; i++)
		sprintf(out + 2 * i, "%02x", b[i]);
	out[2 * n] = 0;
}

static int
check_tables(void)
{
	int seen[256], bad = 0;

	/*
	 * The sp* tables are S-box output pre-spread by the P-function:
	 *   sp1110[i] = 0xSS SS SS 00   (S1 = SBOX1[i])
	 *   sp0222[i] = 0x00 SS SS SS   (S2 = SBOX1[i] <<< 1)
	 *   sp3033[i] = 0xSS 00 SS SS   (S3 = SBOX1[i] <<< 7)
	 *   sp4404[i] = 0xSS SS 00 SS   (S4 = SBOX1[i <<< 1])
	 * Verify the FULL 32-bit pattern of every entry (not just one byte)
	 * against RFC 3713 SBOX1 -- this proves both the S-box values and
	 * the embedded P-function placement.
	 */
	for (int i = 0; i < 256; i++) {
		unsigned int s1 = sbox1[i];
		unsigned int s2 = ((s1 << 1) | (s1 >> 7)) & 0xff;
		unsigned int s3 = ((s1 << 7) | (s1 >> 1)) & 0xff;
		unsigned int xr = ((i << 1) | (i >> 7)) & 0xff;
		unsigned int s4 = sbox1[xr];
		uint32_t e1110 = ((uint32_t)s1 << 24) | ((uint32_t)s1 << 16)
		    | ((uint32_t)s1 << 8);
		uint32_t e0222 = ((uint32_t)s2 << 16) | ((uint32_t)s2 << 8)
		    | s2;
		uint32_t e3033 = ((uint32_t)s3 << 24) | ((uint32_t)s3 << 8)
		    | s3;
		uint32_t e4404 = ((uint32_t)s4 << 24) | ((uint32_t)s4 << 16)
		    | s4;

		if (camellia_sp1110[i] != e1110) {
			printf("FAIL sp1110[%02x]=%08x want=%08x\n", i,
			    camellia_sp1110[i], e1110);
			bad++;
		}
		if (camellia_sp0222[i] != e0222) {
			printf("FAIL sp0222[%02x]=%08x want=%08x\n", i,
			    camellia_sp0222[i], e0222);
			bad++;
		}
		if (camellia_sp3033[i] != e3033) {
			printf("FAIL sp3033[%02x]=%08x want=%08x\n", i,
			    camellia_sp3033[i], e3033);
			bad++;
		}
		if (camellia_sp4404[i] != e4404) {
			printf("FAIL sp4404[%02x]=%08x want=%08x\n", i,
			    camellia_sp4404[i], e4404);
			bad++;
		}
	}
	/* the transcribed RFC SBOX1 must itself be a permutation */
	memset(seen, 0, sizeof(seen));
	for (int i = 0; i < 256; i++) {
		CHECK(seen[sbox1[i]] == 0, "RFC SBOX1 transcription not a "
		    "permutation (%02x dup at %02x)", sbox1[i], i);
		seen[sbox1[i]] = 1;
	}
	printf("%s: sp-table checks bad=%d\n", bad ? "RESULT-tables-FAIL" : "RESULT-tables-OK", bad);
	return bad;
}

struct kat { int bits; const char *key, *pt, *ct; };
static const struct kat kats[] = {
	{ 128, "0123456789abcdeffedcba9876543210",
	       "0123456789abcdeffedcba9876543210",
	       "67673138549669730857065648eabe43" },
	{ 192, "0123456789abcdeffedcba98765432100011223344556677",
	       "0123456789abcdeffedcba9876543210",
	       "b4993401b3e996f84ee5cee7d79b09b9" },
	{ 256, "0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff",
	       "0123456789abcdeffedcba9876543210",
	       "9acc237dff16d76c20ef7c919e3a7509" },
};

static int
check_kats(void)
{
	unsigned char key[32], pt[16], ct[16], out[16], back[16];
	char hex[33];
	uint32_t subkey[CAMELLIA_SUBKEYWORD];
	int bad = 0;

	for (size_t k = 0; k < sizeof(kats) / sizeof(kats[0]); k++) {
		const struct kat *t = &kats[k];
		hex2bin(t->key, key, strlen(t->key) / 2);
		hex2bin(t->pt, pt, 16);
		hex2bin(t->ct, ct, 16);

		memset(subkey, 0xAA, sizeof(subkey)); /* poison */
		Camellia_Ekeygen(t->bits, key, subkey);
		Camellia_EncryptBlock(t->bits, pt, subkey, out);
		bin2hex(out, hex, 16);
		printf("KAT%d enc: %s (want %s) %s\n", t->bits, hex, t->ct,
		    strcmp(hex, t->ct) == 0 ? "OK" : "MISMATCH");
		if (strcmp(hex, t->ct) != 0) bad++;

		Camellia_DecryptBlock(t->bits, ct, subkey, back);
		bin2hex(back, hex, 16);
		printf("KAT%d dec: %s (want %s) %s\n", t->bits, hex, t->pt,
		    strcmp(hex, t->pt) == 0 ? "OK" : "MISMATCH");
		if (strcmp(hex, t->pt) != 0) bad++;

		/* write-coverage check: poison must be overwritten everywhere
		 * the setup writes; SUBL(1)/SUBR(1) (subkey[2],[3]) are the
		 * unused kw2 slots -- never written, never read by the block
		 * functions, so exactly those two must survive as 0xAAAA... */
		size_t words = (t->bits == 128) ? 50 : 66;
		int poison = 0;
		for (size_t i = 0; i < words; i++)
			if (subkey[i] == 0xAAAAAAAA) poison++;
		CHECK(poison == 2 && subkey[2] == 0xAAAAAAAA &&
		    subkey[3] == 0xAAAAAAAA,
		    "KAT%d: unexpected poison survivors=%d", t->bits, poison);
	}
	printf("%s: RFC3713 KATs bad=%d\n", bad ? "RESULT-kat-FAIL" : "RESULT-kat-OK", bad);
	return bad;
}

/* cross-check driver: reads "bits keyhex pthex" lines on stdin, prints ct */
int
main(int argc, char **argv)
{
	int bad = 0;
	bad += check_tables();
	bad += check_kats();

	/* api wrapper round-trips + agreement with raw block API */
	{
		unsigned char key[32], pt[16], ct[16], rt[16];
		camellia_ctx ctx;
		int bits[3] = {128, 192, 256};
		for (int b = 0; b < 3; b++) {
			for (int i = 0; i < 32; i++) key[i] = i * 7 + b;
			for (int i = 0; i < 16; i++) pt[i] = i * 11 + b;
			camellia_set_key(&ctx, key, bits[b]);
			camellia_encrypt(&ctx, pt, ct);
			camellia_decrypt(&ctx, ct, rt);
			CHECK(memcmp(pt, rt, 16) == 0, "api roundtrip %d", bits[b]);
			/* agreement with raw API */
			{
				uint32_t sk[CAMELLIA_SUBKEYWORD];
				unsigned char ct2[16];
				Camellia_Ekeygen(bits[b], key, sk);
				Camellia_EncryptBlock(bits[b], pt, sk, ct2);
				CHECK(memcmp(ct, ct2, 16) == 0, "api/raw agree %d", bits[b]);
			}
		}
		printf("RESULT-api-OK (roundtrips + raw agreement)\n");
	}

	/* stdin cross-check mode for the openssl driver */
	if (argc > 1 && strcmp(argv[1], "-x") == 0) {
		char line[256], keyhex[128], pthex[64], cthex[64];
		while (fgets(line, sizeof(line), stdin)) {
			int bits;
			unsigned char key[32], pt[16], ct[16];
			uint32_t sk[CAMELLIA_SUBKEYWORD];
			if (sscanf(line, "%d %127s %63s", &bits, keyhex, pthex) != 3)
				continue;
			hex2bin(keyhex, key, strlen(keyhex) / 2);
			hex2bin(pthex, pt, 16);
			Camellia_Ekeygen(bits, key, sk);
			Camellia_EncryptBlock(bits, pt, sk, ct);
			bin2hex(ct, cthex, 16);
			printf("%s\n", cthex);
			fflush(stdout);
		}
		return 0;
	}

	printf("%s: %d failure(s)\n", bad ? "OVERALL-FAIL" : "OVERALL-PASS", bad);
	return bad ? 1 : 0;
}