From a6a3b5aeb08405dd63050ea5d2c818f88db84fc8 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 18 Jun 2017 01:57:23 -0400 Subject: [PATCH] Added initial code for producing time-based One Time Passwords. --- otp.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 otp.c diff --git a/otp.c b/otp.c new file mode 100644 index 0000000..1ca3e38 --- /dev/null +++ b/otp.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include "sha1.h" + +static int base32_decode(const char *src, const int srclen, uint8_t *dst, const int dstlen) +{ + const int len = srclen == -1 ? strlen((const char *) src) : srclen; + int retval = 0; + uint32_t accum = 0; + int shifter = 0; + int i; + + for (i = 0; i < len; i++) { + const uint8_t ch = (uint8_t) src[i]; + uint8_t val; + + if ((ch >= 'A') && (ch <= 'Z')) { + val = ch - 'A'; + } else if ((ch >= '2') && (ch <= '7')) { + val = (ch - '2') + 26; + } else if (ch == '=') { + val = 0; + + /* these are illegal in base32, but GAuth keys might have them. */ + } else if (ch == ' ') { + continue; /* skip these. */ + } else if ((ch >= 'a') && (ch <= 'z')) { + val = ch - 'a'; /* treat like uppercase. */ + + } else { + return -1; /* invalid string. */ + } + + accum = (accum << 5) | ((uint32_t) val); + shifter += 5; + if (shifter >= 8) { + if (retval > dstlen) { + return -1; /* dst too small */ + } + dst[retval] = (uint8_t) ((accum >> (shifter - 8)) & 0xFF); + retval++; + shifter -= 8; + } + } + + if (shifter > 0) { + if (retval > dstlen) { + return -1; /* dst too small */ + } + dst[retval] = (uint8_t) (accum & 0xFF); + retval++; + } + + return retval; +} + +static int totp(const char *base32_secret, char *dst, int dstlen) +{ + uint8_t decoded[64]; + int decodedlen; + uint64_t secs; + uint8_t timebytes[8]; + unsigned char digest[SHA1_DIGEST_LENGTH]; + uint8_t *bytes; + uint32_t val; + + decodedlen = base32_decode(base32_secret, -1, decoded, sizeof (decoded)); + if (decodedlen == -1) { + return -1; + } + + secs = ((uint64_t) time(NULL)) / 30; + for (int i = 0; i < 8; i++) { + timebytes[i] = (uint8_t) (secs >> ((7-i) * 8)); + } + + SHA1Hmac(decoded, decodedlen, timebytes, 8, digest); + + bytes = (uint8_t *) (digest + (digest[SHA1_DIGEST_LENGTH-1] & 0xF)); + val = (((uint32_t) bytes[0]) << 24) | (((uint32_t) bytes[1]) << 16) | + (((uint32_t) bytes[2]) << 8) | (((uint32_t) bytes[3])); + val &= 0x7FFFFFFF; /* drop most significant bit. */ + val %= 1000000; /* make it six digits long. */ + + snprintf(dst, dstlen, "%06u", (unsigned int) val); + return 0; +} + +int main(int argc, char **argv) +{ + char result[16]; + int i; + for (i = 1; i < argc; i++) { + printf("%s: ", argv[i]); + if (totp(argv[i], result, sizeof (result)) == -1) { + printf("[FAILED!]"); + } else { + printf("%s (valid for %d more seconds)", result, 30 - ((int) (time(NULL) % 30))); + } + printf("\n"); + } + return 0; +} + +/* end of otp.c ... */ +