🔐二要素認証
Twitterなどの二要素認証では、二要素認証用のアプリで生成した認証コードを使う。
認証コードをどのように生成しているのか、Twitterとは無関係のアプリで生成した認証コードが利用できるのか、クラウドで同期できるのかが疑問だったので実装した。
🔍調査
なにから調べていいか分からなかったので、普段使っているAuthyのサイトを見ていたら、二要素認証の種類について説明しているページがあった。
これによると「TOTP(Time-based One-Time Password)」という方式らしい。
あの6桁の数字はワンタイムパスワードなのかと気づいた。
📕 TOTPアルゴリズム
TOTPというキーワードをもとに検索すると、RFCまで辿りつく。
TOTPは以下の式で計算される。
時刻T(Unix秒)におけるは以下の式であたえられる。 はワンタイムパスワードが有効な秒数であり、通常は30である。
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で定義される。
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コードにも同様の内容が含まれている。
🛠サンプルアプリ
アルゴリズムだけ調べても正しいか不安なので、Swiftでコマンドラインツールを作った。
Mastodonの二要素認証に表示されるSecret keyを入力して、二要素認証に使えることを確認した。
Swift Package Managerを使ったが、プレインテキストでプロジェクトの構成を管理できてよかった。
xcodeprojファイル管理するの大変だからフォルダ構成とかから生成できないかなーと思ってたけど、swift package generate-xcodeprojでできたわ
— mzp (@mzp) 2018年11月3日
👀参考にしたサイト
- Swift Package Manager V4 + Utilityで作る!コマンドラインツール – Eureka Engineering – Medium
- Swift Package Manager (SwiftPM) で作るコマンドラインツール - Qiita
- ワンタイムパスワードジェネレータを作った - ぶていのログでぶログ
💕感想
普段、使ってるものの仕組みをちゃんと調べるのはたのしい。
Authyとかの二段階認証、どういう仕組みになってんの? なんで独立したアプリがTwitterのコードを生成できんの? というのが不思議だったので、RFCとか読みながら作った。 pic.twitter.com/eDGWQd3bWP
— mzp (@mzp) 2018年11月3日
要は事前に共有した秘密鍵と時刻から、認証用のコードを生成してるんだ
— mzp (@mzp) 2018年11月3日