二段階認証(TOTP)メモ

  • 23
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

TOTP

二段階認証のアプリは色々あるらしいが(IIJ Smartkey、Authy、Google Authenticator等)、どうやら TOTPというワンタイムパスワードの規格があり、それに沿っている模様。

RFC6238 Time-based One-time Password Algorithm (TOTP)の仕組みのメモ
http://qiita.com/shrkw/items/426a7f1a59f42e0bd523

そのうち使うかもしれないのでメモ。

QRコード(URIフォーマット)

多くの二段階認証アプリはQRコードで登録するが、実際のURIは以下のようになっている。

otpauth://totp/Example:hoge@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example

  • otpauth://totp/[LABEL]?[Parameters] という形式。
  • [LABEL] は 発行者(Example) と アカウント名を:で区切ったものを記述。
  • secretは任意のパラメータをBase32でエンコードしたもの。
  • issuer[LABEL]と同一の発行者を指定。IIJ SmartKeyの場合、必須らしい。
  • [Parameters] には 任意でalgorithmdigitsが指定できる。

詳細はこちら。
google-authenticator Key Uri Format
https://github.com/google/google-authenticator/wiki/Key-Uri-Format
IIJ SmartKey URIフォーマット
https://www1.auth.iij.jp/smartkey/uri_v1.html

ワンタイムパスコード生成(PHP)

パスコードを実際にどのように生成しているか、の処理。
あらかじめ与えられたsecretとUNIX Time で6桁の数字が確定する。
以下を参考に(ほぼ転載気味)。
Google TOTP Two-factor Authentication for PHP
https://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/

<?php
// Base32でバイナリ変換
function base32_decode($b32) {
  $lut = array("A" => 0,       "B" => 1,
               "C" => 2,       "D" => 3,
               "E" => 4,       "F" => 5,
               "G" => 6,       "H" => 7,
               "I" => 8,       "J" => 9,
               "K" => 10,      "L" => 11,
               "M" => 12,      "N" => 13,
               "O" => 14,      "P" => 15,
               "Q" => 16,      "R" => 17,
               "S" => 18,      "T" => 19,
               "U" => 20,      "V" => 21,
               "W" => 22,      "X" => 23,
               "Y" => 24,      "Z" => 25,
               "2" => 26,      "3" => 27,
               "4" => 28,      "5" => 29,
               "6" => 30,      "7" => 31
  );

  $b32    = strtoupper($b32);
  $l      = strlen($b32);
  $n      = 0;
  $j      = 0;
  $binary = "";

  for ($i = 0; $i < $l; $i++) {

       $n = $n << 5;
       $n = $n + $lut[$b32[$i]];       
       $j = $j + 5;

       if ($j >= 8) {
           $j = $j - 8;
           $binary .= chr(($n & (0xFF << $j)) >> $j);
       }
  }

  return $binary;
}

// google-authenticator のサンプルキー
$sample_secret = 'JBSWY3DPEHPK3PXP';
$binary_key = base32_decode($sample_secret);

// 30秒単位
$timestamp = floor(microtime(true) / 30);

$binary_timestamp = pack('N*',0) . pack('N*', $timestamp);

$hash = hash_hmac ('sha1', $binary_timestamp, $binary_key, true);

// RFC4226 6桁に切り詰める
$offset = ord($hash[19]) & 0xf;
$OTP = (
   ((ord($hash[$offset+0]) & 0x7f) << 24 ) |
    ((ord($hash[$offset+1]) & 0xff) << 16 ) |
    ((ord($hash[$offset+2]) & 0xff) << 8 ) |
    (ord($hash[$offset+3]) & 0xff)
   ) % pow(10, 6);

// 数字を出力
printf("%06d",$OTP);

参考

Googleの2段階認証で使われているOTPの仕様が気になった
http://d.hatena.ne.jp/ritou/20111225/1324822654
Base32
https://ja.wikipedia.org/wiki/%E4%B8%89%E5%8D%81%E4%BA%8C%E9%80%B2%E6%B3%95