二段階認証でよくあるAuthenticatorのPythonによる実装

概要

MicrosoftやGoogle、AmazonやTwitterのログインで使える6桁の二要素認証用のパスワードですが、ふと思い立ったのでAuthenticator(↓こんなやつ)をPythonでアルゴリズムだけ実装してみました。

仕組み

ワンタイムパスワードが30秒ごとに切り替わっていることからも推察できるように、アルゴリズムは時間ベースのワンタイムパスワード(TOTP)です。

なおシークレットと時間の処理にはHMACベースのワンタイムパスワード(HOTP)を使います。

すみませんが、日本語による詳細な説明は参考文献に任せます。

コード

  1. #!/usr/bin/env python3
  2. import base64
  3. import time
  4. import hmac
  5. from hashlib import sha1
  6. def truncate( hash_20, digit):
  7.     offset = hash_20[19] & 15
  8.     P = bytearray(4)
  9.     P[0] = hash_20[offset+0] & 0x7f
  10.     P[1] = hash_20[offset+1]
  11.     P[2] = hash_20[offset+2]
  12.     P[3] = hash_20[offset+3]
  13.     trunc = int.from_bytes(P, 'big') % 10 ** digit
  14.     zerofill = str(trunc).zfill(digit)
  15.     return zerofill
  16. #secret_b32 = 'YOUT_16_DIGITKEY'
  17. #secret_bin = base64.b32decode(secret_b32)
  18. secret_bin = '12345678901234567890'.encode()
  19. unixtime = int(time.time())
  20. #time_counter = int( unixtime / 30)
  21. time_counter = 0
  22. count = time_counter.to_bytes(8,byteorder='big')
  23. m = hmac.new(secret_bin, count, sha1)
  24. sig_20 = m.digest()
  25. totp = truncate( sig_20, 6)
  26. print("Secret:",secret_bin)
  27. print("Counter:",time_counter)
  28. print("HMAC-SHA-1:",m.hexdigest())
  29. print("TOTP:",totp)
  30. #!/usr/bin/env python3
  31. import base64
  32. import time
  33. import hmac
  34. from hashlib import sha1
  35. def truncate( hash_20, digit):
  36.     offset = hash_20[19] & 15
  37.     P = bytearray(4)
  38.     P[0] = hash_20[offset+0] & 0x7f
  39.     P[1] = hash_20[offset+1]
  40.     P[2] = hash_20[offset+2]
  41.     P[3] = hash_20[offset+3]
  42.     trunc = int.from_bytes(P, 'big') % 10 ** digit
  43.     zerofill = str(trunc).zfill(digit)
  44.     return zerofill
  45. #secret_b32 = 'YOUT_16_DIGITKEY'
  46. #secret_bin = base64.b32decode(secret_b32)
  47. secret_bin = '12345678901234567890'.encode()
  48. unixtime = int(time.time())
  49. #time_counter = int( unixtime / 30)
  50. time_counter = 0
  51. count = time_counter.to_bytes(8,byteorder='big')
  52. m = hmac.new(secret_bin, count, sha1)
  53. sig_20 = m.digest()
  54. totp = truncate( sig_20, 6)
  55. print("Secret:",secret_bin)
  56. print("Counter:",time_counter)
  57. print("HMAC-SHA-1:",m.hexdigest())
  58. print("TOTP:",totp)

上記のコードはシークレットが”12345678901234567890″かつタイムカウンタが0のときの結果を出力します。これはHOTPについて記述されているRFC4226のAppendix DのTest Valuesにあわせたものです。実行結果は以下。

  1. $ python3 poc.py
  2. Secret: b'12345678901234567890'
  3. Counter: 0
  4. HMAC-SHA-1: cc93cf18508d94934c64b65d8ba7667fb7cde4b0
  5. TOTP: 755224

各々の値で試したい場合はBASE32に記述されたシークレットはsecret_b32に、time_counterをunixtimeベースの処理にコメントを付け直してください。

数パターンしか試してないですが、たぶんこれで動きます。不具合あったら教えてください。

参考

スポンサーリンク
WPC-336 x 280
WPC-336 x 280

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

スポンサーリンク
WPC-336 x 280