/* * otpcli - command-line interface for HOTP and TOTP * Written in 2020-2021 by Lucas * * To the extent possible under law, the author(s) have dedicated all * copyright and related and neighboring rights to this software to the * public domain worldwide. This software is distributed without any * warranty. * * You should have received a copy of the CC0 Public Domain Dedication * along with this software. If not, see * . */ #include #include #include #include #include #include #include #include #include "base32.h" #include "err.h" #include "strtonum.h" #include "otp.h" extern const char *__progname; static void usage(void) { fprintf(stderr, "Usage:\n" " %s [-a HMAC] [-d digits] -H counter SECRET\n" " %s [-a HMAC] [-d digits] [-p period] [-T time] SECRET\n", __progname, __progname); exit(1); } int main(int argc, char *argv[]) { const char *errstr; unsigned char *key; char *in, *line; size_t inlen, linesz; ssize_t linelen; uint64_t counter; unsigned int period; int32_t r; int ch, digits, do_hotp, do_totp, keylen; enum otp_hmac hmac; counter = (uint64_t)time(NULL); digits = 6; do_hotp = do_totp = 0; hmac = OTP_HMAC_SHA1; period = 30; while ((ch = getopt(argc, argv, "a:d:H:p:T:")) != -1) { switch (ch) { case 'a': if (strcasecmp(optarg, "sha1") == 0) hmac = OTP_HMAC_SHA1; else if (strcasecmp(optarg, "sha256") == 0) hmac = OTP_HMAC_SHA256; else if (strcasecmp(optarg, "sha512") == 0) hmac = OTP_HMAC_SHA512; else usage(); break; case 'd': digits = strtonum(optarg, 6, 10, &errstr); if (errstr != NULL) errx(1, "digits is %s: %s", errstr, optarg); break; case 'H': counter = strtonum(optarg, 0, LLONG_MAX, &errstr); if (errstr != NULL) errx(1, "counter is %s: %s", errstr, optarg); do_hotp = 1; break; case 'p': period = strtonum(optarg, 1, UINT_MAX, &errstr); if (errstr != NULL) errx(1, "period is %s: %s", errstr, optarg); break; case 'T': counter = strtonum(optarg, 0, LLONG_MAX, &errstr); if (errstr != NULL) errx(1, "counter is %s: %s", errstr, optarg); do_totp = 1; break; default: usage(); } } argc -= optind; argv += optind; if (do_hotp && do_totp) { warnx("-H and -T are mutually exclusive"); usage(); } if (argc > 1) usage(); if (argc == 1) { in = argv[0]; inlen = strlen(in); } else { line = NULL; linesz = 0; linelen = getline(&line, &linesz, stdin); if (linelen == -1 && ferror(stdin)) err(1, "getline"); if (linelen > 0 && line[linelen - 1] == '\n') { line[linelen - 1] = '\0'; linelen--; } in = line; inlen = linelen; } keylen = b32_decoded_len(in, inlen); if (keylen == -1) errx(1, "invalid base32 string: %s", in); key = malloc(keylen); if (key == NULL) err(1, "malloc"); if (!b32_decode(key, keylen, in, inlen)) errx(1, "error decoding base32 string"); if (do_hotp) { r = hotp(hmac, key, keylen, counter, digits); if (r == -1) errx(1, "couldn't calculate HOTP"); printf("%0*" PRId32 "\n", digits, r); } else { r = totp(hmac, key, keylen, counter, period, digits); if (r == -1) errx(1, "couldn't calculate TOTP"); printf("%0*" PRId32 "\n", digits, r); } return 0; }