150 lines
3.3 KiB
C
150 lines
3.3 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 "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;
|
|
}
|