diff --git a/Makefile b/Makefile
index 8d09720..55079d6 100644
--- a/Makefile
+++ b/Makefile
@@ -17,8 +17,8 @@
P = otpcli
V = 0.0
-HDR = err.h
-OBJ = cli.o err.o
+HDR = err.h otp.h
+OBJ = cli.o err.o otp.o
SRC = ${OBJ:.o=.c}
DIST_FILES = COPYING Makefile ${HDR} ${SRC}
diff --git a/otp.c b/otp.c
new file mode 100644
index 0000000..87c3b0c
--- /dev/null
+++ b/otp.c
@@ -0,0 +1,82 @@
+/*
+ * otpcli - CLI utility for generating OTPs
+ *
+ * Written in 2020 by Lucas
+ *
+ * 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
+ * .
+ */
+
+#include
+
+#include
+
+#include "otp.h"
+
+static int32_t
+otp(const EVP_MD *evp_md, const void *key, int key_len, uint64_t counter,
+ unsigned int digits)
+{
+ unsigned char md_value[EVP_MAX_MD_SIZE];
+ uint8_t buf[8];
+ uint32_t res;
+ unsigned int md_len, modulo, offset;
+
+ if (digits < 6 || digits > 10)
+ return -1;
+ for (modulo = 1000000; digits > 6; digits--, modulo *= 10)
+ ;
+
+ /* convert counter to big endian */
+ buf[0] = counter >> 56;
+ buf[1] = (counter >> 48) & 0xff;
+ buf[2] = (counter >> 40) & 0xff;
+ buf[3] = (counter >> 32) & 0xff;
+ buf[4] = (counter >> 24) & 0xff;
+ buf[5] = (counter >> 16) & 0xff;
+ buf[6] = (counter >> 8) & 0xff;
+ buf[7] = counter & 0xff;
+
+ if (HMAC(evp_md, key, key_len, buf, sizeof(buf), md_value, &md_len) ==
+ NULL)
+ return -1;
+ offset = md_value[md_len - 1] & 0xf;
+ res = (md_value[offset] << 24) |
+ (md_value[offset + 1] << 16) |
+ (md_value[offset + 2] << 8) |
+ md_value[offset + 3];
+ res &= ~0x80000000;
+
+ return (int32_t)res % modulo;
+}
+
+int32_t
+hotp(enum otp_hmac hmac, const void *key, size_t key_len, uint64_t counter,
+ unsigned int digits)
+{
+ const EVP_MD *evp_md;
+
+ if (key_len > INT_MAX)
+ return -1;
+
+ switch (hmac) {
+ case OTP_HMAC_SHA1: evp_md = EVP_sha1(); break;
+ case OTP_HMAC_SHA256: evp_md = EVP_sha256(); break;
+ case OTP_HMAC_SHA512: evp_md = EVP_sha512(); break;
+ }
+
+ return otp(evp_md, key, (int)key_len, counter, digits);
+}
+
+int32_t
+totp(enum otp_hmac hmac, const void *key, size_t key_len, time_t t,
+ unsigned int granularity, unsigned int digits)
+{
+ return -1;
+}
diff --git a/otp.h b/otp.h
new file mode 100644
index 0000000..812009e
--- /dev/null
+++ b/otp.h
@@ -0,0 +1,27 @@
+/*
+ * otpcli - CLI utility for generating OTPs
+ *
+ * Written in 2020 by Lucas
+ *
+ * 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
+ * .
+ */
+
+#include
+#include
+
+enum otp_hmac {
+ OTP_HMAC_SHA1,
+ OTP_HMAC_SHA256,
+ OTP_HMAC_SHA512,
+};
+
+int32_t hotp(enum otp_hmac, const void *, size_t, uint64_t, unsigned int);
+int32_t totp(enum otp_hmac, const void *, size_t, time_t, unsigned int,
+ unsigned int);