Author | Jamozed <[email protected]> |
Date | 2021-01-23 08:37:30 |
Commit | 79268b0e0cc19eca85467e18b2ec8b2380b75b5f |
Parent | db33ffebe20544f08eb23f2ba6a5b660c6b98f96 |
otp: Add otp utility
Diffstat
M | CMakeLists.txt | | | 5 | +++++ |
M | README.md | | | 1 | + |
A | src/otp.c | | | 152 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 158 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index adbfbca..a6ba700 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,8 @@ SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) SET(CMAKE_STATIC_LIBRARY_PREFIX "") +FIND_PACKAGE(OpenSSL REQUIRED) + FILE(GLOB LIBSRC ${PROJECT_SOURCE_DIR}/src/lib/*) ADD_LIBRARY(lib STATIC ${LIBSRC}) @@ -16,3 +18,6 @@ LINK_LIBRARIES(lib) ADD_EXECUTABLE(alder32 ${PROJECT_SOURCE_DIR}/src/alder32.c) ADD_EXECUTABLE(crc32 ${PROJECT_SOURCE_DIR}/src/crc32.c) +ADD_EXECUTABLE(otp ${PROJECT_SOURCE_DIR}/src/otp.c) + +TARGET_LINK_LIBRARIES(otp OpenSSL::Crypto) diff --git a/README.md b/README.md index 433b85c..00ba130 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ OMKOV cryptutils implements many cryptographic utilities. | ---------------- | ---------------------------------------- | -------- | | alder32 | Compute the Alder-32 for a file | | | crc32 | Compute the CRC-32 for a file | | +| otp | Compute HOTP or TOTP tokens | | ## Build Instructions diff --git a/src/otp.c b/src/otp.c new file mode 100644 index 0000000..d9841e2 --- /dev/null +++ b/src/otp.c @@ -0,0 +1,152 @@ +// otp.c, version 0.1.0 +// OMKOV cryptutils otp +// Copyright (C) 2020, Jakob Wakeling +// All rights reserved. + +/* +OMKOV Permissive Licence, version 1.0 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimers. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimers in the documentation and/or + other materials provided with the distribution. +* Neither the names of the copyright holders, nor the names of its contributors + may be used to endorse or promote products derived from this Software without + specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +*/ + +/* + TODO Switch to base32 for secret input, as this is more commonly used. +*/ + +#include "lib/error.c" +#include "lib/optget.h" + +#include <openssl/evp.h> +#include <openssl/hmac.h> + +#include <math.h> +#include <stdint.h> +#include <stdio.h> + +#define VERSION "0.1.0" + +static struct lop lops[] = { + { "help", ARG_NUL, 256 }, + { "version", ARG_NUL, 257 }, + { NULL, 0, 0 } +}; + +static const uint8_t B16D[]; + +static inline uint32_t hotp(uint8_t *K, size_t Klen, uint64_t C); +static uint8_t *b16d(char *s, size_t l); + +static void hlp(void); +static void ver(void); + +int main(int ac, char *av[]) { A0 = av[0]; + struct opt opt = OPTGET_INIT; opt.str = ""; opt.lops = lops; + for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) { + case 256: { hlp(); return 0; } + case 257: { ver(); return 0; } + default: { return 1; } + } + + if (opt.ind == ac) { error(1, "missing operand"); } + + uint8_t *k; size_t l = strlen(av[opt.ind]); uint64_t c; + + if (opt.ind == ac - 1) { c = time(NULL) / 30; } + else { + register uint64_t d; register char *p = av[opt.ind + 1]; + for (c = 0; *p >= '0' && *p <= '9'; ++p) { + d = (uint64_t)*p - '0'; + if (c > (UINT64_MAX - d) / 10) { break; } + c = c * 10 + d; + } + + if (*p) { error(1, "invalid counter"); } + } + + if (!(k = b16d(av[opt.ind], l))) { error(1, "invalid secret"); } + printf("%06u\n", hotp(k, l / 2, c)); free(k); return 0; +} + +/* Compute HOTP token with a key and counter */ +static inline uint32_t hotp(uint8_t *K, size_t Klen, uint64_t C) { + // Convert the counter to big endian if it isn't already + uint32_t e = 0x000000FF; if ((*(uint8_t *)&e) == 0xFF) { + C = ((C & 0x00000000FFFFFFFF) << 32) | ((C & 0xFFFFFFFF00000000) >> 32); + C = ((C & 0x0000FFFF0000FFFF) << 16) | ((C & 0xFFFF0000FFFF0000) >> 16); + C = ((C & 0x00FF00FF00FF00FF) << 8) | ((C & 0xFF00FF00FF00FF00) >> 8); + } + + // Step 1: Generate HMAC-SHA-1 digest + uint8_t *d = HMAC(EVP_sha1(), K, Klen, (uint8_t *)&C, sizeof (C), NULL, 0); + + // Step 2: Truncate digest to 4 bytes + uint32_t dt = + (d[(d[19] & 0x0F)] & 0x7F) << 24 | + (d[(d[19] & 0x0F) + 1] & 0xFF) << 16 | + (d[(d[19] & 0x0F) + 2] & 0xFF) << 8 | + (d[(d[19] & 0x0F) + 3] & 0xFF); + + // Step 3: Calculate modulus of dt to get HOTP token + return dt % (uint32_t)pow(10, 6/*Token length*/); +} + +/* Decode base16 to an array */ +static uint8_t *b16d(char *s, size_t l) { + if (l % 2) { return NULL; } + + uint8_t *a = (uint8_t *)malloc((l / 2) * sizeof (*a)); + + for (uint8_t *i = a; *s; ++i, s += 2) { + *i = (B16D[s[0]] << 4) | B16D[s[1]]; + } return a; +} + +/* Print help information */ +static void hlp(void) { + puts("otp - compute hotp or totp tokens\n"); + puts("usage: otp secret [counter]\n"); + puts("options:"); + puts(" --help Display help information"); + puts(" --version Display version information"); + return; +} + +/* Print version information */ +static void ver(void) { + puts("OMKOV cryptutils otp, version " VERSION); + puts("Copyright (C) 2020, Jakob Wakeling"); + puts("All rights reserved."); + puts("OMKOV Permissive Licence (https://www.omkov.net/OLPE)"); + return; +} + +static const uint8_t B16D[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0 +};