/*
 * bfkat.c - known-answer test for sys/crypto/blowfish/bf_skey.c + bf_enc.c
 * (compiled as userland objects from the guest's /usr/src/sys tree).
 *
 * Vectors: Bruce Schneier, "Blowfish" test vectors (v1.0, 8-byte keys),
 * published with the reference implementation.
 * Wrap-loop/clamp tests cross-checked against openssl if available.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <crypto/blowfish/blowfish.h>

struct vec { const char *k, *p, *c; };

/* Schneier's published set (hex) */
static struct vec vecs[] = {
{"0000000000000000","0000000000000000","4EF997456198DD78"},
{"FFFFFFFFFFFFFFFF","FFFFFFFFFFFFFFFF","51866FD5B85ECB8A"},
{"3000000000000000","1000000000000001","7D856F9A613063F2"},
{"1111111111111111","1111111111111111","2466DD878B963C9D"},
{"0123456789ABCDEF","1111111111111111","61F9C3802281B096"},
{"1111111111111111","0123456789ABCDEF","7D0CC630AFDA1EC7"},
{"FEDCBA9876543210","0123456789ABCDEF","0ACEAB0FC6A0A28D"},
{"7CA110454A1A6E57","01A1D6D039776742","59C68245EB05282B"},
{"0131D9619DC1376E","5CD54CA83DEF57DA","B1B8CC0B250F09A0"},
};

static int
hex2bin(const char *hex, unsigned char *out, int maxlen)
{
	int n = 0;
	while (hex[0] && hex[1] && n < maxlen) {
		unsigned v;
		sscanf(hex, "%2x", &v);
		out[n++] = (unsigned char)v;
		hex += 2;
	}
	return n;
}

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

int
main(void)
{
	BF_KEY key;
	int i, fails = 0;

	for (i = 0; i < (int)(sizeof(vecs)/sizeof(vecs[0])); i++) {
		unsigned char kb[8], pb[8], got[16], exp[8];
		char gothex[64];
		BF_LONG d[2];

		hex2bin(vecs[i].k, kb, 8);
		hex2bin(vecs[i].p, pb, 8);
		hex2bin(vecs[i].c, exp, 8);

		BF_set_key(&key, 8, kb);
		d[0] = ((BF_LONG)pb[0] << 24) | ((BF_LONG)pb[1] << 16) |
		       ((BF_LONG)pb[2] << 8) | (BF_LONG)pb[3];
		d[1] = ((BF_LONG)pb[4] << 24) | ((BF_LONG)pb[5] << 16) |
		       ((BF_LONG)pb[6] << 8) | (BF_LONG)pb[7];
		BF_encrypt(d, &key);
		got[0] = d[0] >> 24; got[1] = d[0] >> 16;
		got[2] = d[0] >> 8;  got[3] = d[0];
		got[4] = d[1] >> 24; got[5] = d[1] >> 16;
		got[6] = d[1] >> 8;  got[7] = d[1];
		bin2hex(got, 8, gothex);
		if (strcmp(gothex, vecs[i].c) != 0) {
			printf("FAIL vec %d: got %s want %s\n", i, gothex, vecs[i].c);
			fails++;
		} else {
			printf("PASS vec %d: %s\n", i, gothex);
		}
	}

	/*
	 * Wrap-loop demo: variable-length keys 1..72 and a >72 key (clamp).
	 * No public KAT for these; emit ciphertext for openssl cross-match.
	 */
	{
		unsigned char keybuf[100], pt[8] = "BLOWFISH", got[8];
		char gothex[64];
		int lens[] = {1, 2, 3, 4, 5, 7, 8, 12, 16, 20, 24, 32, 56, 71, 72, 73, 100};
		size_t l;

		memset(keybuf, 0, sizeof(keybuf));
		for (l = 0; l < sizeof(keybuf); l++)
			keybuf[l] = (unsigned char)(l + 1);	/* distinguishable */
		for (i = 0; i < (int)(sizeof(lens)/sizeof(lens[0])); i++) {
			BF_LONG d[2];
			l = lens[i];
			BF_set_key(&key, l, keybuf);
			memcpy(got, pt, 8);
			d[0] = ((BF_LONG)got[0] << 24) | ((BF_LONG)got[1] << 16) |
			       ((BF_LONG)got[2] << 8) | (BF_LONG)got[3];
			d[1] = ((BF_LONG)got[4] << 24) | ((BF_LONG)got[5] << 16) |
			       ((BF_LONG)got[6] << 8) | (BF_LONG)got[7];
			BF_encrypt(d, &key);
			got[0] = d[0] >> 24; got[1] = d[0] >> 16;
			got[2] = d[0] >> 8;  got[3] = d[0];
			got[4] = d[1] >> 24; got[5] = d[1] >> 16;
			got[6] = d[1] >> 8;  got[7] = d[1];
			bin2hex(got, 8, gothex);
			printf("WRAP len=%d ct=%s\n", (int)l, gothex);
		}
		/* clamp equivalence: len=73..100 must equal len=72 result? NO -
		 * clamp keeps first 72 bytes; len=100 == len=72 output. Check. */
		{
			BF_LONG d[2];
			unsigned char g72[8], g100[8];
			BF_set_key(&key, 72, keybuf);
			d[0] = ((BF_LONG)pt[0] << 24) | ((BF_LONG)pt[1] << 16) |
			       ((BF_LONG)pt[2] << 8) | (BF_LONG)pt[3];
			d[1] = ((BF_LONG)pt[4] << 24) | ((BF_LONG)pt[5] << 16) |
			       ((BF_LONG)pt[6] << 8) | (BF_LONG)pt[7];
			BF_encrypt(d, &key);
			g72[0]=d[0]>>24;g72[1]=d[0]>>16;g72[2]=d[0]>>8;g72[3]=d[0];
			g72[4]=d[1]>>24;g72[5]=d[1]>>16;g72[6]=d[1]>>8;g72[7]=d[1];
			BF_set_key(&key, 100, keybuf);
			d[0] = ((BF_LONG)pt[0] << 24) | ((BF_LONG)pt[1] << 16) |
			       ((BF_LONG)pt[2] << 8) | (BF_LONG)pt[3];
			d[1] = ((BF_LONG)pt[4] << 24) | ((BF_LONG)pt[5] << 16) |
			       ((BF_LONG)pt[6] << 8) | (BF_LONG)pt[7];
			BF_encrypt(d, &key);
			g100[0]=d[0]>>24;g100[1]=d[0]>>16;g100[2]=d[0]>>8;g100[3]=d[0];
			g100[4]=d[1]>>24;g100[5]=d[1]>>16;g100[6]=d[1]>>8;g100[7]=d[1];
			if (memcmp(g72, g100, 8) == 0)
				printf("PASS clamp: len=100 == len=72\n");
			else {
				printf("FAIL clamp: len=100 != len=72\n");
				fails++;
			}
		}
	}

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