From: mar77i Date: Fri, 4 Aug 2017 17:20:01 +0000 (+0200) Subject: initial commit X-Git-Url: https://git.mar77i.info/?a=commitdiff_plain;h=1d2441778ae51a1aaf567358a5a30740ee39de98;p=ga-cli initial commit --- 1d2441778ae51a1aaf567358a5a30740ee39de98 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..123a83b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +otp +otp.o diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9172814 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2017 mar77i + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e8c524 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ + +CFLAGS += -D_DEFAULT_SOURCE $(PROD_CFLAGS) +LDFLAGS += $(PROD_LDFLAGS) +LDLIBS += -lcrypto + +otp: otp.o + +all: + +include global.mk + +.PHONY: all diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ccc9a4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# OTP + +Simple time-based OTP token generator in C. diff --git a/global.mk b/global.mk new file mode 100644 index 0000000..92175d8 --- /dev/null +++ b/global.mk @@ -0,0 +1,35 @@ + +# global.mk +# +# Copyright (c) 2017, mar77i +# +# This software may be modified and distributed under the terms +# of the ISC license. See the LICENSE file for details. + +# useful compiler flags; thanks xavier roche @ httrack blog + +CFLAGS += -std=c99 -pipe -fvisibility=hidden -Wall -Wextra -Wformat \ + -Wformat-security -Wreturn-type -Wpointer-arith -Winit-self \ + -Wsign-compare -Wmultichar -Wuninitialized -Werror -funroll-loops \ + -funswitch-loops -pedantic + +SHARED_CFLAGS = -fPIC -fvisibility=default + +LDFLAGS += -Wl,-z,relro,-z,now,-O1,--as-needed,--no-undefined \ + -Wl,--build-id=sha1,--no-allow-shlib-undefined -rdynamic + +VALGRIND_FLAGS = --trace-children=yes --leak-check=full --track-origins=yes \ + --show-leak-kinds=all + +LINK = $(CC) $(LDFLAGS) $> $^ $(LOADLIBES) $(LDLIBS) $(LDADD) -o $@ +LINK_SHARED = \ + $(CC) $(LDFLAGS) $> $^ $(LOADLIBES) $(LDLIBS) $(LDADD) -shared -o $@ + +vg: + valgrind $(VALGRIND_FLAGS) $(ARGS) + +DEV_CFLAGS = -Og -g +PROD_CFLAGS = -O3 +PROD_LDFLAGS = -Wl,--discard-all + +.PHONY: vg diff --git a/otp.c b/otp.c new file mode 100644 index 0000000..de06a61 --- /dev/null +++ b/otp.c @@ -0,0 +1,141 @@ + +// otp.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char **environ; + +#define INTERVAL_SECONDS 30 + +char *find_env_var(const char *match) { + size_t len = strlen(match); + char **env; + if(match == NULL) + return NULL; + for(env = environ; *env != NULL; env++) + if(strncmp(*env, match, len) == 0 && (*env)[len] == '=') + return *env + len + 1; + return NULL; +} + +int concat(char *dest, size_t *dest_used, size_t dest_size, char *src) { + size_t len = strlen(src); + if(dest == NULL || dest_used == NULL || src == NULL) + return -1; + if(*dest_used + len + (*dest_used == 0) > dest_size) + return -1; + if(*dest_used == 0) + (*dest_used)++; + memcpy(dest + *dest_used - 1, src, len + 1); + *dest_used += len; + return 0; +} + +int b32_decode(unsigned char *buf, size_t *len) { + const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + size_t i = 0; + char x = 0, *ptr; + unsigned char *output = buf, count, lsh; + if(buf == NULL) + return -1; + for(count = 1;; count++) { + while(isspace(buf[i])) + i++; + if(buf[i] == '\0') + break; + ptr = strchr(alphabet, toupper(buf[i++])); + if(ptr == NULL || *ptr == '\0') + return -1; + x = ptr - alphabet; + if(x < 0) + return -1; + lsh = (count * 3) % 8; + if(lsh > 3) + *output |= x >> (8 - lsh); + if(lsh + (count > 1) > 3) + output++; + *output = (x << lsh) | ((lsh < 3) * *output); + } + if(len != NULL) + *len = output - buf + 1; + return 0; +} + +static inline unsigned char *pack_be64(unsigned char data[], uint64_t value) { + size_t i; + for(i = 0; i < sizeof value; i++) + data[i] = (value >> ((sizeof value - 1 - i) * CHAR_BIT)) & 0xff; + return data; +} + +int calculate_otp(const char *line) { + size_t i, len = 0; + unsigned int hmac_len = 0; + unsigned char data[sizeof(uint64_t)], *ptr; + if(line == NULL) + return -1; + if(strlen(line) < 2) + return 0; + ptr = (unsigned char*)strchr(line, ':'); + if(ptr == NULL) + return -1; + printf("%.*s: ", (int)((char*)ptr - line), line); + ptr++; + if(b32_decode(ptr, &len) < 0) { + fprintf(stderr, "Error: b32_decode.\n"); + return -1; + } + + // fill data with the current time divided by INTERVAL_SECONDS + pack_be64(data, time(NULL) / INTERVAL_SECONDS); + if(HMAC(EVP_sha1(), ptr, len, data, sizeof data, ptr, &hmac_len) == NULL) { + fprintf(stderr, "\nError: hmac.\n"); + return -1; + } + i = ptr[hmac_len - 1] & 0x0f; + printf("%06"PRIu32"\n", (((ptr[i + 0] & 0x7f) << 24) | (ptr[i + 1] << 16) | + (ptr[i + 2] << 8) | ptr[i + 3]) % 1000000); + return 0; +} + +int main(void) { + FILE *fh; + size_t buf_used = 0; + int ret = EXIT_FAILURE; + char buf[PATH_MAX], *ptr = find_env_var("HOME"); + if(ptr == NULL) + return EXIT_FAILURE; + if(concat(buf, &buf_used, sizeof buf, ptr) < 0) + return EXIT_FAILURE; + ptr = buf + buf_used - 2; + while(*ptr == '/') { + *ptr-- = '\0'; + buf_used--; + } + if(concat(buf, &buf_used, sizeof buf, "/.otp_secrets") < 0) + return EXIT_FAILURE; + fh = fopen(buf, "rb"); + if(fh == NULL) { + perror("fopen"); + return EXIT_FAILURE; + } + while(fgets(buf, sizeof buf, fh) != NULL) + if(calculate_otp(buf) < 0) + goto error; + if(ferror(fh)) { + fprintf(stderr, "Error: read error."); + goto error; + } + ret = EXIT_SUCCESS; +error: + fclose(fh); + return ret; +}