ワンタイムパスワード(OTP)の生成


はじめに

D言語でワンタイムパスワードの生成処理を実装してみました。

ワンタイムパスワード(OTP)の種類

ワンタイムパスワードには、生成回数をベースにしたHOTPと生成時刻をベースにしたTOTPの2種類があります。
それぞれ、RFCで定義されています。

HOTP : An HMAC-Based One-Time Password Algorithm
RFC4226
TOTP : Time-Based One-Time Password Algorithm
RFC6238

関連用語

HOTPTOTPを生成するためにHMACSHA-1を利用します。

HMAC : Hash-based Message Authentication Code
RFC 2104

SHA-1 : Secure Hash Algorithm - 1
RFC 3174

これらは、D言語の標準ライブラリを使って実装できます。
D言語での HMAC-SHA-1

ソースコード

ワンタイムパスワードの生成処理の実装例です。
いつものごとく、入力パラメータargsのチェックは省略しています。

otp.d
/+ dub.sdl:
    name "otp"
    dependency "base32" version="~>0.1.0"
+/
// dub build --build=release --single otp.d
import std.conv;
import std.datetime;
import std.digest.hmac;
import std.digest.sha : SHA1;
import std.stdio;

import base32;

int getOTP(string key, ulong input)
{// one time password
    ubyte[] data = new ubyte[8];
    for ( int i = 0; i < 8; i++ ){
        data[7 - i] = input & 0xff;
        input >>= 8;
    }
    auto hmac = HMAC!SHA1(Base32.decode(key));
    hmac.put(data);
    ubyte[20] digest = hmac.finish();
    int offset = digest[19] & 0x0f;
    int number = (digest[offset    ] & 0x7f) << 24 |
                 (digest[offset + 1] & 0xff) << 16 |
                 (digest[offset + 2] & 0xff) <<  8 |
                 (digest[offset + 3] & 0xff);
    return ( number % 1_000_000 );
}

void main(string[] args)
{
    int number;
    if ( args.length < 3 ){
        // TOTP
        ulong input = Clock.currTime.toUnixTime() / 30;
        number = getOTP(args[1], input);
    } else {
        // HOTP
        number = getOTP(args[1], args[2].to!ulong);
    }
    writefln("%06d", number);
}

コンパイル

コマンドプロンプト
D:\Dev> dub build --build=release --single otp.d
Performing "release" build using D:\Dev\dmd2\windows\bin\dmd.exe for x86_64.
base32 0.1.0: target for configuration "library" is up to date.
otp ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.

試してみる

WinAuthを使って、実行結果を比較検証しました。
WinAuthSecret Keyは、Base32でエンコードされています。
D言語のbase32パッケージを使えば、エンコードやデコードすることができます。

Base32の実装例

base32sample.d
/+ dub.sdl:
    name "base32sample"
    dependency "base32" version="~>0.1.0"
+/
// dub build --build=release --single base32sample.d
import std.stdio;
import std.string;

import base32;

void main(string[] args)
{
    writeln(Base32.encode("12345678901234567890"));
    writeln(cast(string)Base32.decode("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"));
}
コマンドプロンプト
D:\Dev> dub build --build=release --single base32sample.d
Performing "release" build using D:\Dev\dmd2\windows\bin\dmd.exe for x86_64.
base32 0.1.0: target for configuration "library" is up to date.
base32sample ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.

D:\Dev> base32sample
GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
12345678901234567890

Secret Keyをセットしました。

HOTPの生成

Secret Keyと生成回数をパラメータとして渡すとHOTPを生成します。
生成回数が増えるごとに、異なるパスワードが生成されます。

コマンドプロンプト
D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ 1
287082

D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ 2
359152


TOTPの生成

Secret Keyのみをパラメータとして渡すとTOTPを生成します。
パスワードは、30秒ごとに新たに生成されます。

コマンドプロンプト
D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
088383

D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
428063


参考情報

ワンタイムパスワード生成アルゴリズムについて学ぶ
二段階認証を実装するまでに調べたこと
WinAuth