/* * otpcli - command-line interface for HOTP and TOTP * Written in 2020-2021 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; int32_t modulo; unsigned int md_len, offset; if (digits < 6 || digits > 10) return -1; /* * 10 digits would be 10_000_000_000, which overflows 32 bits. * Nevertheless, given the number construction, 10 digits OTP are * possible. We won't apply reminder operator in that case. */ if (digits < 10) for (modulo = 1000000; digits > 6; digits--, modulo *= 10) continue; /* 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 &= 0x7fffffff; return digits == 10 ? (int32_t)res : (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; default: return -1; } return otp(evp_md, key, (int)key_len, counter, digits); } int32_t totp(enum otp_hmac hmac, const void *key, size_t key_len, uint64_t t, unsigned int period, unsigned int digits) { return hotp(hmac, key, key_len, t / period, digits); }