| Did you know ... | Search Documentation: |
| Pack logtalk -- logtalk-3.101.0/docs/handbook/_sources/libraries/otp.rst.txt |
.. _library_otp:
otp
The otp library provides predicates for generating and verifying
HOTP and TOTP one-time passwords following:
https://www.rfc-editor.org/rfc/rfc4226
https://www.rfc-editor.org/rfc/rfc6238
The implementation is built on top of the hmac and base32
libraries.
Open the `../../apis/library_index.html#otp <../../apis/library_index.html#otp>`__ link in a web browser.
To load all entities in this library, load the loader.lgt utility
file:
::
| ?- logtalk_load(otp(loader)).
To test this library predicates, load the tester.lgt file:
::
| ?- logtalk_load(otp(tester)).
The library currently provides the following public predicates:
otp::hotp/5otp::totp/5otp::hotp_verify/7otp::totp_verify/7
The arguments are:hotp(Hash, Secret, Counter, Digits, OTP)totp(Hash, Secret, UnixTime, Digits, OTP)hotp_verify(Hash, Secret, Counter, Window, Digits, OTP, MatchedCounter)totp_verify(Hash, Secret, UnixTime, Window, Digits, OTP, MatchedTimeStep)
The totp/5 and totp_verify/7 predicates take an explicit Unix
time in seconds. The current implementation uses the standard 30-second
time step and the Unix epoch T0 = 0.
The Secret argument can be given either as a raw byte list or using
one of the following Base32 wrapper terms:
base32(atom(Atom))base32(chars(Chars))base32(codes(Codes))
For example:
::
| ?- otp::hotp(sha1, base32(atom('GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ')), 0, 6, OTP).
OTP = '755224'
yes
| ?- atom_codes('12345678901234567890', Secret),
otp::hotp(sha1, Secret, 1, 6, OTP).
OTP = '287082'
Secret = [49,50,51,52,53,54,55,56,57,48,49,50,51,52,53,54,55,56,57,48]
yes
HOTP generation uses the supplied moving counter directly:
::
| ?- atom_codes('12345678901234567890', Secret),
otp::hotp(sha1, Secret, 9, 6, OTP).
OTP = '520489'
yes
TOTP generation first converts the explicit Unix time to the corresponding 30-second time step and then computes an HOTP value for that step:
::
| ?- atom_codes('12345678901234567890', Secret),
otp::totp(sha1, Secret, 59, 8, OTP).
OTP = '94287082'
yes
Generated OTP values are returned as zero-padded atoms so that leading zeros are preserved.
The otp::hotp_verify/7 predicate searches forward from the supplied
counter through a bounded counter window and returns the matching
counter on success.
::
| ?- atom_codes('12345678901234567890', Secret),
otp::hotp_verify(sha1, Secret, 0, 9, 6, '520489', MatchedCounter).
MatchedCounter = 9
Secret = [49,50,51,52,53,54,55,56,57,48,49,50,51,52,53,54,55,56,57,48]
yes
The otp::totp_verify/7 predicate searches within a symmetric
time-step window around the current time step and returns the matching
time step on success.
::
| ?- atom_codes('12345678901234567890', Secret),
otp::totp_verify(sha1, Secret, 29, 1, 8, '94287082', MatchedTimeStep).
MatchedTimeStep = 1
Secret = [49,50,51,52,53,54,55,56,57,48,49,50,51,52,53,54,55,56,57,48]
yes
The Hash argument must identify a currently loaded hash object that
provides digest/2, digest_size/1, and block_size/1, and
whose digest size is at least 20 bytes.
In practice, the standard RFC examples in this library use sha1,
sha256, and sha512 on unbounded-integer backends. Hashes such as
md5 are rejected because their digest size is too short for this
library's validation rules.
The current repository hash implementation only defines sha1,
sha256, and sha512 on unbounded-integer backends. On
bounded-integer backends, the hashes library loads hash_32.lgt,
which does not provide those digest objects.
As a consequence:
otp library itself still loads on bounded backendssha1, sha256, or sha512
is not available thereotp::hotp(sha1, Secret, Counter, Digits, OTP) fail
with domain_error(otp_hash, sha1) on bounded-integer backends
This is a limitation of the currently available hash objects in the repository, not of the OTP control-flow logic itself.