/* * 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" int32_t otp(const struct otpcfg *otpcfg) { const EVP_MD *evp_md; unsigned char md_value[EVP_MAX_MD_SIZE]; uint8_t buf[8]; uint64_t counter; uint32_t res; int32_t modulo; unsigned int digits, md_len, offset; switch (otpcfg->type) { case OTP_HOTP: counter = otpcfg->u.hotp.counter; break; case OTP_TOTP: if (otpcfg->u.totp.period == 0) return -1; counter = otpcfg->u.totp.time / otpcfg->u.totp.period; break; default: return -1; } switch (otpcfg->algorithm) { 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; } digits = otpcfg->digits; 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, otpcfg->secret, otpcfg->secretlen, 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; }