You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
2.2 KiB
93 lines
2.2 KiB
/* |
|
* 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; |
|
}
|
|
|