LoginSignup
49
62

More than 5 years have passed since last update.

二段階認証(TOTP)メモ

Last updated at Posted at 2015-10-26

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

49
62
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
62