ワンタイムパスワードの作り方

# はじめに
ワンタイムパスワード(OTP)の生成に使われている標準的なアルゴリズムを紹介します。
また、D言語でワンタイムパスワードの生成処理を実装しました。

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

HOTP : An HMAC-Based One-Time Password Algorithm（HMACをもとにしたワンタイムパスワード）
[RFC4226](https://tools.ietf.org/html/rfc4226)

TOTP : Time-Based One-Time Password Algorithm（時刻をもとにしたワンタイムパスワード）
[RFC6238](https://tools.ietf.org/html/rfc6238)

# メッセージ認証コード、ハッシュ処理
`HOTP`や`TOTP`を生成するために`HMAC`、`SHA-1`を利用します。

[HMAC](https://ja.wikipedia.org/wiki/HMAC) : Hash-based Message Authentication Code（ハッシュをもとにしたメッセージ認証コード）
[RFC 2104](https://tools.ietf.org/html/rfc2104)

[SHA-1](https://ja.wikipedia.org/wiki/SHA-1) : Secure Hash Algorithm - 1（セキュアなハッシュアルゴリズム）
[RFC 3174](https://tools.ietf.org/html/rfc3174)

これらは、D言語の標準ライブラリを使って実装できます。
[D言語での HMAC-SHA-1](https://dlang.org/phobos/std_digest_hmac.html)

# ソースコード
ワンタイムパスワードの生成処理の実装例です。
ソースコードの読みやすさを優先して、入力パラメータ`args`のチェック処理等は省略しています。

```d: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);
}
```

# コンパイル
```text:コマンドプロンプト
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](https://winauth.github.io/winauth/download.html)を使って、実行結果を比較検証しました。
`WinAuth`の`Secret Key`は、[Base32](https://en.wikipedia.org/wiki/Base32)でエンコードされています。
D言語のbase32パッケージを使えば、エンコードやデコードすることができます。

## Base32の実装例
```d: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"));
}
```

```text:コマンドプロンプト
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`をセットしました。
![totp1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675901/0fa91eaa-64d7-299c-34de-658b34ae0370.png)

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

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

D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ 2
359152
```
![hotp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675901/b736a4a1-482e-2724-4ade-ca5b97f81170.png)
![hotp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675901/edd00de0-12d9-b655-40be-1bce55989384.png)

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

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

D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
428063
```
![totp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675901/4641b85f-02ac-0c76-869b-1a99340287d4.png)
![totp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675901/c9bd627c-772c-1659-ee4f-77a1e4c13c3c.png)

# 参考情報
[ワンタイムパスワード生成アルゴリズムについて学ぶ](https://uyorum.github.io/blog/hotp/)
[二段階認証を実装するまでに調べたこと](https://qiita.com/goombeer/items/70da733124b8e860f0fd)
[WinAuth](https://winauth.github.io/winauth/download.html)