TOTPを実装する
ここ数年で多くのサービスで採用されてきている二要素認証ですが、皆さん使っているでしょうか。 私は実は最近までは面倒であまり使っていなかったのですが、ようやく重い腰を上げてあちこち設定しました。 そのうち、近年特によく使われているのがTOTP(Time-Based One-Time Password)と呼ばれるアルゴリズムです。 TOTPアルゴリズムはRFC6238 で定義されたアルゴリズムで、サーバとクライアントが共有する秘密鍵および現在時刻から確認用のコードを生成するものです。 RFCやWikipedia を見てわかるよう、かなり簡素なアルゴリズムで、一つ一つ理解していけば比較的簡単に実装することができます。 Go言語のコードを実例に、サンプルコードを実装してみます。 HOTPとTOTP TOTPアルゴリズムとよく似たものに、HOTP(HMAC-Based One-Time Password)と呼ばれるアルゴリズムがあります。 これは、サーバとクライアントが共有する秘密鍵と、「何回目の認証か」から確認用のコードを生成するアルゴリズムです。 HOTPアルゴリズムは(勿論)アルゴリズムですから、ある計算手順であり、秘密鍵と認証回数を引数にとって認証用コードを返す関数として表すことができます。 この、認証回数という引数に対して、現在時刻を入力したものがTOTPです。 認証「回数」というくらいですから、値は正の整数値です。時刻を整数として入力するため、UnixTimeを使用します。 実際にはUnixTimeそのままで入力すると1秒ごとに認証用コードが変わってしまい実用できではありませんから、ある秒数を一周期として、現在が何周期目なのか、という値を入力します。 TOTPを実装する 扨、前置きはこれくらいにしてTOTPアルゴリズムを実装します。 次の式で表されます。 \begin{eqnarray*} TOTP(K, T_0, X) &=& HOTP(K, T(T_0, X)) \\ T(T_0, X) &=& \frac{(CurrentUnixTime - T_0)}{X} \end{eqnarray*} $K$は共有秘密鍵です。 $T_0$は数えはじめの時間で、通常はUnix epoch、すなわち0を使用します。 $X$は一周期の秒数で、規定値は30秒です。(実際、多くのサービスが30秒ベースです) プログラム実装は以下の様に書いてみます。 1 2 3 4 5 6 7 func TOTP(k string, t0, x int) int { return HOTP(k, T(t0, x)) } func T(t0, x int) int { return (time.Now().Unix - t0)/x } 簡単ですね。上記の内、定義されていないのはHOTP(K, T)だけとなりました。 HOTPを実装する TOTPのコードではHOTPアルゴリズム部分が実装されていませんので、ここを実装すれば実際に使用できるはずです。 HOTPアルゴリズムはRFC4226 で定義されているので、これを読みながら実装します。 RFCを読むと、HOTPは大きく次の3ステップで求められることがわかります。 共有秘密鍵と認証回数からHMAC-SHA1の値を求める 4byteの文字列を生成する HOTPの値を計算する 何が何やらですね。もう少し詳しく見ていきましょう。 1. HMAC-SHA1の値を求める HMACはメッセージ認証コードの一種で、ハッシュ関数を使用し、秘密鍵とメッセージから認証コードを生成します。 ここでは、その名の通り、ハッシュ関数としてSHA1を使用します。 また、メッセージとして認証回数を使用します。 ...