]> git.mar77i.info Git - ga-cli/commitdiff
initial commit
authormar77i <mar77i@protonmail.ch>
Fri, 4 Aug 2017 17:20:01 +0000 (19:20 +0200)
committermar77i <mar77i@protonmail.ch>
Fri, 4 Aug 2017 17:21:24 +0000 (19:21 +0200)
.gitignore [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
global.mk [new file with mode: 0644]
otp.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..123a83b
--- /dev/null
@@ -0,0 +1,2 @@
+otp
+otp.o
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..9172814
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017 mar77i <mar77i at protonmail dot ch>
+
+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 (file)
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 (file)
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 (file)
index 0000000..92175d8
--- /dev/null
+++ b/global.mk
@@ -0,0 +1,35 @@
+
+# global.mk
+#
+# Copyright (c) 2017, mar77i <mar77i at protonmail dot ch>
+#
+# 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 (file)
index 0000000..de06a61
--- /dev/null
+++ b/otp.c
@@ -0,0 +1,141 @@
+
+// otp.c
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <openssl/hmac.h>
+
+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;
+}