otpcli/cli.c

139 lines
3.0 KiB
C

/*
* otpcli - CLI utility for generating OTPs
*
* 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
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "err.h"
#include "strtonum.h"
#include "otp.h"
#define LINE_SIZE 16384
extern const char *__progname;
static void
usage(void)
{
fprintf(stderr, "Usage:\n"
" %s [-d digits] [-h HMAC] -H counter SECRET\n"
" %s [-d digits] [-h HMAC] [-s step] [-T counter] SECRET\n",
__progname, __progname);
exit(1);
}
int
main(int argc, char *argv[])
{
const char *errstr;
char *key;
size_t key_len;
uint64_t counter;
unsigned int step;
int32_t r;
int ch, digits, do_hotp, do_totp;
enum otp_hmac hmac;
counter = (uint64_t)time(NULL);
digits = 6;
do_hotp = do_totp = 0;
hmac = OTP_HMAC_SHA1;
step = 30;
while ((ch = getopt(argc, argv, "d:H:h:s:T:")) != -1) {
switch (ch) {
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 'h':
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 's':
step = strtonum(optarg, 1, UINT_MAX, &errstr);
if (errstr != NULL)
errx(1, "step 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) {
key = argv[0];
key_len = strlen(key);
} else {
key = malloc(sizeof(uint8_t) * (LINE_SIZE + 1));
if (key == NULL)
err(1, "malloc");
if (fgets(key, LINE_SIZE + 1, stdin) == NULL)
err(1, "fgets");
key_len = strlen(key);
if (key[key_len - 1] == '\n') {
key[key_len - 1] = '\0';
key_len--;
}
}
if (do_hotp) {
r = hotp(hmac, key, key_len, counter, digits);
if (r == -1)
errx(1, "couldn't calculate HOTP");
printf("%0*" PRId32 "\n", digits, r);
} else {
r = totp(hmac, key, key_len, counter, step, digits);
if (r == -1)
errx(1, "couldn't calculate TOTP");
printf("%0*" PRId32 "\n", digits, r);
}
return 0;
}