banner
ZetoHkr

ZetoHkr

摸🐟从未停止,努力从未开始
github

TOTPアルゴリズムの簡単な解析

簡単な TOTP アルゴリズムの解析#

TOTP とは?#

TOTP、正式名称:時間ベースのワンタイムパスワード(Time-based one-time password)、このアルゴリズムはRFC 6238標準に従い、RFC 4226 標準に従った HOTP アルゴリズムを基に、メッセージの増分カウンターとして時間を付加しています。

TOTP はどこで使われるのか?#

最も直感的な例はMicrosoft AuthenticatorGoogle が提供する Authenticatorアプリで、QR コードをスキャンするか、キー(Secret)を入力することで対応するアカウントを追加できます。

どのように実現されるのか?#

まず、生成に使用する BASE32 文字列を持っていると仮定します:4FCDTLHR446DPFCKUA46UFIAYTQIDSZ2
この文字列をバイトコードに変換します:

34 46 43 44 54
4c 48 52 34 34
36 44 50 46 43
4b 55 41 34 36
55 46 49 41 59
54 51 49 44 53
5a 32

次に、BASE32 デコードを行うと、20 個の 16 進数シーケンスが得られます:

0xE1 0x44 0x39 0xAC 0xF1
0xE7 0x3C 0x37 0x94 0x4A
0xA0 0x39 0xEA 0x15 0x00
0xC4 0xE0 0x81 0xCB 0x3A

次に、今日の正午 12 時を時間要因として使用します:2023 年 06 月 26 日 12:00:00(タイムスタンプ:1687752000(秒))
以上の 2 つの要因があれば、計算を開始できます。

第二ステップでは、時間要因のタイムスタンプを 30 で割り、冗長性を持たせます(30 秒のウィンドウ期間):1687752000 / 30 = 56258400、次にこれを 16 進数に変換します:0x035A6F60

注意:ここで 8 バイトの長さに 0 を埋める必要があります:00 00 00 00 03 5A 6F 60

第三ステップでは、上記の秘密鍵を HMAC-SHA-1 の秘密鍵として使用し、得られた時間要因の結果をメッセージとして暗号化します。すると、次のようになります:

ee 91 c2 ea 63
f2 e4 9c 65 5d
c5 2e c4 9f b0
dd 45 f6 09 09

第四ステップでは、得られたハッシュの最後のバイトを 0x0F とビットごとに AND 演算し、オフセット(OFFSET)を得ます:0x09 & 0x0F = 0x09
オフセットを以下の式に代入します:

ハッシュ結果はHASH
オフセットはOFFSET = 0x09

(HASH[OFFSET] & 0x7F) << 24
= (0x5D & 0x7F) << 24
= 0x5D << 24
= 0x5D000000

(HASH[OFFSET + 1] & 0xFF) << 16
= (0xC5 & 0xFF) << 16
= 0xC5 << 16
= 0xC50000

(HASH[OFFSET + 2] & 0xFF) << 8
= (0x2E & 0xFF) << 8
= 0x2E << 8
= 0x2E00

HASH[OFFSET + 3] & 0xFF
= 0xC4 & 0xFF
= 0xC4

上記の結果をビットごとにOR演算し、後6桁(10進数)を取得します
(0x5D000000 | 0xC50000 | 0x2E00 | 0xC4) % 0xF4240 = 0x5DC52EC4 % 0xF4240
= 1573203652 % 1000000 = 203652

これで、203652 が生成された TOTP コードです。

コード(C 言語)#

注:OpenSSL をインクルードする必要があります

#include <stdio.h>
#include "string.h"
#include "time.h"
#include "math.h"
#include "openssl/hmac.h"

void generate_totp_code(const uint8_t * secret, uint32_t counter, int digits) {
    uint8_t counter_bytes[8];
    for (int i = 1; i <= 8; ++i) {
        counter_bytes[8 - i] = (u_int8_t) (counter % 256) & 0xff;
        counter /= 256;
    }
    unsigned char hmac_result[20];
    HMAC(EVP_sha1(), secret, 20, counter_bytes,
         sizeof(counter_bytes), hmac_result, NULL);
    int offset = hmac_result[19] & 0x0f;
    int truncated_hash = (hmac_result[offset] & 0x7f) << 24 |
                         (hmac_result[offset + 1] & 0xff) << 16 |
                         (hmac_result[offset + 2] & 0xff) << 8 |
                         (hmac_result[offset + 3] & 0xff);
    int otp = truncated_hash % 0xF4240;
    printf("TOTP: %0*d\n", digits, otp);
}

int main() {
    // 4FCDTLHR446DPFCKUA46UFIAYTQIDSZ2
    // to bytes
    // 34 46 43 44 54 4c 48 52 34 34 36 44 50 46 43 4b 55 41 34 36 55 46 49 41 59 54 51 49 44 53 5a 32
    // base32 decode
    // e1 44 39 ac f1 e7 3c 37 94 4a a0 39 ea 15 00 c4 e0 81 cb 3a
    uint8_t secret[] = {0xe1, 0x44, 0x39, 0xac,
                        0xf1, 0xe7, 0x3c, 0x37,
                        0x94, 0x4a, 0xa0, 0x39,
                        0xea, 0x15, 0x00, 0xc4,
                        0xe0, 0x81, 0xcb, 0x3a};
    uint32_t  counter = time(NULL) / 30;
    int digits = 6;
    generate_totp_code(secret, counter, digits);
    return 0;
}

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。