File:  [ELWIX - Embedded LightWeight unIX -] / embedtools / src / voucher.c
Revision 1.2: download - view: text, annotated - select for diffs - revision graph
Sun Jul 22 22:46:48 2012 UTC (11 years, 10 months ago) by misho
Branches: MAIN
CVS tags: tools2_0, tools1_2, TOOLS1_2, TOOLS1_1, HEAD
version 1.1

#include "global.h"
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>


cfg_root_t cfg;
array_t *vs;
FILE *output;
int Verbose;
char szConfig[MAXPATHLEN] = VOUCHER_CFG;
extern char compiled[], compiledby[], compilehost[];


static void
Usage()
{
	printf(	" -= VOUCHER =- management tool\n"
		"=== %s === %s@%s ===\n\n"
		"Syntax: voucher [options] -r <RollID> [count]\n"
		"\tvoucher [options] -t <voucher> [voucher [voucher ...]]\n\n"
		"\t-v\t\tVerbose (more -v more verbosity)\n"
		"\t-r\t\tRequest new voucher(s) mode\n"
		"\t-t\t\tTest voucher(s) mode\n"
		"\t-g\t\tRequest new RSA pair mode\n"
		"\t-c <config>\tConfig file\n"
		"\t-o <output>\tOutput file [default=-]\n"
		"\n", compiled, compiledby, compilehost);
}

static void
AtExit()
{
	io_freeVars(&vs);
	if (output != stdout)
		fclose(output);
}

static inline int
RedirOutput(const char *name)
{
	AtExit();

	if (strcmp(name, "-")) {
		output = fopen(name, "w+");
		if (!output) {
			printf("Error:: can't redirect output #%d - %s\n", 
					errno, strerror(errno));
			return -1;
		}
	} else
		output = stdout;
	return 0;
}

static int
NewRSA()
{
	RSA *k = RSA_generate_key(VOUCHER_MAX_RSA * 8, 65537, NULL, NULL);
	if (!k) {
		printf("Error:: can't generate RSA key\n");
		return 2;
	}

	PEM_write_RSAPrivateKey(output, k, NULL, NULL, 0, NULL, NULL);
	PEM_write_RSA_PUBKEY(output, k);
	return 0;
}

static RSA *
LoadKey(char mode)
{
	FILE *f;
	RSA *key = NULL;
	ait_val_t v;

#ifndef NDEBUG
	ERR_load_crypto_strings();
#endif

	AIT_INIT_VAL(&v);
	if (mode == 1)
		cfg_loadAttribute(&cfg, "voucher", "key_private", &v, VOUCHER_KEY);
	else
		cfg_loadAttribute(&cfg, "voucher", "key_public", &v, VOUCHER_CRT);

	f = fopen(AIT_GET_STR(&v), "r");
	AIT_FREE_VAL(&v);
	if (!f) {
		printf("Error:: open key #%d - %s\n", errno, strerror(errno));
		return NULL;
	}

	if (mode == 1)
		key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
	else
		key = PEM_read_RSA_PUBKEY(f, NULL, NULL, NULL);

	fclose(f);

	if (!key)
		printf("Error:: wrong key !!!\n");
	return key;
}

static void
ShowConfig(char mode, int rid, int cnt)
{
	register int i;

	if (mode == 1)
		printf(">>> Request voucher ticket(s) %d for Roll %d\n", cnt, rid);
	else
		printf(">>> Test voucher ticket(s) %d\n", cnt);

	for (i = 0; i < io_arraySize(vs); i++)
		printf(" + Voucher[%d]= %s\n", i, AIT_GET_STR(io_getVars(&vs, i)));
	printf(" + Roll ID %d bits\n", (int) 
			strtol(cfg_getAttribute(&cfg, "voucher", "rollbits"), NULL, 0));
	printf(" + Ticket ID %d bits\n", (int) 
			strtol(cfg_getAttribute(&cfg, "voucher", "ticketbits"), NULL, 0));
	printf(" + CheckSum %d bits\n", (int) 
			strtol(cfg_getAttribute(&cfg, "voucher", "cksumbits"), NULL, 0));
	printf(" + Magic 0x%llx\n", (uint64_t) 
			strtoll(cfg_getAttribute(&cfg, "voucher", "magic"), NULL, 0));
	printf(" + Charset %s\n", cfg_getAttribute(&cfg, "voucher", "charset"));
	if (!cfg_getAttribute(&cfg, "voucher", "key_private"))
		cfg_setAttribute(&cfg, "voucher", "key_private", VOUCHER_KEY);
	printf(" + Private key %s\n", cfg_getAttribute(&cfg, "voucher", "key_private"));
	if (!cfg_getAttribute(&cfg, "voucher", "key_public"))
		cfg_setAttribute(&cfg, "voucher", "key_public", VOUCHER_CRT);
	printf(" + Public key %s\n", cfg_getAttribute(&cfg, "voucher", "key_public"));
}

static int
CheckConfig(char mode, int rid, int cnt)
{
	const char *str;

	str = cfg_getAttribute(&cfg, "voucher", "charset");
	if (!str || strlen(str) < 2) {
		printf("Error:: charset too short ... '%s'\n", str);
		return -1;
	}
	if (strtol(cfg_getAttribute(&cfg, "voucher", "rollbits"), NULL, 0) > 31 || 
			strtol(cfg_getAttribute(&cfg, "voucher", "ticketbits"), NULL, 0) > 31 || 
			strtol(cfg_getAttribute(&cfg, "voucher", "cksumbits"), NULL, 0) > 31) {
		printf("Error:: bits must be between 1..31\n");
		return -1;
	}
	if (mode == 1) {
		if (rid >= 1LL << strtol(cfg_getAttribute(&cfg, "voucher", "rollbits"), NULL, 0)) {
			printf("Error:: Roll bits must be 0..%lu\n", 
					strtol(cfg_getAttribute(&cfg, "voucher", "rollbits"), NULL, 0));
			return -1;
		}
		if (cnt < 1 || cnt >= 1LL << strtol(cfg_getAttribute(&cfg, "voucher", "ticketbits"), NULL, 0)) {
			printf("Error:: Ticket bits count must be 1..%lu\n", 
					strtol(cfg_getAttribute(&cfg, "voucher", "ticketbits"), NULL, 0));
			return -1;
		}
	}

	return 0;
}

static inline void
ll2buf(uint64_t ll, u_char * __restrict buf, int len)
{
	register int i;

	for (i = len - 1; i >= 0; i--) {
		buf[i] = ll & 0xff;
		ll >>= 8;
	}
}

static inline void
buf2ll(u_char * __restrict buf, uint64_t * __restrict ll, int len)
{
	register int i;

	for (i = 1, *ll = buf[0]; i < len; i++) {
		*ll <<= 8;
		*ll += buf[i];
	}
}

static int
ComputeVouchers(int rid, int cnt, RSA * __restrict key)
{
	int base, clen, alen;
	u_int roll, ticket, cksum, mb;
	uint64_t clrcode, code, magic;
	register int i, num;
	const char *charset;
	u_char clrbuf[VOUCHER_MAX_RSA], codebuf[VOUCHER_MAX_LEN], *p = codebuf;

	/* prepare vars */
	roll = strtol(cfg_getAttribute(&cfg, "voucher", "rollbits"), NULL, 0);
	ticket = strtol(cfg_getAttribute(&cfg, "voucher", "ticketbits"), NULL, 0);
	cksum = strtol(cfg_getAttribute(&cfg, "voucher", "cksumbits"), NULL, 0);

	magic = strtoll(cfg_getAttribute(&cfg, "voucher", "magic"), NULL, 0);

	charset = cfg_getAttribute(&cfg, "voucher", "charset");
	base = strlen(charset);
	clen = RSA_size(key);
	alen = clen < sizeof(clrcode) ? clen : sizeof(clrcode);
	if (((roll + ticket + cksum) >> 3) + 1 > alen) {
		printf("Error: roll+ticket+cksum bits too large for given key\n");
		return -1;
	}

	mb = alen * 8 - (roll + ticket + cksum + 1);
	if (mb > 0)
		magic &= (1LL << mb) - 1;
	else {
		mb ^= mb;
		magic ^= magic;
	}

	for (i = 1; i <= cnt; i++) {
		clrcode = magic << cksum;
		clrcode += (rid + i) % (1 << cksum);
		clrcode <<= ticket;
		clrcode += i;
		clrcode = (clrcode << roll) + rid;
		clrcode &= 0xffffffffffffffffLL >> (65 - 8 * clen);

		ll2buf(clrcode, clrbuf, clen);

		if (RSA_private_encrypt(clen, clrbuf, codebuf, key, RSA_NO_PADDING) == -1) {
			printf("Error:: ticket[%d] RSA private encrypt %s\n", i, 
					ERR_error_string(ERR_get_error(), NULL));
			continue;
		}

		buf2ll(codebuf, &code, clen);

		/* pretty print */
		memset(codebuf, 0, sizeof codebuf);
		for (p = codebuf, num = sizeof codebuf; code && num; num--) {
			*p++ = charset[code % base];
			code /= base;
		}
		if (code) {
			printf("Error:: voucher gets too long ... (%llu)\n", code);
			continue;
		}

		printf("Voucher[%d]= %s\n", i, codebuf);
	}

	return 0;
}

static int
TestVouchers(array_t * __restrict v, RSA * __restrict key)
{
	int base, clen, alen;
	u_int roll, ticket, cksum, mb;
	uint64_t clrcode, code, magic;
	register int i, checksum, rid, cnt;
	const char *charset, *voucher;
	u_char clrbuf[VOUCHER_MAX_RSA], codebuf[VOUCHER_MAX_LEN], *p, *s;

	/* prepare vars */
	roll = strtol(cfg_getAttribute(&cfg, "voucher", "rollbits"), NULL, 0);
	ticket = strtol(cfg_getAttribute(&cfg, "voucher", "ticketbits"), NULL, 0);
	cksum = strtol(cfg_getAttribute(&cfg, "voucher", "cksumbits"), NULL, 0);

	magic = strtoll(cfg_getAttribute(&cfg, "voucher", "magic"), NULL, 0);

	charset = cfg_getAttribute(&cfg, "voucher", "charset");
	base = strlen(charset);
	clen = RSA_size(key);
	alen = clen < sizeof(clrcode) ? clen : sizeof(clrcode);
	if (((roll + ticket + cksum) >> 3) + 1 > alen) {
		printf("Error: roll+ticket+cksum bits too large for given key\n");
		return -1;
	}

	mb = alen * 8 - (roll + ticket + cksum + 1);
	if (mb > 0)
		magic &= (1LL << mb) - 1;
	else {
		mb ^= mb;
		magic ^= magic;
	}

	for (i = 0, code = 0; i < io_arraySize(v); i++, code = 0) {
		voucher = AIT_GET_STR(io_getVars(&v, i));

		/* convert from voucher string to number */
		p = (u_char*) strrchr(voucher, '\0'); 
		while (p > (u_char*) voucher) {
			p--;
			if (*p == ' ')
				break;
			code *= base;
			s = (u_char*) strchr(charset, *p);
			if (!s) {
				VERB(1) printf("Error:: illegal character (%c) found in %s\n", 
						*p, voucher);
				printf("Ticket[%d] = %s \"%s\"\n", i, VOUCHER_ERR, voucher);
				break;
			}
			code += (s - (u_char*) charset);
		}

		ll2buf(code, codebuf, clen);

		if (RSA_public_decrypt(clen, codebuf, clrbuf, key, RSA_NO_PADDING) == -1) {
			VERB(1) printf("Error:: ticket[%d] RSA decrypt failed %s\n", i, 
					ERR_error_string(ERR_get_error(), NULL));
			printf("Ticket[%d] = %s \"%s\"\n", i, VOUCHER_ERR, voucher);
			continue;
		}

		buf2ll(clrbuf, &clrcode, clen);

		rid = clrcode & ((1 << roll) - 1);
		cnt = (clrcode >> roll) & ((1 << ticket) - 1);
		checksum = (clrcode >> (ticket + roll)) & ((1 << cksum) - 1);
		if (magic != ((clrcode >> (roll + ticket + cksum)))) {
			VERB(1) printf("Error:: ticket[%d] invalid magic\n", i);
			printf("Ticket[%d] = %s \"%s\"\n", i, VOUCHER_ERR, voucher);
			continue;
		}
		if ((cnt + rid) % (1L << cksum) != checksum) {
			VERB(1) printf("Error:: ticket[%d] invalid checksum\n", i);
			printf("Ticket[%d] = %s \"%s\"\n", i, VOUCHER_ERR, voucher);
			continue;
		}

		printf("Ticket[%d] = %s \"%s\" %d %d\n", i, VOUCHER_OK, voucher, rid, cnt);
	}

	return 0;
}


int
main(int argc, char **argv)
{
	char ch, mode = 0;
	int rid = 0, cnt = 1;
	register int i;
	RSA *key;

	output = stdout;
	atexit(AtExit);

#ifdef __NetBSD__
	srandom(getpid() ^ time(NULL));
#else
	srandomdev();
#endif

	while ((ch = getopt(argc, argv, "hvrtgc:o:")) != -1)
		switch (ch) {
			case 'r':
				mode = 1;
				break;
			case 't':
				mode = 2;
				break;
			case 'g':
				return NewRSA();
			case 'c':
				strlcpy(szConfig, optarg, sizeof szConfig);
				break;
			case 'o':
				RedirOutput(optarg);
				break;
			case 'v':
				Verbose++;
				break;
			case 'h':
			default:
				Usage();
				return 1;
		}
	argc -= optind;
	argv += optind;
	if (!argc || !mode || mode > 2) {
		printf("Error:: not enough parameter or unspecified mode ...\n\n");
		Usage();
		return 1;
	}
	if (mode == 1) {
		if (argc > 1)
			cnt = strtol(argv[1], NULL, 0);
		rid = strtol(argv[0], NULL, 0);
	} else {
		cnt = argc;
		vs = io_allocVars(cnt);
		for (i = 0; i < argc; i++)
			AIT_SET_STR(io_getVars(&vs, i), argv[i]);
	}

	if (cfgLoadConfig(szConfig, &cfg)) {
		printf("Error:: load config #%d - %s\n", cfg_GetErrno(), cfg_GetError());
		return 3;
	} else {
		VERB(1) ShowConfig(mode, rid, cnt);
		if (CheckConfig(mode, rid, cnt)) {
			cfgUnloadConfig(&cfg);
			return 3;
		}
	}

	if (!(key = LoadKey(mode))) {
		cfgUnloadConfig(&cfg);
		return 4;
	}
	if (RSA_size(key) > VOUCHER_MAX_RSA) {
		printf("Error:: RSA key size %d bits. Max %d bits\n", 
				RSA_size(key) * 8, VOUCHER_MAX_RSA * 8);
		RSA_free(key);
		cfgUnloadConfig(&cfg);
		return 5;
	} else
		VERB(1) printf(" + Key size %d bits\n\n", RSA_size(key) * 8);

	if (mode == 1) {
		if (ComputeVouchers(rid, cnt, key)) {
			RSA_free(key);
			cfgUnloadConfig(&cfg);
			return 6;
		}
	} else {
		if (TestVouchers(vs, key)) {
			RSA_free(key);
			cfgUnloadConfig(&cfg);
			return 6;
		}
	}

	RSA_free(key);
	cfgUnloadConfig(&cfg);
	return 0;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>