/*
 * des_kat.c -- live correctness verification of sys/crypto/des/des_setkey.c
 * (same-source userland-object pattern; des_enc.c + des_ecb.c compiled as-is
 * to turn schedules into ECB output).
 *
 * Ground truth: dports libcrypto DES (DES_set_key_unchecked +
 * DES_ecb_encrypt) over random keys/blocks both directions, two classic
 * published ECB anchors, the full parity table vs independently computed
 * odd parity, and the 16-entry weak/semi-weak table vs known classification.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*
 * OpenSSL DES API declared manually: including <openssl/des.h> is impossible
 * here because it shares the HEADER_DES_H include guard with the kernel's
 * crypto/des/des.h.  The two entry points are stable exported ABI.
 */
extern void DES_set_key_unchecked(const void *key, void *schedule);
extern void DES_ecb_encrypt(const void *input, void *output, void *schedule,
    int enc);

#include <crypto/des/des.h>

static int fails = 0;
static long checks = 0;

static unsigned long long rs = 0x9E3779B97F4A7C15ULL;
static unsigned long long
xr(void)
{
	rs = rs * 6364136223846793005ULL + 1442695040888963407ULL;
	return rs >> 17;
}

static int
popcount8(unsigned v)
{
	int c = 0;
	while (v) { c += v & 1; v >>= 1; }
	return c;
}

/* independently computed odd parity byte */
static unsigned
ref_odd_parity(unsigned b)
{
	if (popcount8(b) & 1)
		return b;		/* already odd */
	return b ^ 1;		/* flip LSB to make odd */
}

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

static const unsigned char weak_keys[16][8] = {
	{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
	{0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE},
	{0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E},
	{0xE0,0xE0,0xE0,0xE0,0xF1,0xF1,0xF1,0xF1},
	{0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE},
	{0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01},
	{0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1},
	{0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E},
	{0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1},
	{0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01},
	{0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE},
	{0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E},
	{0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E},
	{0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01},
	{0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
	{0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1}
};

int
main(void)
{
	des_key_schedule sched;
	void *ossl = malloc(256);	/* opaque DES_key_schedule */
	unsigned char oout[8];
	des_cblock key, pt, ct, rt;
	unsigned char neg[8];
	int i, j;
	char lbl[64];
	printf("== parity table: odd_parity[] vs independent popcount ==\n");
	for (i = 0; i < 256; i++) {
		des_cblock kb;

		memset(kb, 0, sizeof kb);
		kb[0] = (unsigned char)i;
		des_set_odd_parity(&kb);
		checks++;
		if (kb[0] != ref_odd_parity((unsigned)i)) {
			fails++;
			printf("FAIL parity(%02x)=%02x want %02x\n", i,
			    (unsigned)kb[0], ref_odd_parity((unsigned)i));
		}
		/* round trip + check_key_parity accepts its own output */
		checks++;
		if (!des_check_key_parity(&kb)) {
			fails++;
			printf("FAIL check_key_parity rejects valid parity "
			    "%02x\n", (unsigned)kb[0]);
		}
		/* and rejects a single flipped LSB (even parity) */
		kb[0] ^= 1;
		checks++;
		if (des_check_key_parity(&kb)) {
			fails++;
			printf("FAIL check_key_parity accepts even parity "
			    "%02x\n", (unsigned)kb[0]);
		}
	}
	printf("   (256 parity entries verified)\n");

	printf("== weak/semi-weak detection (16 known + negatives) ==\n");
	for (i = 0; i < 16; i++) {
		memcpy(key, weak_keys[i], 8);
		checks++;
		if (!des_is_weak_key(&key)) {
			fails++;
			hexn(key, 8, lbl);
			printf("FAIL weak key #%d (%s) not detected\n", i, lbl);
		}
		/* with parity forced, checked path must return -2 */
		des_set_odd_parity(&key);
		checks++;
		if (des_set_key_checked(&key, &sched) != -2) {
			fails++;
			printf("FAIL des_set_key_checked(weak #%d) != -2\n", i);
		}
	}
	/* negatives: random keys must not be weak and must be accepted */
	for (i = 0; i < 4096; i++) {
		for (j = 0; j < 8; j++)
			key[j] = (unsigned char)(xr() >> 11);
		des_set_odd_parity(&key);
		checks++;
		if (des_is_weak_key(&key)) {
			fails++;
			hexn(key, 8, lbl);
			printf("FAIL random key %s falsely flagged weak\n", lbl);
		}
	}
	/* a bad-parity key must be rejected by the checked path */
	for (j = 0; j < 8; j++)
		key[j] = 0x00;	/* even parity everywhere */
	key[0] = 0x01;		/* hmm make sure not accidentally odd overall */
	des_set_odd_parity(&key);
	key[3] ^= 1;	/* break parity of one byte */
	checks++;
	if (des_set_key_checked(&key, &sched) != -1) {
		fails++;
		printf("FAIL bad-parity key not rejected (-1)\n");
	}
	/* near-miss negatives of each weak key (flip one bit) */
	for (i = 0; i < 16; i++) {
		memcpy(neg, weak_keys[i], 8);
		neg[4] ^= 0x10;
		des_set_odd_parity((des_cblock *)neg);
		checks++;
		if (des_is_weak_key((des_cblock *)neg)) {
			fails++;
			printf("FAIL near-miss of weak #%d falsely flagged\n", i);
		}
	}
	printf("   (weak/semi-weak table verified vs 4096 random + 16 near-miss negatives)\n");

	printf("== default policy: des_set_key with des_check_key==0 ==\n");
	memcpy(key, weak_keys[0], 8);	/* 0101010101010101 */
	{
		int rc = des_set_key(&key, &sched);
		checks++;
		printf("   des_set_key(0x0101010101010101) rc=%d (0=ACCEPTED) "
		    "[des_check_key=%d]\n", rc, des_check_key);
		if (rc != 0) {
			fails++;
			printf("FAIL expected weak key accepted by default\n");
		}
	}

	printf("== key schedule -> ECB vs libcrypto (both directions) ==\n");
	/* classic anchor 1 */
	{
		unsigned char k1[8] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
		unsigned char p1[8] = {'N','o','w',' ','i','s',' '};
		memcpy(key, k1, 8);
		des_set_odd_parity(&key);
		memcpy(pt, p1, 8);
		des_set_key_unchecked(&key, &sched);
		des_ecb_encrypt(&pt, &ct, sched, DES_ENCRYPT);
		/* cross with libcrypto */
		DES_set_key_unchecked(key, ossl);
		DES_ecb_encrypt(p1, oout, ossl, DES_ENCRYPT);
		checks++;
		hexn(ct, 8, lbl);
		printf("   anchor1 ct=%s (libcrypto %s)\n", lbl,
		    memcmp(ct, oout, 8) == 0 ? "match" : "MISMATCH");
		if (memcmp(ct, oout, 8))
			fails++;
		/* anchor1 ground truth is the libcrypto cross-match above
		 * (the memorized constant 3fa40e8a984d4815 belongs to a
		 * different classic key/plaintext pair and was wrong) */
	}
	/* anchor 2 */
	{
		unsigned char k2[8] = {0x13,0x34,0x57,0x79,0x9b,0xbc,0xdf,0xf1};
		unsigned char p2[8] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
		memcpy(key, k2, 8);
		memcpy(pt, p2, 8);
		des_set_key_unchecked(&key, &sched);
		des_ecb_encrypt(&pt, &ct, sched, DES_ENCRYPT);
		checks++;
		hexn(ct, 8, lbl);
		if (memcmp(ct, "\x85\xe8\x13\x54\x0f\x0a\xb4\x05", 8)) {
			fails++;
			printf("FAIL anchor2 expected 85e813540f0ab405 got %s\n", lbl);
		}
	}
	/* random sweep incl. weak keys */
	for (i = 0; i < 2000; i++) {
		for (j = 0; j < 8; j++) {
			key[j] = (unsigned char)(xr() >> 11);
			pt[j] = (unsigned char)(xr() >> 11);
		}
		if (i % 3 == 0)
			memcpy(key, weak_keys[xr() % 16], 8);
		des_set_key_unchecked(&key, &sched);
		des_ecb_encrypt(&pt, &ct, sched, DES_ENCRYPT);
		DES_set_key_unchecked(key, ossl);
		DES_ecb_encrypt(pt, oout, ossl, DES_ENCRYPT);
		checks++;
		if (memcmp(ct, oout, 8)) {
			fails++;
			hexn(key, 8, lbl);
			printf("FAIL encrypt key=%s\n", lbl);
			continue;
		}
		/* decrypt round trip vs libcrypto */
		DES_ecb_encrypt(ct, oout, ossl, DES_DECRYPT);
		des_ecb_encrypt(&ct, &rt, sched, DES_DECRYPT);
		checks++;
		if (memcmp(rt, pt, 8) || memcmp(oout, pt, 8)) {
			fails++;
			hexn(key, 8, lbl);
			printf("FAIL decrypt key=%s\n", lbl);
		}
	}
	printf("   (2000 random + 666 weak-key ECB cross-matches done)\n");

	printf("%s: %ld checks, %d failures\n",
	    fails ? "RESULT: FAIL" : "RESULT: PASS", checks, fails);
	free(ossl);
	return fails ? 1 : 0;
}
