version 1.1, 2012/07/04 13:49:02
|
version 1.2, 2012/07/22 22:46:48
|
Line 0
|
Line 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; |
|
} |