Time-based one-time password algorithm: Difference between revisions

m
→‎{{header|Wren}}: Minor tidy and rerun
m (→‎{{header|Wren}}: Minor tidy and rerun)
 
(31 intermediate revisions by 16 users not shown)
Line 2:
{{wikipedia}}
 
A Time-based One-time Password Algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It is the cornerstone of [[wp:Initiative_For_Open_Authentication|Initiative For Open Authentication (OATH)]] and is used in a number of two factor authentication systems. Essentially, both the server and the client compute the time-limited token, then the server checks if the token supplied by the client matches the locally generated token.
 
A Time-based One-time Password Algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It is the cornerstone of [[wp:Initiative_For_Open_Authentication|Initiative For Open Authentication (OATH)]] and is used in a number of two factor authentication systems.
The task here is to implement this algorithm using 'HMAC-SHA1' and an optional step is to generate the random [[wp:Base32|Base-32]] string used as the secret key, but this is not a requirement. A reference implementation, based on JavaScript, can be found at the following location:
 
Essentially, both the server and the client compute the time-limited token, then the server checks if the token supplied by the client matches the locally generated token.
 
 
;Task:
Implement this algorithm using '''HMAC-SHA1''' and an optional step is to generate the random [[wp:Base32|Base-32]] string used as the secret key, but this is not a requirement.
 
A reference implementation, based on JavaScript, can be found at the following location:
 
::: [http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript]
 
[http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript]
 
According to RFC 6238, the reference implementation is as follows:
*   Generate a key, '''K''', which is an arbitrary bytestring, and share it securely with the client.
*  Agree upon an epoch, T0, and an interval, TI, which will be used to calculate the value of the counter C (defaults are the Unix epoch as T0 and 30 seconds as TI)
*   Agree upon a cryptographic hash method (default is SHA-1)
*   Agree upon a token length, '''N''' (default is 6)
Although RFC 6238 allows different parameters to be used, the Google implementation of the authenticator app does not support T0, TI values, hash methods and token lengths different from the default. It also expects the K secret key to be entered (or supplied in a QR code) in base-32 encoding according to RFC 3548.
 
Although RFC 6238 allows different parameters to be used, the Google implementation of the authenticator app does not support T0, TI values, hash methods and token lengths different from the default.   It also expects the '''K''' secret key to be entered (or supplied in a QR code) in base-32 encoding according to RFC 3548.
* [https://itunes.apple.com/gb/app/google-authenticator/id388497605 Google Authenticator App (Apple iOS)]
 
* [https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2 Google Authenticator App (Google Android)]
*   [httphttps://wwwitunes.windowsphoneapple.com/en-us/storegb/app/google-authenticator/e7994dbc-2336-4950-91ba-ca22d653759bid388497605 MicrosoftGoogle Authenticator App (WindowsApple PhoneiOS)]
*   [https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2 Google Authenticator App (Google Android)]
*   [http://www.windowsphone.com/en-us/store/app/authenticator/e7994dbc-2336-4950-91ba-ca22d653759b Microsoft Authenticator App (Windows Phone)]
<br><br>
 
=={{header|C sharp|C#}}==
<syntaxhighlight lang="csharp">using System;
using System.Security.Cryptography;
 
namespace RosettaTOTP
{
public class TOTP_SHA1
{
private byte[] K;
public TOTP_SHA1()
{
GenerateKey();
}
public void GenerateKey()
{
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
/* Keys SHOULD be of the length of the HMAC output to facilitate
interoperability.*/
K = new byte[HMACSHA1.Create().HashSize / 8];
rng.GetBytes(K);
}
}
public int HOTP(UInt64 C, int digits = 6)
{
var hmac = HMACSHA1.Create();
hmac.Key = K;
hmac.ComputeHash(BitConverter.GetBytes(C));
return Truncate(hmac.Hash, digits);
}
public UInt64 CounterNow(int T1 = 30)
{
var secondsSinceEpoch = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
return (UInt64)Math.Floor(secondsSinceEpoch / T1);
}
private int DT(byte[] hmac_result)
{
int offset = hmac_result[19] & 0xf;
int bin_code = (hmac_result[offset] & 0x7f) << 24
| (hmac_result[offset + 1] & 0xff) << 16
| (hmac_result[offset + 2] & 0xff) << 8
| (hmac_result[offset + 3] & 0xff);
return bin_code;
}
 
private int Truncate(byte[] hmac_result, int digits)
{
var Snum = DT(hmac_result);
return Snum % (int)Math.Pow(10, digits);
}
}
 
 
class Program
{
static void Main(string[] args)
{
var totp = new TOTP_SHA1();
Console.WriteLine(totp.HOTP(totp.CounterNow()));
}
}
}
</syntaxhighlight>
 
=={{header|Caché ObjectScript}}==
 
<langsyntaxhighlight lang="cos">
Class Utils.Security [ Abstract ]
{
Line 108 ⟶ 182:
 
}
</syntaxhighlight>
</lang>
{{out|Example}}
<pre>
Line 126 ⟶ 200:
=={{header|Go}}==
A slightly [https://github.com/gwwfps/onetime/pull/1 fixed] version of a [https://github.com/gwwfps/onetime package by Zitao Zhang] (released under a [https://github.com/gwwfps/onetime/blob/master/LICENSE simplified BSD license]).
<langsyntaxhighlight lang="go">// Package onetime provides a library for one-time password generation,
// implementing the HOTP and TOTP algorithms as specified by IETF RFC-4226
// and RFC-6238.
Line 199 ⟶ 273:
p[0] &= 0x7f
return p
}</langsyntaxhighlight>
{{out|Example use}}
(in a format that gets put into the [https://godoc.org/github.com/dchapes/onetime generated documentation])
<syntaxhighlight lang="go">package onetime
 
import (
"crypto/sha256"
"fmt"
"time"
 
"github.com/gwwfps/onetime"
)
 
func Example_simple() {
// Simple 6-digit HOTP code:
var secret = []byte("SOME_SECRET")
var counter uint64 = 123456
var otp, _ = onetime.Simple(6)
var code = otp.HOTP(secret, counter)
fmt.Println(code)
// Output:
// 260040
}
 
func Example_authenticator() {
// Google authenticator style 8-digit TOTP code:
var secret = []byte("SOME_SECRET")
var otp, _ = onetime.Simple(8)
var code = otp.TOTP(secret)
fmt.Println(code)
}
 
func Example_custom() {
// 9-digit 5-second-step TOTP starting on midnight 2000-01-01 UTC, using SHA-256:
var secret = []byte("SOME_SECRET")
const ts = 5 * time.Second
var t = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
var otp = onetime.OneTimePassword{
Digit: 9, TimeStep: ts, BaseTime: t, Hash: sha256.New}
var code = otp.TOTP(secret)
fmt.Println(code)
}</syntaxhighlight>
 
=={{header|J}}==
<syntaxhighlight lang="j">DA =: '0123456789ABCDEF'
to_bytes =: 16 16#.(2,~2%~#)$ DA i. ]
upper =: 1&(3!:12)
xor =: 22 b.
and =: 17 b.
 
hmac_sha1 =: {{
sha1 =. _1&(128!:6)
 
b_size =. 512 % 8
 
pad_key =. b_size {.]
 
block_sized_key =. pad_key a.&i. x
o_key_pad =. block_sized_key xor b_size $ 16b5c
i_key_pad =. block_sized_key xor b_size $ 16b36
 
hashed =. sha1 (i_key_pad { a.), y
a. i. sha1 (o_key_pad { a.), hashed }}
 
totp =: {{
h =. x hmac_sha1&:(a. {~ to_bytes&]) y
offset =. 16bf and {: h
1000000|16b7fffffff and (4$256)#. 4 {. offset |. h }}
</syntaxhighlight>
 
{{out|Example use}}
<syntaxhighlight lang="j">time =: '0000000000000001' NB. 64-bit timestamp in hex format
secrete =: 'AB54A98CEB1F0AD2' NB. secrete key in hex format
time totp secrete NB. 758742
</syntaxhighlight>
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">using CodecBase
using SHA
 
function hmac(key, msg, hashfunction, blocksize=64)
key = hashfunction(key)
paddingneeded = blocksize - length(key)
if paddingneeded > 0
resize!(key, blocksize)
key[end-paddingneeded+1:end] .= 0
end
return hashfunction([key .⊻ 0x5c; hashfunction([key .⊻ 0x36; msg])])
end
 
#see also https://github.com/ylxdzsw/TOTP.jl
hmac(hashfunc, bs=64) = (key, msg, blocksize=bs) -> hmac(key, msg, hashfunc, blocksize)
 
function genotp(secret::String; tokenlength=6, hashfunc=hmac(sha1), tim=time()/30, outbase=10)
message = (([UInt8((Int(floor(tim)) >> 8i) & 0xff) for i in 7:-1:0]))
msghash = hashfunc(secret, message)
 
offset = msghash[length(msghash)] & 0x0f
binary = (Int(msghash[offset+1] & 0x7f) << 24) |
(Int(msghash[offset+2] & 0xff) << 16) |
(Int(msghash[offset+3] & 0xff) << 8) |
(msghash[offset+4] & 0xff)
otp = binary % outbase^tokenlength
string(otp, pad=tokenlength, base=outbase)
end
 
function genopt_fromb32(secret32; kwargs...)
secret = transcode(Base32Decoder(), secret32)
return genotp(secret; kwargs...)
end
 
for i in 1:7
println(genotp("JBSWY3DPEHPK3PXP"))
sleep(15)
end
</syntaxhighlight>{{out}}
<pre>
656601
537396
537396
656756
656756
514592
514592
</pre>
 
=={{header|Nim}}==
{{trans|Go}}
We borrowed the HMAC function from Julia solution, but didn’t apply a hash on the key at beginning. This way, we obtain the same result as Go for the “Simple” example.
 
Note that due to the interface of the hash function in module “std/sha1”, we have chosen to work with sequences of characters rather than sequences of bytes. The other way is of course possible, but maybe less convenient.
 
<syntaxhighlight lang="nim">import endians, math, sequtils, std/sha1, times
 
type
 
HashFunc = proc(msg: openArray[char]): seq[char]
 
OneTimePassword = object
digit: int # Length of code generated.
timeStep: Duration # Length of each time step for TOTP.
baseTime: Time # The start time for TOTP step calculation.
hash: HashFunc # Hash algorithm used with HMAC.
 
 
func sha1Hash(msg: openArray[char]): seq[char] =
mapIt(@(Sha1Digest(secureHash(msg))), char(it))
 
 
func `xor`(s: seq[char]; val: byte): seq[char] =
## Apply a XOR to the chars of a sequence.
s.mapIt(char(it.byte xor val))
 
 
func hmac(key, msg: openArray[char]; hashFunc: HashFunc; blockSize = 64): seq[char] =
## Compute a HMAC for gien key, message, hash function and block size.
var key = @key
let paddingNeeded = blockSize - key.len
if paddingNeeded > 0: key.setLen(blockSize)
result = hashFunc((key xor 0x5c) & hashFunc((key xor 0x36) & @msg))
 
 
func simple(digit: int): OneTimePassword =
## Return a new OneTimePassword with the specified HTOP code length,
## SHA-1 as the HMAC hash algorithm, the Unix epoch as the base time, and
## 30 seconds as the step length.
doAssert digit in 6..9, "HTOP code length must be in 6..9."
let step = initDuration(seconds = 30)
result = OneTimePassword(digit: digit, timeStep: step, baseTime: fromUnix(0), hash: sha1Hash)
 
 
func hmacSum(otp: OneTimePassword; secret: openArray[char]; count: uint64): seq[char] =
var count = count
var beCount: uint64
bigEndian64(beCount.addr, count.addr)
let msg = cast[array[8, char]](beCount)
result = hmac(secret, msg, otp.hash)
 
 
func dt(hs: seq[char]): seq[char] =
let offset = hs[^1].byte and 0xf
result = hs[offset..offset+3]
result[0] = char(result[0].byte and 0x7f)
 
 
func truncate(otp: OneTimePassword; hs: seq[char]): uint64 =
let sbits = dt(hs)
let snum = sbits[3].uint64 or sbits[2].uint64 shl 8 or
sbits[1].uint64 shl 16 or sbits[0].uint64 shl 24
result = snum mod 10u^otp.digit
 
 
func hotp(otp: OneTimePassword; secret: openArray[char]; count: uint64): uint64 =
let hs = otp.hmacSum(secret, count)
result = otp.truncate(hs)
 
 
func steps(otp: OneTimePassword; t: Time): uint64 =
let elapsed = t - otp.baseTime
result = uint64(elapsed.inSeconds div otp.timeStep.inSeconds)
 
 
proc totp(otp: OneTimePassword; secret: openArray[char]): uint64 =
## Return a TOTP code calculated with the current time and the given secret.
otp.hotp(secret, otp.steps(getTime()))
 
 
when isMainModule:
 
proc exampleSimple =
## Simple 6-digit HOTP code.
const secret = "SOME_SECRET"
var counter: uint64 = 123456
let otp = simple(6)
let code = otp.hotp(secret, counter)
echo code
# Output:
# 260040
 
proc exampleAuthenticator =
## Google authenticator style 8-digit TOTP code.
const secret = "SOME_SECRET"
let otp = simple(8)
let code = otp.totp(secret)
echo code
 
echo "Simple:"
exampleSimple()
 
echo "Google authenticator:"
exampleAuthenticator()</syntaxhighlight>
 
{{out}}
<pre>Simple:
260040
Google authenticator:
94364703</pre>
 
=={{header|Perl}}==
{{trans|Raku}}
<syntaxhighlight lang="perl"># 20200704 added Perl programming solution
 
use strict;
use warnings;
 
use Authen::OATH;
 
my $message = "show me the monKey"; # convert to base32 is optional
 
my $oath = Authen::OATH->new(); # default SHA1 and TI=30
 
for ( my $t = 2177452800 ; $t < 2177452919 ; $t += 13 ) {
print "At ", scalar gmtime $t, " : ", $oath->totp( $message, $t ), "\n" ;
}</syntaxhighlight>
{{out}}
<pre>
At Sat Jan 1 00:00:00 2039 : 950428
At Sat Jan 1 00:00:13 2039 : 950428
At Sat Jan 1 00:00:26 2039 : 950428
At Sat Jan 1 00:00:39 2039 : 361382
At Sat Jan 1 00:00:52 2039 : 361382
At Sat Jan 1 00:01:05 2039 : 022576
At Sat Jan 1 00:01:18 2039 : 022576
At Sat Jan 1 00:01:31 2039 : 341623
At Sat Jan 1 00:01:44 2039 : 341623
At Sat Jan 1 00:01:57 2039 : 341623
</pre>
 
=={{header|Phix}}==
{{trans|Perl}}
Note the byte ordering of hmac (etc) is suspect and may change, hence bmap below
<!--<syntaxhighlight lang="phix">-->
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1.0.1"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- sha1.e added</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">sha1</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">hmac</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">bmap</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12</span><span style="color: #0000FF;">,</span><span style="color: #000000;">11</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">16</span><span style="color: #0000FF;">,</span><span style="color: #000000;">15</span><span style="color: #0000FF;">,</span><span style="color: #000000;">14</span><span style="color: #0000FF;">,</span><span style="color: #000000;">13</span><span style="color: #0000FF;">,</span><span style="color: #000000;">20</span><span style="color: #0000FF;">,</span><span style="color: #000000;">19</span><span style="color: #0000FF;">,</span><span style="color: #000000;">18</span><span style="color: #0000FF;">,</span><span style="color: #000000;">17</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">dt</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">hmac_result</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">digits</span><span style="color: #0000FF;">=</span><span style="color: #000000;">6</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">a</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">offset</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">and_bits</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hmac_result</span><span style="color: #0000FF;">[</span><span style="color: #000000;">bmap</span><span style="color: #0000FF;">[$]],</span><span style="color: #000000;">#0F</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">4</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">a</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">*</span><span style="color: #000000;">#100</span><span style="color: #0000FF;">+</span><span style="color: #000000;">hmac_result</span><span style="color: #0000FF;">[</span><span style="color: #000000;">bmap</span><span style="color: #0000FF;">[</span><span style="color: #000000;">offset</span><span style="color: #0000FF;">+</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #000000;">a</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">and_bits</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">,</span><span style="color: #000000;">#7F</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">a</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">power</span><span style="color: #0000FF;">(</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span><span style="color: #000000;">digits</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">a</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">totp</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">t</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">ts</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">int_to_bytes</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">t</span><span style="color: #0000FF;">/</span><span style="color: #000000;">30</span><span style="color: #0000FF;">),</span><span style="color: #000000;">8</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">hmac_sha1</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ts</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">dt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">timedate</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #000000;">set_timedate_formats</span><span style="color: #0000FF;">({</span><span style="color: #008000;">"Mmm dth yyyy h:mm:ss"</span><span style="color: #0000FF;">})</span>
<span style="color: #000000;">timedate</span> <span style="color: #000000;">td0</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">parse_date_string</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Jan 1st 1970 00:00:00"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">message</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"show me the monKey"</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">to</span> <span style="color: #000000;">9</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">t</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">2177452800</span><span style="color: #0000FF;">+</span><span style="color: #000000;">i</span><span style="color: #0000FF;">*</span><span style="color: #000000;">13</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">format_timedate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">adjust_timedate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">td0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">t</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"At %s : %06d\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">totp</span><span style="color: #0000FF;">(</span><span style="color: #000000;">message</span><span style="color: #0000FF;">,</span><span style="color: #000000;">t</span><span style="color: #0000FF;">)})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
At Jan 1st 2039 0:00:00 : 950428
At Jan 1st 2039 0:00:13 : 950428
At Jan 1st 2039 0:00:26 : 950428
At Jan 1st 2039 0:00:39 : 361382
At Jan 1st 2039 0:00:52 : 361382
At Jan 1st 2039 0:01:05 : 022576
At Jan 1st 2039 0:01:18 : 022576
At Jan 1st 2039 0:01:31 : 341623
At Jan 1st 2039 0:01:44 : 341623
At Jan 1st 2039 0:01:57 : 341623
</pre>
 
=={{header|PicoLisp}}==
Using the <tt>sha1</tt> function defined at ''[[SHA-1#PicoLisp]]'':
<syntaxhighlight lang="picolisp">(load "sha1.l")
 
(de hmac ("Fun" Msg Key)
(let (Key (copy Key) Len (length Key))
(and
(> Len 64)
(setq Key ("Fun" Key)) )
(setq Key (need -64 Key 0))
("Fun"
(append
(mapcar x| (need 64 `(hex "5C")) Key)
("Fun" (append (mapcar x| (need 64 `(hex "36")) Key) Msg)) ) ) ) )
 
(de endian64 (N)
(make
(do 8
(yoke (& N 255))
(setq N (>> 8 N)) ) ) )
 
(de endian32 (L)
(apply
|
(mapcar >> (-24 -16 -8 0) L) ) )
 
(de truncate (Lst D)
(let L (nth Lst (inc (& (last Lst) `(hex "F"))))
(set L (& (car L) `(hex "7F")))
(% (endian32 (head 4 L)) (** 10 D)) ) )
 
(de hotp (K N D)
(default D 6)
(truncate
(hmac 'sha1 (endian64 N) K)
D ) )
 
(def 'totp hotp)
 
# RFC4226
(for
(I . N)
(755224 287082 359152 969429 338314
254676 287922 162583 399871 520489 )
(test
N
(hotp (mapcar char (chop "12345678901234567890")) (dec I)) ) )
 
# RFC6238
(for L
(quote
(1 . 94287082) (37037036 . 7081804) (37037037 . 14050471)
(41152263 . 89005924) (66666666 . 69279037) (666666666 . 65353130) )
(test
(cdr L)
(totp (mapcar char (chop "12345678901234567890")) (car L) 8) ) )</syntaxhighlight>
 
=={{header|Racket}}==
This includes BASE32 encoding, token based authentication and other such stuff.
 
<syntaxhighlight lang="racket">#lang racket
(require (only-in web-server/stuffers/hmac-sha1 HMAC-SHA1))
 
(define << arithmetic-shift) ; deep down, there lurks a C programmer in me
(define && bitwise-and)
 
;; These are the parameters available through RFC6238
(define T0 (make-parameter current-seconds)) ; produces unix epoch times. parameterised for testing
(define X (make-parameter 30))
(define H (make-parameter (lambda (k d) (HMAC-SHA1 k d))))
(define N (make-parameter 6))
 
;; http://tools.ietf.org/html/rfc4226#section-5.3
(define (HOTP sha1-bytes (Digit (N)))
(define (DT b)
(define offset (&& #b1111 (bytes-ref b 19)))
(define P/32 (subbytes b offset (+ offset 4)))
(+ (<< (bytes-ref P/32 3) 0) (<< (bytes-ref P/32 2) 8) (<< (bytes-ref P/32 1) 16)
(<< (&& #b01111111 (bytes-ref P/32 0)) 24)))
(define s-bits (DT sha1-bytes))
(modulo s-bits (expt 10 Digit)))
 
(define (Generate-HOTP K C (Digit (N)))
(HOTP ((H) K (integer->integer-bytes C 8 #t)) Digit))
 
;; http://tools.ietf.org/html/rfc6238
(define (T #:previous-timeframe (T- 0))
(- (quotient ((T0)) (X)) T-))
 
(define (TOTP K #:previous-timeframe (T- 0)) (Generate-HOTP K (T #:previous-timeframe T-) (N)))
 
;; RFC 3548
(define (pad-needed bits)
(modulo (- 5 bits) 5))
 
(define (5-bits->base32-char n)
(string-ref "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" n))
 
(define (base32-encode-block bs)
(define v (for/fold ((v 0)) ((b bs)) (+ (<< v 8) b)))
(define v-bits (* 8 (bytes-length bs)))
(define pad (pad-needed v-bits))
(define padded-bits (+ v-bits pad))
(define v-padded (<< v pad))
(for ((end-bit (in-range padded-bits 4 -5)))
(write-char (5-bits->base32-char (bitwise-bit-field v-padded (- end-bit 5) end-bit))))
(write-string (make-string (- 8 (/ padded-bits 5)) #\=)))
 
(define A-char (char->integer #\A))
(define Z-char (char->integer #\Z))
(define 2-char (char->integer #\2))
(define 7-char (char->integer #\7))
(define =-char (char->integer #\=))
(define (byte->5bit b)
(cond
[(<= A-char b Z-char) (- b A-char)]
[(<= 2-char b 7-char) (+ 26 (- b 2-char))]
[else #f]))
 
(define (base32-decode-block bs)
(for*/fold ((v 0) (b 0)) ((bt bs) (b5 (in-value (byte->5bit bt))) #:break (not b5))
(define v+ (+ (<< v 5) b5))
(define b+ (+ b 5))
(cond
[(< b+ 8) (values v+ b+)]
[else
(define start-bit (- b+ 8))
(write-byte (&& 255 (<< v+ (- start-bit))))
(values (bitwise-bit-field v+ 0 start-bit) start-bit)])))
 
(define (base32-encode) (for ((bs (in-port (curry read-bytes 5)))) (base32-encode-block bs)))
(define (base32-decode) (for ((bs (in-port (curry read-bytes 8)))) (base32-decode-block bs)))
 
(define (base32-encode-bytes b) (with-input-from-bytes b (λ () (with-output-to-bytes base32-encode))))
(define (base32-decode-bytes b) (with-input-from-bytes b (λ () (with-output-to-bytes base32-decode))))
 
(module+ main
(require racket/date)
;; my secret, as stuck on a postit note on my monitor
(define Tims-K #"Super Secret Password Key 88!")
(define ((pseudo-time-now (offset 0))) (+ 1413976828 offset))
(define totp #f)
(parameterize ((T0 (pseudo-time-now)))
(printf "I want authentication at: ~a ~s~%" ((T0)) (date->string (seconds->date ((T0))) #t))
(set! totp (TOTP (base32-encode-bytes Tims-K)))
(printf "My TOTP is: ~a~%" totp)
(printf "sent to authentication service...~%"))
;; as stored on authenticator
(define K/base32 (base32-encode-bytes Tims-K))
(printf "K/base32: ~a~%" K/base32)
(parameterize ((T0 (pseudo-time-now 1)))
(printf "1 second later... authentication service checks against: ~a~%" totp)
(define auth-totp (TOTP K/base32))
(printf "~a is the same? ~a~%" auth-totp (= totp auth-totp)))
(parameterize ((T0 (pseudo-time-now 3)))
(printf "but 3 seconds later... authentication service checks against: ~a~%" totp)
(define auth-totp (TOTP K/base32))
(printf "~a is the same? ~a~%" auth-totp (= totp auth-totp))
(printf "oh dear... fall back one time-frame...~%")
(define auth-totp-1 (TOTP K/base32 #:previous-timeframe 1))
(printf "~a is *that* the same? ~a~%" auth-totp-1 (= totp auth-totp-1))))
 
(module+ test
(require tests/eli-tester)
(test
;; From RFC4226 Page 7
(HOTP (bytes
#x1f #x86 #x98 #x69 #x0e #x02 #xca #x16 #x61 #x85
#x50 #xef #x7f #x19 #xda #x8e #x94 #x5b #x55 #x5a)
6)
=> 872921
(pad-needed 0) => 0
(pad-needed 2) => 3
(pad-needed 4) => 1
(pad-needed 6) => 4
(pad-needed 8) => 2
(pad-needed 10) => 0
(pad-needed 12) => 3
;; http://commons.apache.org/proper/commons-codec/xref-test/org/apache/commons/codec/binary/Base32Test.html
(base32-encode-bytes #"") => #""
(base32-encode-bytes #"f") => #"MY======"
(base32-encode-bytes #"fo") => #"MZXQ===="
(base32-encode-bytes #"foo") => #"MZXW6==="
(base32-encode-bytes #"foob") => #"MZXW6YQ="
(base32-encode-bytes #"fooba") => #"MZXW6YTB"
(base32-encode-bytes #"foobar") => #"MZXW6YTBOI======"
(base32-decode-bytes #"") => #""
(base32-decode-bytes #"MY======") => #"f"
(base32-decode-bytes #"MZXQ====") => #"fo"
(base32-decode-bytes #"MZXW6===") => #"foo"
(base32-decode-bytes #"MZXW6YQ=") => #"foob"
(base32-decode-bytes #"MZXW6YTB") => #"fooba"
(base32-decode-bytes #"MZXW6YTBOI======") => #"foobar"
(base32-encode-bytes #"Super Secret Password Key 88!")
=> #"KN2XAZLSEBJWKY3SMV2CAUDBONZXO33SMQQEWZLZEA4DQII="
))</syntaxhighlight>
 
{{out}}
<pre>23 tests passed
I want authentication at: 1413976828 "Wednesday, October 22nd, 2014 12:20:28pm"
My TOTP is: 742249
sent to authentication service...
K/base32: KN2XAZLSEBJWKY3SMV2CAUDBONZXO33SMQQEWZLZEA4DQII=
1 second later... authentication service checks against: 742249
742249 is the same? #t
but 3 seconds later... authentication service checks against: 742249
317129 is the same? #f
oh dear... fall back one time-frame...
742249 is *that* the same? #t</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
This is a minimal attempt that covers only the "Time-based" part of the requirement.
<syntaxhighlight lang="raku" line># Reference:
# https://github.com/retupmoca/P6-Digest-HMAC
 
use v6.d;
use Digest::HMAC;
use Digest::SHA;
 
sub totp (Str \secret, DateTime \counter, Int \T0=0, Int \T1=30 --> Str) {
my \key = ( counter - DateTime.new(T0) ).Int div T1;
return hmac-hex(key.Str, secret, &sha1).substr(0,6) # first 6 chars of sha1
}
 
my $message = "show me the monKey";
 
say "Deterministic output at ", DateTime.new(2177452800), " with fixed checks,";
loop (my $t = 2177452800 ; $t < 2177452900 ; $t+= 17 ) { # Y2038 safe
say totp $message, DateTime.new($t);
}
 
say "Current time output at ", DateTime.new(now), " with random checks,";
loop (my $n = 0 ; $n < 6 ; $n++, sleep (13..23).roll ) {
say totp $message, DateTime.new(now);
}
</syntaxhighlight>
{{out}}
<pre>Deterministic output at 2039-01-01T00:00:00Z with fixed checks,
34ca2a
acfa3f
950fc3
950fc3
a2d4ea
a2d4ea
Current time output at 2019-03-31T15:00:01.765312Z with random checks,
4e36de
d4e9f8
d4e9f8
077e2c
63bbb5
63bbb5</pre>
 
=={{header|Tcl}}==
 
This TOTP/HOTP module clocks in small by taking advantage of [https://core.tcl.tk/tcllib/doc/trunk/embedded/www/toc.html tcllib's] existing hashing and base32 modules.
 
<syntaxhighlight lang="tcl">
# rfc6238 contains examples of hotp with 8-digit modulus and sha1/sha256/sha512 hmac
#
# these require options handling, perhaps http://wiki.tcl.tk/38965
#
catch {namespace delete ::totp}
namespace eval ::totp {
package require sha1
oo::class create totp {
variable Secret
variable Interval
variable Window
constructor {secret {interval 30} {window 300}} {
if {![string is digit $interval]} {
set interval [expr {[clock scan $interval] - [clock scan now]}]
}
if {![string is digit $window]} {
set window [expr {[clock scan $window] - [clock scan now]}]
}
if {$window % $interval} {
throw {TOTP BADARGS} "$window is not a multiple of $interval"
}
set window [expr {$window / $interval}]
set Secret $secret
set Interval $interval
set Window $window
}
method totp {{when now}} {
if {![string is integer $when]} {
set when [clock scan $when]
}
set when [expr {$when / $Interval}]
set bytes [binary format W $when]
binary scan $bytes H* when
hotp $Secret $bytes
}
}
proc hotp {secret bytes {length 6}} {
set hmac [sha1::hmac -bin $secret $bytes]
set ofs [string index $hmac end]
binary scan $ofs c ofs
set ofs [expr {$ofs & 0xf}]
set chunk [string range $hmac $ofs $ofs+4]
binary scan $chunk I code
return [format %0${length}.${length}d [expr {($code & 0x7fffffff) % 10 ** $length}]]
 
}
namespace export *
}
namespace import ::totp::*
 
if 0 { ;# tests
if {[info commands assert] eq ""} {
proc assert {what} {
puts [uplevel 1 [list subst $what]]
}
}
totp::totp create t 12345678901234567890
assert {287082 eq [t totp 59]}
 
t destroy
package require base32
totp::totp create t [base32::decode AAAAAAAAAAAAAAAA]
proc google {when} {
list [t totp [expr {$when-30}]] [t totp $when] [t totp [expr {$when+30}]]
}
assert {{306281 553572 304383} eq [google 1400000000]}
}</syntaxhighlight>
 
=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-long}}
{{libheader|Wren-crypto}}
{{libheader|Wren-date}}
{{libheader|Wren-srandom}}
As Wren-cli currently has no way of determining the Unix time, this needs to be input as a command line parameter so we can track it from there.
<syntaxhighlight lang="wren">import "os" for Process
import "./long" for ULong
import "./crypto" for Bytes, Sha1, Sha256, Hmac
import "./date" for Date
import "./srandom" for SRandom
 
var StartTime = null // time program was started (as a Unix timestamp)
 
class OneTimePassword {
construct new(digit, timeStep, baseTime, hashClass) {
_digit = digit // length of code generated
_timeStep = timeStep // length of each time step for TOTP
_baseTime = baseTime // start time for TOTP step calculation (as a Unix timestamp)
_hashClass = hashClass // hash class to be used with HMAC
}
 
// Convenience version of above which uses defaults values for all except 'digit' parameter.
static simple(digit) { new(digit, 30, 0, Sha1) }
// Returns a HOTP code with the given secret and counter.
hotp(secret, count) { truncate_(hmacSum_(secret, count)) }
 
// Returns a TOTP code calculated with the current time and the given secret.
totp(secret) { hotp(secret, steps_(timeNow_)) }
 
/* private helper methods */
 
hmacSum_(secret, count) {
var msg = ULong.new(count).toBytes[-1..0] // big-endian
return Bytes.fromHexString(Hmac.digest(secret, msg, Sha1))
}
 
dt_(hs) {
var offset = hs[-1] & 0xf
var p = hs[offset...offset+4]
p[0] = p[0] & 0x7f
return p
}
 
truncate_(hs) {
var sbits = dt_(hs)
var snum = Bytes.toIntBE(sbits)
var pwr = 10.pow(_digit)
return snum % pwr
}
 
steps_(now) { ((now - _baseTime)/_timeStep).floor }
 
timeNow_ { (StartTime + System.clock).floor }
}
 
var args = Process.arguments
if (args.count != 1) {
System.print("Please pass the Unix timestamp when starting the program.")
return
}
StartTime = Num.fromString(args[0])
var secret = "SOME_SECRET".bytes.toList
 
// Simple 6-digit HOTP code.
var otp = OneTimePassword.simple(6)
System.print(otp.hotp(secret, 123456))
 
// Google authenticator style 8-digit TOTP code.
otp = OneTimePassword.simple(8)
System.print(otp.totp(secret))
 
// Custom 9 digit, 5 second step TOTP starting on midnight 2000-01-01 UTC, using Sha256.
var baseTime = Date.new(2000, 1, 1).unixTime
otp = OneTimePassword.new(9, 5, baseTime, Sha256)
System.print(otp.totp(secret))
 
// As above using a random, 32 byte, Base32 key
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" // for Base32
var randKey = List.filled(32, null)
for (i in 0..31) randKey[i] = alphabet[SRandom.int(32)]
System.print(otp.totp(randKey.join().bytes.toList))</syntaxhighlight>
 
{{out}}
Sample output:
<pre>
$ date '+%s'
1707913906
$ wren-cli Time-based_one-time_password_algorithm.wren 1707913906
260040
38559208
185760458
556237229
</pre>
 
=={{header|zkl}}==
Uses the MsgHash dll, which includes SHA-1, SHA-256 hashes and HMAC routines for SHA-* and MD5.
{{trans|Go}}
<syntaxhighlight lang="zkl">var [const] MsgHash = Import.lib("zklMsgHash");
// OneTimePassword stores the configuration values relevant to HOTP/TOTP calculations.
class OneTimePassword{
fcn init(Digit,TimeStep,BaseTime,HMAC){
var digit =Digit, // Length of code generated, # digits
timeStep=TimeStep, // Length of each time step for TOTP, in seconds
baseTime=BaseTime, // The start time for TOTP step calculation (seconds since Unix epoch)
hmac =HMAC; // Hash algorithm used with HMAC --> bytes
}
// hotp returns a HOTP code with the given secret and counter.
fcn hotp(secret,count){ // eg ("SOME_SECRET",123456)
hmac(secret,count.toBigEndian(8)) : // (key,msg), msg is count as 8 bytes
// --> 20 bytes (SHA1), eg (de,7c,9b,85,b8,b7,8a,a6,bc,8a,7a,36,f7,0a,90,70,1c,9d,b4,d9)
truncate(_)
}
fcn truncate(hs) // pick off bottom digit digits
{ dt(hs) % (10).pow(digit) }
fcn dt(hs){
hs[-1].bitAnd(0xf) : // bottom 4 bits (0-15) of LSB of hash to index
hs.toBigEndian(_,4) // 4 bytes of hash to 32 bit unsigned int
}
// Simple returns a new OneTimePassword with the specified HTOP code length,
// SHA-1 as the HMAC hash algorithm, the Unix epoch as the base time, and
// 30 seconds as the step length.
fcn simple(digit){ //--> OneTimePassword
if(digit<6)
throw(Exception.ValueError("minimum of 6 digits is required for a valid HTOP code"));
if(digit>9)
throw(Exception.ValueError("HTOP code cannot be longer than 9 digits"));
self(digit,30,0,MsgHash.extra.hmacSHA1.fp2(False))
}
// TOTP returns a TOTP code calculated with the current time and the given secret.
fcn totp(secret){ hotp(secret, steps(Time.Clock.time())) }
fcn steps(now) { (now - baseTime)/timeStep } // elapsed time chunked
} // OneTimePassword</syntaxhighlight>
Note: MsgHash hashes return a string by default, they can also return the hash as bytes. Ditto the HMAC routines, it is the third parameter. So, to create a hmac that returns bytes, use (eg) MsgHash.extra.hmacSHA1.fp2(False), this creates a partial application (closure) of the hmac using SHA-1 fixing the third parameter as False.
{{out|Example uses}}
<syntaxhighlight lang="zkl">fcn example_simple{
// Simple 6-digit HOTP code:
secret := "SOME_SECRET";
counter := 123456;
otp := OneTimePassword.simple(6);
code := otp.hotp(secret, counter);
println(code); //-->260040 (const)
}();
 
fcn example_authenticator{
// Google authenticator style 8-digit TOTP code:
secret := "SOME_SECRET";
otp := OneTimePassword.simple(8);
code :=otp.totp(secret);
println(code) //-->eg 44653788
}();
 
fcn example_custom{
// 9-digit 5-second-step TOTP starting on midnight 2000-01-01 UTC, using SHA-256:
secret := "SOME_SECRET";
ts := 5; // seconds
t := Time.Clock.mktime(2000,1,1, 0,0,0); //(Y,M,D, h,m,s)
otp := OneTimePassword(9,ts,t,MsgHash.extra.hmacSHA256.fp2(False));
code := otp.totp(secret);
println(code) //-->eg 707355416
}();</syntaxhighlight>
{{out|Example use}}
Showing how to sync with changes over time. A six digit OTP w/MD5 changing every 17 seconds. Latency can effect the result when totp is called at a time boundary, so a retry may be required.
<syntaxhighlight lang="zkl">fcn overTime{
secret,ts:= "SOME_SECRET",17;
otp := OneTimePassword(6,ts,Time.Clock.time(),MsgHash.extra.hmacMD5.fp2(False));
chg,s := 0,"";
while(1){
code,t := otp.totp(secret),Time.Clock.time() - otp.baseTime;
if(t/ts!=chg){ chg = t/ts; s=" (should change)"; }
println("%4d: %6d %s".fmt(t,code,s)); s = "";
Atomic.sleep(10);
}
}</syntaxhighlight>
{{out}}
<pre>
0: 53454
10: 53454
20: 2947 (should change)
30: 2947
40: 287972 (should change)
50: 287972
...
220: 510180
230: 207 (should change)
240: 380959 (should change)
250: 380959
</pre>
{{out|HMAC code}}
The MsgHash HMAC routines are pretty simple (the hash code is C), included here for completeness:
<syntaxhighlight lang="zkl">// https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
fcn hmac(key,msg,hashFcn,asString){
blkSz,H,Hx := 64,hashFcn.fp1(1,Data()), (asString and hashFcn or H);
kn:=key.len();
if(kn>blkSz) key=H(key).howza(0);
else key=Data().fill(0,blkSz-kn).write(key).howza(0);
opad:=key.pump(Data,(0x5c).bitXor);
ipad:=key.pump(Data,(0x36).bitXor);
 
Hx(opad + H(ipad + msg)) //-->String|Data
}
fcn hmacSHA1( key,msg,asString=True){ hmac(key,msg,MsgHash.SHA1, asString) }
fcn hmacSHA256(key,msg,asString=True){ hmac(key,msg,MsgHash.SHA256,asString) }
fcn hmacMD5( key,msg,asString=True){ hmac(key,msg,Utils.MD5.calc,asString) }</syntaxhighlight>
9,479

edits