/*
 * DF-4577 proof-of-arithmetic: simulate intel_hdcp_validate_v_prime's
 * KSV->HDCP_SHA_TEXT byte-stream construction (DF/sys form) versus the
 * upstream-fixed form (master: off = (4 - j - 1 - sha_leftovers)*8) and
 * the true required stream, for N downstream devices (5-byte KSVs).
 *
 * Pure userspace simulation on the audit host. Compile: cc -o sim sim.c
 */
#include <stdio.h>
#include <string.h>

#define KSV_LEN 5
#define MAXN 6

/* emit-word callback: gets each 32-bit word the driver would write */
static int words_old[16][8], words_new[16][8];
static int nw_old[MAXN + 1], nw_new[MAXN + 1];

static void sim(int n, int fixed)
{
	int sha_leftovers = 0, sha_text = 0, i, j;
	int (*w)[8] = fixed ? words_new : words_old;
	int *nw = fixed ? &nw_new[n] : &nw_old[n];
	int idx = 0;
	memset(w, 0, sizeof(words_old));
	*nw = 0;

	for (i = 0; i < n; i++) {
		int ksv[KSV_LEN];
		int sha_empty;
		for (j = 0; j < KSV_LEN; j++)
			ksv[j] = i * KSV_LEN + j + 1;	/* stream bytes 1,2,3... */

		sha_empty = 4 - sha_leftovers;
		for (j = 0; j < sha_empty; j++) {
			int off = fixed ? (4 - j - 1 - sha_leftovers) * 8
					: (4 - j - 1) * 8;
			sha_text |= ksv[j] << off;
		}
		w[n][idx++] = sha_text;	/* intel_write_sha_text(sha_text) */

		sha_leftovers = KSV_LEN - sha_empty;
		sha_text = 0;
		for (j = 0; j < sha_leftovers; j++)
			sha_text |= ksv[sha_empty + j] << ((4 - j - 1) * 8);

		if (4 > sha_leftovers)
			continue;
		w[n][idx++] = sha_text;
		sha_leftovers = 0;
		sha_text = 0;
	}
	*nw = idx;
}

static void dump_words(const char *tag, int (*w)[8], int n, int nw)
{
	int i, b;
	printf("  %-10s:", tag);
	for (i = 0; i < nw; i++)
		for (b = 3; b >= 0; b--)
			printf(" %2d", (w[n][i] >> (b * 8)) & 0xff);
	printf("\n");
}

int main(void)
{
	int n;
	for (n = 1; n <= 5; n++) {
		int bytes = n * KSV_LEN, k, true_stream[64];

		sim(n, 0);	/* DF / pre-fix form */
		sim(n, 1);	/* upstream-fixed form */

		printf("N=%d downstream device(s), %d KSV bytes:\n", n, bytes);
		for (k = 0; k < bytes; k++)
			true_stream[k] = k + 1;
		printf("  required   :");
		for (k = 0; k < bytes; k++)
			printf(" %2d", true_stream[k]);
		printf("\n");
		dump_words("DF (old)", words_old, n, nw_old[n]);
		dump_words("upstream", words_new, n, nw_new[n]);

		/* compare DF stream vs required over the KSV bytes */
		int df[64], ndf = 0, ok = 1;
		for (k = 0; k < nw_old[n]; k++) {
			int b;
			for (b = 3; b >= 0; b--)
				df[ndf++] = (words_old[n][k] >> (b * 8)) & 0xff;
		}
		for (k = 0; k < bytes; k++)
			if (df[k] != true_stream[k])
				ok = 0;
		printf("  => DF KSV stream %s the required concatenation",
		       ok ? "MATCHES" : "CORRUPTS");
		if (!ok) {
			printf(" (first divergence at byte ");
			for (k = 0; k < bytes; k++)
				if (df[k] != true_stream[k]) {
					printf("%d: got %d want %d", k + 1,
					       df[k], true_stream[k]);
					break;
				}
			printf(")");
		}
		printf("\n\n");
	}
	return 0;
}
