再現してたら1週間かかったのは秘密。
なんかすごく時間がないので、詳しいことは来週加筆します。
本当はもっとマトモなエクステンションにしたかったんです… しかし時間が… ウグッ
挙動確認するときは認証アプリに手動でアカウント登録してくだしあ
bridging-header.h
#import <CommonCrypto/CommonHMAC.h>
main.swift
import Cocoa
extension NSDate {
static func google2FA(key: String) -> String {
func base32decode(k: String) -> [UInt8] {
func decode(c: CChar) -> UInt8? {
switch c {
case 50...55:
return UInt8(c - 24)
case 65...90:
return UInt8(c - 65)
default:
return nil
}
}
func convert(raw: ArraySlice<CChar>, result: [UInt8]) -> [UInt8] {
if let first = raw.first.flatMap(decode) {
return convert(raw.dropFirst(), result: result + [first])
} else {
return result
}
}
let nums = k.uppercaseString.cStringUsingEncoding(NSASCIIStringEncoding)
.map(ArraySlice.init).map { convert($0, result: []) } ?? []
var bs = [UInt8]()
var buffer = 0
var length = 0
for c in nums {
buffer = buffer << 5 | numericCast(c)
length += 5
if (length >= 8) {
bs.append(numericCast(buffer >> (length - 8)))
buffer -= (numericCast(bs.last!) << (length - 8))
length -= 8
}
}
return bs
}
let secret = base32decode(key)
var time = CFSwapInt64HostToBig(UInt64(NSDate().timeIntervalSince1970) / 30)
var hash = [UInt8](count: numericCast(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(numericCast(kCCHmacAlgSHA1), secret, secret.count, &time, sizeof(UInt64), &hash)
let offset = Int(hash.last!) & 0xf
let truncated = hash[offset...offset + 3]
var t = 0
for n in truncated {
t = (t << 8) | numericCast(n)
}
return String(format: "%06d", (t & (0x7fffffff) ) % 1000000)
}
}
// 使い方
print(NSDate.google2FA("aaaaaaaaaaaaaaaa"))