LoginSignup
7
6

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-04-24

はじめに

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

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

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

HOTP : An HMAC-Based One-Time Password Algorithm(HMACをもとにしたワンタイムパスワード)
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をセットしました。
totp1.png

HOTPの生成

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

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

D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ 2
359152

hotp.png
hotp2.png

TOTPの生成

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

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

D:\Dev> otp GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
428063

totp.png
totp2.png

参考情報

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

7
6
0

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
7
6