OTP-0.1.0.0: HMAC-Based and Time-Based One-Time Passwords (HOTP & TOTP)

LicenseMIT
Safe HaskellNone
LanguageHaskell2010

Data.OTP

Contents

Description

Implements the HMAC-Based One-Time Password Algorithm (HOTP) as defined in RFC 4226 and the Time-Based One-Time Password Algorithm (TOTP) as defined in RFC 6238.

Many operations in this module take or return a Word32 OTP value (whose most significant bit is always 0) which is truncated modulo 10^digits according to the Word8 digits parameter. Consequently, passing a value above 10 won't produce more than 10 digits and will effectively return the raw non-truncated 31-bit OTP value.

Since: OTP-0.1.0.0

Synopsis

HOTP

hotp Source #

Arguments

:: HashAlgorithm

Hashing algorithm

-> Secret

Shared secret

-> Word64

Counter value

-> Word8

Number of base10 digits in HOTP value

-> Word32

HOTP value

Compute HMAC-Based One-Time Password using secret key and counter value.

>>> hotp SHA1 "1234" 100 6
317569
>>> hotp SHA512 "1234" 100 6
134131
>>> hotp SHA512 "1234" 100 8
55134131

hotpCheck Source #

Arguments

:: HashAlgorithm

Hash algorithm to use

-> Secret

Shared secret

-> (Word8, Word8)

Valid counter range, before and after ideal

-> Word64

Ideal (expected) counter value

-> Word8

Number of base10 digits in a password

-> Word32

Password (i.e. HOTP value) entered by user

-> Bool

True if password is valid

Check presented password against a valid range.

>>> hotp SHA1 "1234" 10 6
50897
>>> hotpCheck SHA1 "1234" (0,0) 10 6 50897
True
>>> hotpCheck SHA1 "1234" (0,0) 9 6 50897
False
>>> hotpCheck SHA1 "1234" (0,1) 9 6 50897
True
>>> hotpCheck SHA1 "1234" (1,0) 11 6 50897
True
>>> hotpCheck SHA1 "1234" (2,2) 8 6 50897
True
>>> hotpCheck SHA1 "1234" (2,2) 7 6 50897
False
>>> hotpCheck SHA1 "1234" (2,2) 12 6 50897
True
>>> hotpCheck SHA1 "1234" (2,2) 13 6 50897
False

TOTP

totp Source #

Arguments

:: HashAlgorithm

Hash algorithm to use

-> Secret

Shared secret

-> UTCTime

Time of TOTP

-> Word64

Time range in seconds

-> Word8

Number of base10 digits in TOTP value

-> Word32

TOTP value

Compute a Time-Based One-Time Password using secret key and time.

>>> totp SHA1 "1234" (read "2010-10-10 00:01:00 UTC") 30 6
388892
>>> totp SHA1 "1234" (read "2010-10-10 00:01:00 UTC") 30 8
43388892
>>> totp SHA1 "1234" (read "2010-10-10 00:01:15 UTC") 30 8
43388892
>>> totp SHA1 "1234" (read "2010-10-10 00:01:31 UTC") 30 8
39110359

totpCheck Source #

Arguments

:: HashAlgorithm

Hash algorithm to use

-> Secret

Shared secret

-> (Word8, Word8)

Valid counter range, before and after ideal

-> UTCTime

Time of TOTP

-> Word64

Time range in seconds

-> Word8

Number of base10 digits in a password

-> Word32

Password given by user

-> Bool

True if password is valid

Check presented password against time periods.

>>> totp SHA1 "1234" (read "2010-10-10 00:00:00 UTC") 30 6
778374
>>> totpCheck SHA1 "1234" (0, 0) (read "2010-10-10 00:00:00 UTC") 30 6 778374
True
>>> totpCheck SHA1 "1234" (0, 0) (read "2010-10-10 00:00:30 UTC") 30 6 778374
False
>>> totpCheck SHA1 "1234" (1, 0) (read "2010-10-10 00:00:30 UTC") 30 6 778374
True
>>> totpCheck SHA1 "1234" (1, 0) (read "2010-10-10 00:01:00 UTC") 30 6 778374
False
>>> totpCheck SHA1 "1234" (2, 0) (read "2010-10-10 00:01:00 UTC") 30 6 778374
True

Auxiliary

totpCounter Source #

Arguments

:: UTCTime

Time of totp

-> Word64

Time range in seconds

-> Word64

Resulting counter

Calculate HOTP counter using time. Starting time (T0 according to RFC6238) is 0 (begining of UNIX epoch)

>>> totpCounter (read "2010-10-10 00:00:00 UTC") 30
42888960
>>> totpCounter (read "2010-10-10 00:00:30 UTC") 30
42888961
>>> totpCounter (read "2010-10-10 00:01:00 UTC") 30
42888962

counterRange Source #

Arguments

:: (Word8, Word8)

Number of counters before and after ideal

-> Word64

Ideal counter value

-> [Word64] 

Make a sequence of acceptable counters, protected from arithmetic overflow.

>>> counterRange (0, 0) 9000
[9000]
>>> counterRange (1, 0) 9000
[8999,9000]
>>> length $ counterRange (5000, 0) 9000
501
>>> length $ counterRange (5000, 5000) 9000
1000
>>> counterRange (2, 2) maxBound
[18446744073709551613,18446744073709551614,18446744073709551615]
>>> counterRange (2, 2) minBound
[0,1,2]
>>> counterRange (2, 2) (maxBound `div` 2)
[9223372036854775805,9223372036854775806,9223372036854775807,9223372036854775808,9223372036854775809]
>>> counterRange (5, 5) 9000
[8995,8996,8997,8998,8999,9000,9001,9002,9003,9004,9005]

RFC recommends avoiding excessively large values for counter ranges.

totpCounterRange :: (Word8, Word8) -> UTCTime -> Word64 -> [Word64] Source #

Make a sequence of acceptable periods.

>>> totpCounterRange (0, 0) (read "2010-10-10 00:01:00 UTC") 30
[42888962]
>>> totpCounterRange (2, 0) (read "2010-10-10 00:01:00 UTC") 30
[42888960,42888961,42888962]
>>> totpCounterRange (0, 2) (read "2010-10-10 00:01:00 UTC") 30
[42888962,42888963,42888964]
>>> totpCounterRange (2, 2) (read "2010-10-10 00:01:00 UTC") 30
[42888960,42888961,42888962,42888963,42888964]

data HashAlgorithm Source #

Hash algorithm used for HOTP/TOTP computations

Constructors

SHA1 
SHA256 
SHA512 
Instances
Eq HashAlgorithm Source # 
Instance details

Defined in HashImpl

Show HashAlgorithm Source # 
Instance details

Defined in HashImpl

type Secret = ByteString Source #

Shared secret encoded as raw octets