--- embedtools/src/voucher.c 2012/07/04 15:59:10 1.1.2.2 +++ embedtools/src/voucher.c 2017/06/28 15:19:32 1.4 @@ -1,10 +1,58 @@ -#include "global.h" +/************************************************************************* + * (C) 2011 AITNET - Sofia/Bulgaria - + * by Michael Pounov + * + * $Author: misho $ + * $Id: voucher.c,v 1.4 2017/06/28 15:19:32 misho Exp $ + * + ************************************************************************* +The ELWIX and AITNET software is distributed under the following +terms: +All of the documentation and software included in the ELWIX and AITNET +Releases is copyrighted by ELWIX - Sofia/Bulgaria -io_enableDEBUG; +Copyright 2004 - 2017 + by Michael Pounov . All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: +This product includes software developed by Michael Pounov +ELWIX - Embedded LightWeight unIX and its contributors. +4. Neither the name of AITNET nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. +*/ +#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[]; @@ -19,6 +67,7 @@ Usage() "\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); @@ -27,6 +76,7 @@ Usage() static void AtExit() { + ait_freeVars(&vs); if (output != stdout) fclose(output); } @@ -48,17 +98,312 @@ RedirOutput(const char *name) 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 < array_Size(vs); i++) + printf(" + Voucher[%d]= %s\n", i, AIT_GET_STR(ait_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%jx\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 ... (%ju)\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 < array_Size(v); i++, code = 0) { + voucher = AIT_GET_STR(ait_getVars((array_t**) &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, cnt = 1; + int rid = 0, cnt = 1; + register int i; + RSA *key; output = stdout; atexit(AtExit); - while ((ch = getopt(argc, argv, "hvrtc:o:")) != -1) +#ifdef __NetBSD__ + srandom(getpid() ^ time(NULL)); +#else + srandomdev(); +#endif + + while ((ch = getopt(argc, argv, "hvrtgc:o:")) != -1) switch (ch) { case 'r': mode = 1; @@ -66,6 +411,8 @@ main(int argc, char **argv) case 't': mode = 2; break; + case 'g': + return NewRSA(); case 'c': strlcpy(szConfig, optarg, sizeof szConfig); break; @@ -73,7 +420,7 @@ main(int argc, char **argv) RedirOutput(optarg); break; case 'v': - io_incDebug; + Verbose++; break; case 'h': default: @@ -82,16 +429,61 @@ main(int argc, char **argv) } argc -= optind; argv += optind; - if (!argc || !mode) { + 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 = ait_allocVars(cnt); + for (i = 0; i < argc; i++) + AIT_SET_STR(ait_getVars(&vs, i), argv[i]); + } + if (cfgLoadConfig(szConfig, &cfg)) { printf("Error:: load config #%d - %s\n", cfg_GetErrno(), cfg_GetError()); - return 2; + 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; }