C library that generates TOTP and HOTP according to RFC-6238
- GCC/Clang and CMake to build the library
- libgcrypt >= 1.8.0 or openssl >= 3.0.0 or mbedtls (works with both 2.x and 3.x)
$ git clone https://github.com/paolostivanin/libcotp.git
$ cd libcotp
$ mkdir build && cd $_
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make
$ sudo make install
Available options you can pass to cmake
:
-DBUILD_TESTS=ON
: if you want to compile also the tests (default OFF, requires criterion)-DBUILD_SHARED_LIBS=OFF
: if you want to build libcotp as a static library (default ON)-DHMAC_WRAPPER="<gcrypt|openssl|mbedtls>"
: you can choose between GCrypt, OpenSSL or MbedTLS (default Gcrypt)
char *totp = get_totp (const char *base32_encoded_secret,
int digits,
int period,
int algo,
cotp_error_t *err);
char *steam_totp = get_steam_totp (const char *secret,
int period,
cotp_error_t *err);
char *hotp = get_hotp (const char *base32_encoded_secret,
long counter,
int digits,
int algo,
cotp_error_t *err);
char *totp_at = get_totp_at (const char *base32_encoded_secret,
long target_date,
int digits,
int algo,
cotp_error_t *err);
int64_t otp_i = otp_to_int (const char *otp,
cotp_error_t *err_code);
where:
secret_key
is the base32 encoded secret. Usually, a website gives you the secret already base32 encoded, so you should pay attention to not encode the secret again. The format of the secret can either behxdm vjec jjws
orHXDMVJECJJWS
. In the first case, the library will normalize the secret to second format before computing the OTP.digits
is between3
and10
inclusiveperiod
is between1
and120
inclusivecounter
is a value decided with the servertarget_date
is the target date specified as the unix epoch format in secondsalgo
is eitherSHA1
,SHA256
orSHA512
get_totp
, get_hotp
and get_totp_at
return NULL
if an error occurs and err
is set to one of the following values:
Errors:
GCRYPT_VERSION_MISMATCH
, set if the installed Gcrypt library is too oldINVALID_B32_INPUT
, set if the given input is not valid base32 textINVALID_ALGO
, set if the given algo is not supported by the libraryINVALID_PERIOD
, set ifperiod
is<= 0
or> 120
secondsINVALID_DIGITS
, set ifdigits
is< 4
or> 10
MEMORY_ALLOCATION_ERROR
, set if an error happened during memory allocationINVALID_USER_INPUT
, set if the given input is not validINVALID_COUNTER
, set ifcounter
is< 0
All good:
NO_ERROR
, set if no error occurredVALID
, set if the given OTP is valid
The function otp_to_int
:
- returns
-1
if an error occurs and setserr
toINVALID_USER_INPUT
. - warns the user if the leading zero is missing. For example, since the otp string
"012345"
can't be returned as the integer012345
(because it would be interpreted as octal number), the function returns12345
and setserr
toMISSING_LEADING_ZERO
)
In case of success, the value returned by get_totp
, get_hotp
, get_totp_at
and get_steam_totp
must be freed once no longer needed.
Since release 2.0.0, libbaseencode has been merged with libcotp. This means that you can now use base32 functions by just including cotp.h
:
char *base32_encode (const uchar *user_data,
size_t data_len,
cotp_error_t *err_code);
uchar *base32_decode (const char *user_data,
size_t data_len,
cotp_error_t *err_code);
bool is_string_valid_b32 (const char *user_data);
where:
user_data
is the data to be encoded or decodeddata_len
is the length of the data to be encoded/decodederr_code
is where the error is stored
base32_encode
returns NULL
if an error occurs and err_code
is set to one of the following values:
INVALID_USER_INPUT
, set if the given input is not validMEMORY_ALLOCATION_ERROR
, set if an error happened during memory allocationINVALID_USER_INPUT
, set if the given input is not valid
base32_decode
returns NULL
if an error occurs and err_code
is set to one of the following values:
INVALID_USER_INPUT
, set if the given input is not validMEMORY_ALLOCATION_ERROR
, set if an error happened during memory allocationINVALID_B32_INPUT
, set if the given input is not valid base32 textINVALID_USER_INPUT
, set if the given input is not valid
Both functions return and empty string if the input is an empty string. In such a case, err
is set to EMPTY_STRING
.
is_string_valid_b32
returns true
if user_data
is a valid base32 encoded string, false
otherwise. Please note that user_data
can contain spaces, since
the fucntion will also take care of trimming those.