├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── totp.c /.gitignore: -------------------------------------------------------------------------------- 1 | totp 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, Adrien Gallouët 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -Wall -O2 3 | prefix = /usr/local 4 | 5 | totp: 6 | $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) totp.c -o totp 7 | 8 | install: totp 9 | mkdir -p $(DESTDIR)$(prefix)/bin 10 | mv -f totp $(DESTDIR)$(prefix)/bin 11 | 12 | uninstall: 13 | rm -f $(DESTDIR)$(prefix)/bin/totp 14 | 15 | clean: 16 | rm -f totp 17 | 18 | .PHONY: totp install uninstall clean 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # totp 2 | A tiny command line utility to generate OTP tokens 3 | 4 | ## Build and install 5 | 6 | Clone the repository: 7 | 8 | $ git clone https://github.com/angt/totp 9 | $ cd totp 10 | 11 | Then, run as `root`: 12 | 13 | # make install 14 | 15 | As usual, you can customize the destination with `DESTDIR` and `prefix`. 16 | 17 | ## Usage 18 | 19 | It has been thought for [secret](https://github.com/angt/secret), 20 | maybe it will be directly integrated in a future version, in the meantime: 21 | 22 | ### Add a new TOTP key 23 | 24 | $ echo -n JBSWY3DPEHPK3PXP | base32 -d | secret set test/totp 25 | 26 | ### Generate a TOTP token 27 | 28 | $ secret show test/totp | totp 29 | $ 123456 30 | 31 | --- 32 | For feature requests and bug reports, 33 | please create an [issue](https://github.com/angt/totp/issues). 34 | -------------------------------------------------------------------------------- /totp.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 by angt 2 | // Don't try to reuse the HMAC-SHA1 implementation of this project, 3 | // you will suffer unproductively :P 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static inline void 12 | ebe32(uint8_t *dst, uint32_t x) 13 | { 14 | dst[0] = (uint8_t)(x >> 24); 15 | dst[1] = (uint8_t)(x >> 16); 16 | dst[2] = (uint8_t)(x >> 8); 17 | dst[3] = (uint8_t)(x ); 18 | } 19 | 20 | static inline uint32_t 21 | dbe32(const uint8_t *src) 22 | { 23 | return ((uint32_t)src[0] << 24) 24 | | ((uint32_t)src[1] << 16) 25 | | ((uint32_t)src[2] << 8) 26 | | ((uint32_t)src[3] ); 27 | } 28 | 29 | static inline void 30 | ebe64(uint8_t *dst, uint64_t x) 31 | { 32 | ebe32(dst, (uint32_t)(x >> 32)); 33 | ebe32(dst + 4, (uint32_t)(x )); 34 | } 35 | 36 | static inline uint32_t 37 | rotl(int n, uint32_t x) 38 | { 39 | return (x << n) | (x >> (32 - n)); 40 | } 41 | 42 | static void 43 | sha1_process(const uint8_t *buf, uint32_t x[5]) 44 | { 45 | uint32_t w[80]; 46 | uint32_t a = x[0], b = x[1], c = x[2], d = x[3], e = x[4]; 47 | 48 | for (int i = 0; i < 16; i++) 49 | w[i] = dbe32(&buf[i << 2]); 50 | 51 | for (int i = 16; i < 80; i++) 52 | w[i] = rotl(1, w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]); 53 | 54 | for (int i = 0; i < 80; i++) { 55 | uint32_t t = rotl(5, a) + e + w[i]; 56 | if (i < 20) t += 0x5A827999 + ((b & c) | ((~b) & d)); 57 | else if (i < 40) t += 0x6ED9EBA1 + (b ^ c ^ d); 58 | else if (i < 60) t += 0x8F1BBCDC + ((b & c) | (b & d) | (c & d)); 59 | else t += 0xCA62C1D6 + (b ^ c ^ d); 60 | e = d; d = c; c = rotl(30, b); b = a; a = t; 61 | } 62 | x[0] += a; x[1] += b; x[2] += c; x[3] += d; x[4] += e; 63 | } 64 | 65 | static void 66 | sha1(uint8_t *digest, uint8_t *buf, size_t len) 67 | { 68 | uint8_t tmp[64] = {0}; 69 | uint32_t x[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0}; 70 | size_t p = 0; 71 | 72 | for (; p + 64 <= len; p += 64) 73 | sha1_process(buf + p, x); 74 | 75 | if (len > p) 76 | memcpy(tmp, buf + p, len - p); 77 | 78 | p = len - p; 79 | tmp[p++] = 0x80; 80 | 81 | if (p > 56) { 82 | sha1_process(tmp, x); 83 | memset(tmp, 0, sizeof(tmp)); 84 | } 85 | ebe64(tmp + 56, len << 3); 86 | sha1_process(tmp, x); 87 | 88 | for (int i = 0; i < 5; i++) 89 | ebe32(&digest[i << 2], x[i]); 90 | } 91 | 92 | static void 93 | erase(void *buf, size_t len) 94 | { 95 | volatile uint8_t *volatile x = (volatile uint8_t *volatile)buf; 96 | 97 | for (size_t i = 0; i < len; i++) 98 | x[i] = 0; 99 | } 100 | 101 | int 102 | main(int argc, char **argv) 103 | { 104 | uint8_t h[20]; 105 | uint8_t ki[64 + 8] = {0}; 106 | uint8_t ko[64 + 20] = {0}; 107 | ssize_t len = read(0, ki, 64); 108 | 109 | if (len <= 0) 110 | return -1; 111 | 112 | memcpy(ko, ki, len); 113 | 114 | for (int i = 0; i < 64; i++) { 115 | ki[i] ^= 0x36; 116 | ko[i] ^= 0x5c; 117 | } 118 | ebe64(&ki[64], ((uint64_t)time(NULL)) / 30); 119 | sha1(&ko[64], ki, sizeof(ki)); 120 | sha1(h, ko, sizeof(ko)); 121 | 122 | erase(ki, sizeof(ki)); 123 | erase(ko, sizeof(ko)); 124 | 125 | uint32_t ret = (dbe32(&h[h[19] & 0xF]) & ~(UINT32_C(1) << 31)) 126 | % UINT32_C(1000000); 127 | 128 | printf("%06" PRIu32 "\n", ret); 129 | } 130 | --------------------------------------------------------------------------------