みずぴー日記

人間の再起動ボタンはハワイのビーチにある

🔐二要素認証

Twitterなどの二要素認証では、二要素認証用のアプリで生成した認証コードを使う。

認証コードをどのように生成しているのか、Twitterとは無関係のアプリで生成した認証コードが利用できるのか、クラウドで同期できるのかが疑問だったので実装した。

🔍調査

なにから調べていいか分からなかったので、普段使っているAuthyのサイトを見ていたら、二要素認証の種類について説明しているページがあった。

f:id:mzp:20181103234308p:plain Authy features

これによると「TOTP(Time-based One-Time Password)」という方式らしい。

あの6桁の数字はワンタイムパスワードなのかと気づいた。

📕 TOTPアルゴリズム

TOTPというキーワードをもとに検索すると、RFCまで辿りつく。

TOTPは以下の式で計算される。

 \displaystyle
\mathrm{TOTP}(K) = \mathrm{HOTP}(K, C_T)

時刻T(Unix秒)におけるC_Tは以下の式であたえられる。 T_xワンタイムパスワードが有効な秒数であり、通常は30である。

 \displaystyle
C_T = \lfloor \frac{T}{T_x} \rfloor

Swiftで書くとこのようになる。

public final class TOTPGenerator {
    private let hotp: HOTPGenerator

    public init(secret : [UInt8]) {
        self.hotp = HOTPGenerator(secret: secret)
    }

    public func generate(at date : Date, format: OTPFormat) -> String? {
        let count = UInt64(date.timeIntervalSince1970) / 30
        return hotp.generate(at: count, format: format)
    }
}

📒HOTPアルゴリズム

TOTPはHOTP(HMAC-based One-time Password)のパラメータを時刻にしたものなので、メインのアルゴリズムはHOTPで定義される。

 \displaystyle
\mathrm{HOTP}(K, C) = \mathrm{Truncate}(\mathrm{HMAC-SHA1}(K, C))

Truncateはハッシュ値を指定の桁までに切りつめる操作だが、数式で書くとつらいのでSwiftで書くとこのようになる。 ほぼこのままの式がRFCにのっている。

func truncate(hash : [UInt8]) -> Int {
    let offset = Int(hash[19] & 0x0f)
    return Int(hash[offset] & 0x7f) << 24
                | Int(hash[offset + 1]) << 16
                | Int(hash[offset + 2]) << 8
                | Int(hash[offset + 3])
}

これを使いうとHOTPは以下のコードで生成できる。

import class CryptoSwift.HMAC

class HOTPGenerator {
    private let hmac: HMAC

    init(secret : [UInt8]) {
        self.hmac = HMAC(key: secret, variant: .sha1)
    }

    func generate(at count : UInt64, format: OTPFormat) -> String? {
        guard let hash = try? hmac.authenticate(Array(uint64: count)) else {
            return nil
        }
        // 先頭6文字のみを使う
        return String(String(truncate(code)).suffix(6))
    }
}

🤝秘密鍵の交換

TOTP/HOTPでは秘密鍵を共有する必要がある。

秘密鍵はBase32でエンコードされてやりとりされる。 二要素認証の設定画面に表示される「7nx ofic ...」がそれにあたる。QRコードにも同様の内容が含まれている。

f:id:mzp:20181104001820p:plain

🛠サンプルアプリ

アルゴリズムだけ調べても正しいか不安なので、Swiftでコマンドラインツールを作った。

http://github.com/mzp/totp

Mastodonの二要素認証に表示されるSecret keyを入力して、二要素認証に使えることを確認した。

f:id:mzp:20181103233300p:plain

Swift Package Managerを使ったが、プレインテキストでプロジェクトの構成を管理できてよかった。

👀参考にしたサイト

💕感想

普段、使ってるものの仕組みをちゃんと調べるのはたのしい。