One Time Passwords

One time password schemes are a user authentication method that relies on a fixed secret key which is used to derive a sequence of short passwords, each of which is accepted only once. Commonly this is used to implement two-factor authentication (2FA), where the user authenticates using both a conventional password (or a public key signature) and an OTP generated by a small device such as a mobile phone.

Botan implements the HOTP and TOTP schemes from RFC 4226 and 6238.

Since the range of possible OTPs is quite small, applications must rate limit OTP authentication attempts to some small number per second. Otherwise an attacker could quickly try all 1000000 6-digit OTPs in a brief amount of time.

HOTP

HOTP generates OTPs that are a short numeric sequence, between 6 and 8 digits (most applications use 6 digits), created using the HMAC of a 64-bit counter value. If the counter ever repeats the OTP will also repeat, thus both parties must assure the counter only increments and is never repeated or decremented. Thus both client and server must keep track of the next counter expected.

Anyone with access to the client-specific secret key can authenticate as that client, so it should be treated with the same security consideration as would be given to any other symmetric key or plaintext password.

class HOTP

Implement counter-based OTP

HOTP(const SymmetricKey &key, const std::string &hash_algo = "SHA-1", size_t digits = 6)

Initialize an HOTP instance with a secret key (specific to each client), a hash algorithm (must be SHA-1, SHA-256, or SHA-512), and the number of digits with each OTP (must be 6, 7, or 8).

In RFC 4226, HOTP is only defined with SHA-1, but many HOTP implementations support SHA-256 as an extension. The collision attacks on SHA-1 do not have any known effect on HOTP’s security.

uint32_t generate_hotp(uint64_t counter)

Return the OTP assosciated with a specific counter value.

std::pair<bool, uint64_t> verify_hotp(uint32_t otp, uint64_t starting_counter, size_t resync_range = 0)

Check if a provided OTP matches the one that should be generated for the specified counter.

The starting_counter should be the counter of the last successful authentication plus 1. If resync_resync is greater than 0, some number of counter values above starting_counter will also be checked if necessary. This is useful for instance when a client mistypes an OTP on entry; the authentication will fail so the server will not update its counter, but the client device will subsequently show the OTP for the next counter. Depending on the environment a resync_range of 3 to 10 might be appropriate.

Returns a pair of (is_valid,next_counter_to_use). If the OTP is invalid then always returns (false,starting_counter), since the last successful authentication counter has not changed.

TOTP

TOTP is based on the same algorithm as HOTP, but instead of a counter a timestamp is used.

class TOTP
TOTP(const SymmetricKey &key, const std::string &hash_algo = "SHA-1", size_t digits = 6, size_t time_step = 30)

Setup to perform TOTP authentication using secret key key.

uint32_t generate_totp(std::chrono::system_clock::time_point time_point)
uint32_t generate_totp(uint64_t unix_time)

Generate and return a TOTP code based on a timestamp.

bool verify_totp(uint32_t otp, std::chrono::system_clock::time_point time, size_t clock_drift_accepted = 0)
bool verify_totp(uint32_t otp, uint64_t unix_time, size_t clock_drift_accepted = 0)

Return true if the provided OTP code is correct for the provided timestamp. If required, use clock_drift_accepted to deal with the client and server having slightly different clocks.