--- embedtools/src/voucher.c 2012/07/04 13:49:02 1.1 +++ embedtools/src/voucher.c 2012/07/22 22:46:48 1.2 @@ -0,0 +1,444 @@ +#include "global.h" +#include +#include +#include + + +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 [count]\n" + "\tvoucher [options] -t [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 \tConfig file\n" + "\t-o \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; +}