// // OTPGenerator.swift // OTPKit // // Created by Shadowfacts on 8/21/21. // import Foundation import CryptoKit public struct OTPGenerator { private init() {} public static func generate(key: OTPKey, counter: Int64) -> String { let data = withUnsafeBytes(of: counter.bigEndian) { Data($0) } let mac = HMAC.authenticationCode(for: data, using: SymmetricKey(data: key.secret)) let uint32Code = mac.withUnsafeBytes { (ptr) -> UInt32 in let offset = Int(ptr.last! & 0x0F) let offsetPtr = ptr.baseAddress!.advanced(by: offset) return offsetPtr.assumingMemoryBound(to: UInt32.self).pointee.bigEndian & 0x7FFFFFFF } let truncated = uint32Code % UInt32(pow(10, Double(key.digits))) var s = truncated.description if s.count < key.digits { s = String(repeating: "0", count: key.digits - s.count) + s } return s } public static func generate(key: TOTPKey) -> TOTPCode { let now = Date() let counter = Int64(now.timeIntervalSince1970 / Double(key.period)) let start = Date(timeIntervalSince1970: Double(counter * Int64(key.period))) let code = generate(key: key, counter: counter) return TOTPCode(code: code, validFrom: start, validInterval: key.period) } }