94 lines
2.2 KiB
C
94 lines
2.2 KiB
C
/*
|
|
* 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
|
|
* <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
*/
|
|
|
|
#include <limits.h>
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
#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;
|
|
}
|