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] には 任意でalgorithmやdigitsが指定できる。
詳細はこちら。
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