otpcli/cli.c

157 lines
3.5 KiB
C
Raw Permalink Normal View History

2020-06-14 05:23:29 +02:00
/*
2021-02-13 00:05:40 +01:00
* otpcli - command-line interface for HOTP and TOTP
* Written in 2020-2021 by Lucas
2020-06-14 05:23:29 +02:00
*
* 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/>.
*/
2020-06-14 06:02:43 +02:00
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
2020-06-14 05:23:29 +02:00
#include <stdio.h>
#include <stdlib.h>
2020-06-14 06:02:43 +02:00
#include <string.h>
#include <time.h>
2020-06-14 06:02:43 +02:00
#include <unistd.h>
2020-06-14 05:23:29 +02:00
#include "base32.h"
2020-06-14 05:23:29 +02:00
#include "err.h"
#include "strtonum.h"
2020-06-14 05:23:29 +02:00
#include "otp.h"
2020-06-14 17:00:33 +02:00
extern const char *__progname;
2020-06-14 05:23:29 +02:00
static void
usage(void)
{
fprintf(stderr, "Usage:\n"
2021-02-12 02:41:54 +01:00
" %s [-a HMAC] [-d digits] -H counter SECRET\n"
" %s [-a HMAC] [-d digits] [-p period] [-T time] SECRET\n",
__progname, __progname);
2020-06-14 05:23:29 +02:00
exit(1);
}
int
main(int argc, char *argv[])
{
const char *errstr;
unsigned char *secret;
char *in, *line;
struct otpcfg otpcfg;
size_t inlen, linesz;
2021-02-12 00:13:08 +01:00
ssize_t linelen;
2020-06-14 06:02:43 +02:00
uint64_t counter;
2021-02-12 02:38:22 +01:00
unsigned int period;
2020-06-14 06:02:43 +02:00
int32_t r;
int ch, digits, do_hotp, do_totp, secretlen;
enum otp_hmac hmac;
2020-06-14 06:02:43 +02:00
counter = (uint64_t)time(NULL);
digits = 6;
2020-06-14 20:23:14 +02:00
do_hotp = do_totp = 0;
hmac = OTP_HMAC_SHA1;
2021-02-12 02:38:22 +01:00
period = 30;
2021-02-12 02:41:54 +01:00
while ((ch = getopt(argc, argv, "a:d:H:p:T:")) != -1) {
2020-06-14 06:02:43 +02:00
switch (ch) {
2021-02-12 02:41:54 +01:00
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;
2020-06-14 06:02:43 +02:00
case 'H':
counter = strtonum(optarg, 0, LLONG_MAX, &errstr);
if (errstr != NULL)
errx(1, "counter is %s: %s", errstr, optarg);
2020-06-14 06:02:43 +02:00
do_hotp = 1;
break;
2021-02-12 02:38:22 +01:00
case 'p':
period = strtonum(optarg, 1, UINT_MAX, &errstr);
if (errstr != NULL)
2021-02-12 02:38:22 +01:00
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);
2020-06-14 20:23:14 +02:00
do_totp = 1;
break;
2020-06-14 06:02:43 +02:00
default:
usage();
}
}
argc -= optind;
argv += optind;
2020-06-14 20:23:14 +02:00
if (do_hotp && do_totp) {
warnx("-H and -T are mutually exclusive");
usage();
}
if (argc > 1)
2020-06-14 06:02:43 +02:00
usage();
if (argc == 1) {
in = argv[0];
inlen = strlen(in);
} else {
2021-02-12 00:13:08 +01:00
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;
}
secretlen = b32_decoded_len(in, inlen);
if (secretlen == -1)
errx(1, "invalid base32 string: %s", in);
secret = malloc(secretlen);
if (secret == NULL)
err(1, "malloc");
if (!b32_decode(secret, secretlen, in, inlen))
errx(1, "error decoding base32 string");
otpcfg.algorithm = hmac;
otpcfg.digits = digits;
otpcfg.secret = secret;
otpcfg.secretlen = secretlen;
2020-06-14 06:02:43 +02:00
if (do_hotp) {
otpcfg.type = OTP_HOTP;
otpcfg.u.hotp.counter = counter;
} else {
otpcfg.type = OTP_TOTP;
otpcfg.u.totp.time = counter;
otpcfg.u.totp.period = period;
}
2020-06-14 06:02:43 +02:00
r = otp(&otpcfg);
free(secret);
if (r == -1)
errx(1, "couldn't calculate %cOTP", do_hotp ? 'H' : 'T');
printf("%0*" PRId32 "\n", digits, r);
2020-06-14 05:23:29 +02:00
return 0;
}