I wanted to try the generation of the QRcode, which I did withe the following code:
ad_proc -private mfa::generate_secret {} {
Generate the secret Base32
} {
package require base32
set raw [ns_crypto::randombytes 20]
return [base32::encode $raw]
}
# create a new secret
set secret [mfa::generate_secret]
# create URI otpauth
set issuer "OpenACS"
set account_name "Claudio@[ad_conn peeraddr]"
set uri "otpauth://totp/${issuer}:${account_name}?secret=$secret&issuer=$issuer&digits=6"
# creates QR code PNG
set png_path /vagrant/alter-dev/www/resources/qr.png
exec qrencode -o $png_path $uri
Following is the code used to generate a TOTP based on the secret
ad_proc mfa::totp {
-secret:required
{-for_time ""}
{-time_step 30}
{-digits 6}
} {
# generates current TOTP (6 digits, 30 seconds step)
} {
package require base32
if {$for_time eq ""} {
set for_time [clock seconds]
}
set counter [expr {int($for_time / $time_step)}]
set key [base32::decode $secret]
return [ns_totp -key $key \
-time $for_time \
-interval $time_step \
-digits $digits \
-digest sha1]
}
and finally the code to compare the generated TOTP with that generated by Google Authenticator
ad_proc mfa::verify {
-secret:required
-code:required
{-time_step 30}
{-skew 1}
{-digits 6}
} {
Compares the secret with the code entered by the user (skew ±1 for clock tolerance)
} {
set code [string trim $code]
for {set i -$skew} {$i <= $skew} {incr i} {
set t [expr {[clock seconds] + $i * $time_step}]
set totp_code [mfa::totp \
-secret $secret \
-for_time $t \
-time_step $time_step \
-digits $digits]
if {$totp_code eq $code} {
return 1
}
}
return 0
}
Claudio