/*
 * DF-3569 harness -- camellia_set_key() key-length validation gap
 *                   (sys/crypto/camellia/camellia-api.c:37-42)
 *
 * Target defect (pass 2, GLM 5.3):
 *   camellia_set_key() forwards `bits` to Camellia_Ekeygen() with NO
 *   validation of its own and then unconditionally stores ctx->bits = bits.
 *   The only guard on the key length is the KASSERT in Camellia_Ekeygen
 *   (sys/crypto/camellia/camellia.c:1253), which is compiled out on
 *   production kernels (no INVARIANTS).  In that configuration:
 *     - Camellia_Ekeygen()'s switch takes `default: break;`  -> the 68-word
 *       schedule is NEVER generated (stays whatever the caller left there),
 *     - ctx->bits still records the invalid length,
 *     - the subsequent Camellia_EncryptBlock/Camellia_DecryptBlock switch
 *       also takes `default: break;` -> the 16-byte block is copied from
 *       src to dst WITHOUT any round function: "encryption" degenerates to
 *       the identity function.
 *
 * This harness compiles the REAL kernel sources (camellia-api.c + camellia.c)
 * into a userland binary.  camellia.c:40-43 maps KASSERT->assert() in
 * userland, so:
 *     -DNDEBUG  == production-kernel semantics (KASSERT compiled out)
 *     (none)    == INVARIANTS/debug-kernel semantics (KASSERT traps)
 *
 * Modes:
 *   kat    RFC 3713 Appendix A known-answer tests for 128/192/256-bit keys,
 *          decrypt roundtrip, and a bounds canary proving the schedule
 *          writes stay inside ctx->subkey[68] (ctx-layout interplay).
 *   guard  bits in {64, 0, -1, 129}: demonstrate (a) no trap, (b) schedule
 *          untouched, (c) ctx->bits stores the invalid value, (d) encrypt is
 *          the identity function.  Exit 42 = BUG-CONFIRMED (unfixed),
 *          exit 0  = BUG-NOT-PRESENT (fixed build rejects + refuses).
 *
 * Build/run: see build.sh / run.sh in this evidence pack.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <crypto/camellia/camellia.h>

static int
hexparse(const char *s, unsigned char *out, int n)
{
	int i;

	for (i = 0; i < n; i++) {
		unsigned v;

		if (sscanf(s + 2 * i, "%2x", &v) != 1)
			return (-1);
		out[i] = (unsigned char)v;
	}
	return (0);
}

static void
dump(const char *tag, const unsigned char *p, int n)
{
	int i;

	printf("%-11s", tag);
	for (i = 0; i < n; i++)
		printf("%02x", p[i]);
	printf("\n");
}

/* ctx-layout interplay: schedule array immediately followed by canary words */
typedef struct {
	int	bits;
	uint32_t subkey[CAMELLIA_SUBKEYWORD];
	uint32_t canary[4];
} wrapped_ctx;

#define CANARY	0xC0FFEE11u

static int
kat(void)
{
	static const char *k128 =
	    "0123456789abcdeffedcba9876543210";
	static const char *k192 =
	    "0123456789abcdeffedcba98765432100011223344556677";
	static const char *k256 =
	    "0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff";
	static const char *ptx =
	    "0123456789abcdeffedcba9876543210";
	static const char *c128 =
	    "67673138549669730857065648eabe43";
	static const char *c192 =
	    "b4993401b3e996f84ee5cee7d79b09b9";
	static const char *c256 =
	    "9acc237dff16d76c20ef7c919e3a7509";
	struct {
		int	bits;
		const char *key;
		int	klen;
		const char *ct;
	} tv[3] = {
		{ 128, k128, 16, c128 },
		{ 192, k192, 24, c192 },
		{ 256, k256, 32, c256 },
	};
	int fail = 0;
	size_t i;

	for (i = 0; i < sizeof(tv) / sizeof(tv[0]); i++) {
		unsigned char key[32], pt[16], ct[16], exp[16], back[16];
		camellia_ctx ctx;
		wrapped_ctx w;
		int j, canary_ok = 1, enc_ok, dec_ok;

		memset(&w, 0, sizeof(w));
		for (j = 0; j < 4; j++)
			w.canary[j] = CANARY;

		hexparse(tv[i].key, key, tv[i].klen);
		hexparse(ptx, pt, 16);
		hexparse(tv[i].ct, exp, 16);

		/* primitive: does the schedule stay inside subkey[68]? */
		Camellia_Ekeygen(tv[i].bits, key, w.subkey);
		for (j = 0; j < 4; j++)
			if (w.canary[j] != CANARY)
				canary_ok = 0;

		/* wrapper API: the code path under audit */
		camellia_set_key(&ctx, key, tv[i].bits);
		camellia_encrypt(&ctx, pt, ct);
		camellia_decrypt(&ctx, ct, back);

		enc_ok = (memcmp(ct, exp, 16) == 0);
		dec_ok = (memcmp(back, pt, 16) == 0);
		printf("KAT-%3d: enc %s  dec %s  canary %s  ctx->bits %d\n",
		    tv[i].bits,
		    enc_ok ? "OK  " : "FAIL",
		    dec_ok ? "OK  " : "FAIL",
		    canary_ok ? "OK" : "VIOLATED(schedule overran subkey[68])",
		    ctx.bits);
		if (!enc_ok) {
			dump("expected-ct", exp, 16);
			dump("got-ct", ct, 16);
		}
		if (!enc_ok || !dec_ok || !canary_ok || ctx.bits != tv[i].bits)
			fail++;
	}
	printf("RESULT-%s: %d failures\n", fail ? "FAIL" : "PASS", fail);
	return (fail);
}

static int
one_guard(int bits, int verbose)
{
	unsigned char key[32], pt[16], ct[16];
	camellia_ctx ctx;
	int i, untouched = 1, identity, refused;

	memset(key, 0xAB, sizeof(key));
	hexparse("0123456789abcdeffedcba9876543210", pt, 16);

	memset(&ctx, 0, sizeof(ctx));
	memset(ctx.subkey, 0xA5, sizeof(ctx.subkey));
	ctx.bits = 0x7B;			/* marker: fix must leave it */

	/* the call under audit: void return, no error signalling at all */
	camellia_set_key(&ctx, key, bits);

	for (i = 0; i < CAMELLIA_SUBKEYWORD; i++)
		if (ctx.subkey[i] != 0xA5A5A5A5u) {
			untouched = 0;
			break;
		}

	memset(ct, 0x5A, 16);
	camellia_encrypt(&ctx, pt, ct);
	identity = (memcmp(ct, pt, 16) == 0);
	refused = (ct[0] == 0x5A);

	if (verbose && bits == 64) {
		printf("after camellia_set_key(key, 64):\n");
		printf("  ctx->bits : %d (0x%x)\n", ctx.bits, ctx.bits);
		printf("  schedule  : %s\n", untouched ?
		    "UNTOUCHED -- never generated for this key length" :
		    "overwritten");
		dump("  plaintext ", pt, 16);
		dump("  ciphertext", ct, 16);
		printf("  encrypt   : %s\n", identity ?
		    "IDENTITY (ct == pt, no rounds executed)" :
		    (refused ? "REFUSED (dst untouched -- fixed behavior)" :
		     "transformed"));
	}
	if (ctx.bits == bits)
		return (identity ? 2 : 1);	/* invalid length accepted */
	if (ctx.bits == 0x7B && untouched)
		return (refused ? -1 : -2);	/* rejected, ctx preserved */
	return (0);
}

static int
guard(void)
{
	static const int bad[] = { 64, 0, -1, 129 };
	int i, accepted = 0, rejected = 0;

	printf("semantics: %s (KASSERT %s)\n",
#ifdef NDEBUG
	    "production (-DNDEBUG)",
	    "COMPILED OUT"
#else
	    "debug (assert active)",
	    "ACTIVE"
#endif
	    );

	for (i = 0; i < (int)(sizeof(bad) / sizeof(bad[0])); i++) {
		int r = one_guard(bad[i], bad[i] == 64);

		if (r >= 1) {
			printf("bits=%-4d : ACCEPTED%s\n", bad[i],
			    r == 2 ? "  (encrypt = identity)" : "");
			accepted++;
		} else if (r == -1) {
			printf("bits=%-4d : REJECTED (ctx untouched, encrypt refused)\n",
			    bad[i]);
			rejected++;
		} else {
			printf("bits=%-4d : indeterminate state (%d)\n",
			    bad[i], r);
		}
	}

	if (accepted == 4) {
		printf("BUG-CONFIRMED: invalid key lengths silently accepted;"
		    " schedule never generated; encryption is the identity"
		    " function\n");
		return (42);
	}
	if (rejected == 4) {
		printf("BUG-NOT-PRESENT: invalid key lengths rejected before"
		    " touching key material; encrypt/decrypt refuse invalid"
		    " schedules\n");
		return (0);
	}
	printf("INDETERMINATE: accepted=%d rejected=%d\n", accepted, rejected);
	return (1);
}

int
main(int argc, char **argv)
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s kat|guard\n", argv[0]);
		return (2);
	}
	if (strcmp(argv[1], "kat") == 0)
		return (kat() ? 3 : 0);
	if (strcmp(argv[1], "guard") == 0)
		return (guard());
	fprintf(stderr, "unknown mode %s\n", argv[1]);
	return (2);
}
