Google Authenticator Soft Token Generator

Problem this snippet solves:

iRule to generate Google Authenticator soft tokens for use with the Google Authenticator iRule For Two-Factor Auth With LDAP. Add to pool-less HTTP virtual server and access from browser.

Code :

when HTTP_REQUEST {
  set account [URI::query [HTTP::uri] "account"]
  set domain [URI::query [HTTP::uri] "domain"]
  set secret [URI::query [HTTP::uri] "secret"]
  set qr_code [URI::query [HTTP::uri] "qr_code"]

  if { ([HTTP::path] starts_with "/ga_secret_generator") && ($account ne "") && ($domain ne "") } {
    if { [string length $secret] <= 10  } {
      set secret [b64encode [md5 [expr rand()]]]
    }

    set secret [string range $secret 0 9]

    array set b32_alphabet_inv {
       0 A  1 B  2 C  3 D
       4 E  5 F  6 G  7 H
       8 I  9 J 10 K 11 L
      12 M 13 N 14 O 15 P
      16 Q 17 R 18 S 19 T
      20 U 21 V 22 W 23 X
      24 Y 25 Z 26 2 27 3
      28 4 29 5 30 6 31 7
    }
     
    set secret_b32 ""
    set l [string length $secret]
    set n 0
    set j 0
     
    # encode loop is outlined in RFC 4648 (http://tools.ietf.org/html/rfc4648#page-8)
    for { set i 0 } { $i < $l } { incr i } {
      set n [expr $n << 8]
      set n [expr $n + [scan [string index $secret $i] %c]]
      set j [incr j 8]
     
      while { $j >= 5 } {
        set j [incr j -5]
        append secret_b32 $b32_alphabet_inv([expr ($n & (0x1F << $j)) >> $j])
      }
    }
     
    # pad final input group with zeros to form an integral number of 5-bit groups, then encode
    if { $j > 0 } { append secret_b32 $b32_alphabet_inv([expr $n << (5 - $j) & 0x1F]) }
     
    # if the final quantum is not an integral multiple of 40, append "=" padding
    set pad [expr 8 - [string length $secret_b32] % 8]
    if { ($pad > 0) && ($pad < 8) } { append secret_b32 [string repeat = $pad] }

    set ga_qr_code_link "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/"
    append ga_qr_code_link "$account@$domain"
    append ga_qr_code_link "%3Fsecret%3D"
    append ga_qr_code_link $secret_b32

    
    set ga_secret_http_resp {
  
    
} if { $qr_code eq "yes" } { append ga_secret_http_resp " \n" } append ga_secret_http_resp "

account: $account@$domain" append ga_secret_http_resp "key (secret): $secret_b32

\n
\n \n" HTTP::respond 200 content $ga_secret_http_resp } else { HTTP::respond 200 content {

Google Authenticator key (shared secret) generator

account: @
secret: *optional 10 character key (additional chars truncated), random secret used if blank
generate QR code? *a request will be made to Google to generate QR code
} } }
Published Mar 17, 2015
Version 1.0

Was this article helpful?

6 Comments

  • I don't see where you store the secret so it can be inserted in the datagroup or other wise used on the F5 after the secret is generated.
  • We were reviewing the code for this as we think:

     

    set secret [b64encode [md5 [expr rand()]]] set secret [string range $secret 0 9]

     

    Is suspect, as base 64 is 6 bits a character, and 10 lots of 6 bits is 60bits, when Google Authenticator uses 80 bits of entropy (16 x base 32 = 80 bits), and the RFC recommends 160 bits as minimum.

     

    Not sure this undermines Google Authenticator quite, but it must make brute force and similar attacks at least a million times more feasible.

     

  • We are looking at something like:

     

    set secret [ CRYPTO::keygen -alg random -len 256 ]

     

    set secret [ call base32encode $secret ]

     

    set secret [ string range $secret 0 31 ]

     

  • how can we generate a QR locally instead of using the google APIs ?