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