botan-low-0.0.1.0: Low-level Botan bindings
Copyright(c) Leo D 2023
LicenseBSD-3-Clause
Maintainerleo@apotheca.io
Stabilityexperimental
PortabilityPOSIX
Safe HaskellSafe-Inferred
LanguageHaskell2010

Botan.Low.TOTP

Description

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.

Synopsis

Time-based one time passwords

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.

TOTP generates OTPs that are a short numeric sequence, between 6 and 8 digits (most applications use 6 digits), created using a 64-bit timestamp 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 their clocks synchronized.

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.

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

To use TOTP for MFA / 2FA, the client authenticator must generate a client-specific shared secret, and securely communicate it to the server authenticator.

The secret key may be any bytestring value with more than 160 bits, such as a Bcrypt digest or SRP6 shared key.

import Botan.Low.TOTP
import Botan.Low.RNG
import Data.Time.Clock.POSIX
timestep = 30
drift = 3
sharedSecret <- systemRNGGet 16

The client and server authenticators are now in a shared state, and any login attempts from a new device may be authenticated using TOTP as MFA.

A client has requested a new connection, and TOTP is being used as MFA/2FA to authenticate their request. The server authenticator receives the client connection request and initializes a TOTP session using the stored client-specific shared secret, and then sends an authentication request to the client authenticator:

-- serverSharedSecret <- lookupServerSharedSecret
serverSession <- totpInit serverSharedSecret TOTP_SHA512 8 timestep
-- sendMFAAuthenticationRequest
NOTE: We are using a timestep value of 30 seconds, which means that the
code will refresh every 30 seconds

The client authenticator receives the authentication request, generates a client-side code using their timestamp, and displays the TOTP code to the user:

-- clientSharedSecret <- lookupClientSharedSecret
clientSession <- totpInit clientSharedSecret TOTP_SHA512 8 timestep
(clientTimestamp :: TOTPTimestamp) <- round <$> getPOSIXTime
clientCode <- totpGenerate clientSession clientTimestamp
-- displayClientCode clientCode

The client then sends the client code to the server authenticator using the unauthenticated / requested connection:

-- clientCode <- readClientCode
-- sendMFAAuthenticationResponse clientCode

The server authenticator receives the authentication response, and performs a check of the key, with an acceptable clock drift in steps, in case the client and server are slightly desynchronized.

-- serverClientCode <- didreceiveMFAAuthenticationResponse
(serverTimestamp :: TOTPTimestamp) <- round <$> getPOSIXTime
isValid <- totpCheck serverSession serverClientCode serverTimestamp drift
NOTE: We are using a acceptable clock drift value of 3, which means that the
codes for the previous 3 time steps are still valid.

If the code is valid, then the signin may be completed on the new connection as normal.

The server should discontinue the session and refuse any new connections to the account after multiple unsuccessful authentication attempts. The user should then be notified.

TOTP

newtype TOTP Source #

Constructors

MkTOTP 

Fields

withTOTP :: TOTP -> (BotanTOTP -> IO a) -> IO a Source #

totpInit Source #

Arguments

:: ByteString

key[]

-> TOTPHashName

hash_algo

-> Int

digits

-> TOTPTimestep

time_step

-> IO TOTP

totp

Initialize a TOTP instance

NOTE: Digits should be 6-8

totpGenerate Source #

Arguments

:: TOTP

totp: the TOTP object

-> TOTPTimestamp

totp_code: the OTP code will be written here

-> IO TOTPCode

timestamp: the current local timestamp

Generate a TOTP code for the provided timestamp

totpCheck Source #

Arguments

:: TOTP

totp: the TOTP object

-> TOTPCode

totp_code: the presented OTP

-> TOTPTimestamp

timestamp: the current local timestamp

-> Int

acceptable_clock_drift: specifies the acceptable amount of clock drift (in terms of time steps) between the two hosts.

-> IO Bool 

Verify a TOTP code

TOTP Hashes

Convenience