#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>