import Foundation import CommonCrypto enum CryptoAlgorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 var HMACAlgorithm: CCHmacAlgorithm { var result: Int = 0 switch self { case .MD5: result = kCCHmacAlgMD5 case .SHA1: result = kCCHmacAlgSHA1 case .SHA224: result = kCCHmacAlgSHA224 case .SHA256: result = kCCHmacAlgSHA256 case .SHA384: result = kCCHmacAlgSHA384 case .SHA512: result = kCCHmacAlgSHA512 } return CCHmacAlgorithm(result) } var digestLength: Int { var result: Int32 = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } } extension String { func hmac(algorithm: CryptoAlgorithm, key: String) -> String { let str = self.cString(using: String.Encoding.utf8) let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8)) let digestLen = algorithm.digestLength let result = UnsafeMutablePointer.allocate(capacity: digestLen) let keyStr = key.cString(using: String.Encoding.utf8) let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8)) CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result) let array = Array(UnsafeBufferPointer(start: result, count: digestLen)) let encodedArray = Base64FS.encode(data: array) let string = String(data: Data(encodedArray), encoding: .utf8)!.trimmingCharacters(in: CharacterSet(charactersIn: "=")) result.deallocate() return string } private func stringFromResult(result: UnsafeMutablePointer, length: Int) -> String { let hash = NSMutableString() for i in 0.. where T: JwtPayload { private var jwt: String public let payload: T public var expired: Bool { return TimeInterval(self.payload.exp) < Date().timeIntervalSince1970 } public init?(string: String) { self.jwt = string let parts = string.split(separator: ".") if parts.count == 3 { var payloadStr = String(parts[1]) if (payloadStr.count % 4 != 0) { payloadStr += String(repeating: "=", count: (4 - payloadStr.count % 4)) } if let json = Data(base64Encoded: payloadStr, options: [.ignoreUnknownCharacters]) { do { self.payload = try JSONDecoder().decode(T.self, from: json) } catch { print(error) return nil } } else { return nil } } else { return nil } } public static func generate(for plateNumber: String) throws -> String { let header = #"{ "typ": "JWT", "alg": "HS256" }"# let bodyDict: [String: Any] = [ "iat": Date().timeIntervalSince1970, "exp": Date(timeIntervalSinceNow: 24*60*60).timeIntervalSince1970, "plateNumber": plateNumber ] let bodyData = try JSONSerialization.data(withJSONObject: bodyDict, options: []) guard let body = String(data: bodyData, encoding: .utf8) else { throw CocoaError.error("Error", reason: "Failed to generate JWT for sharing report via link") } let twoParts = Base64FS.encodeString(str: header).trimmingCharacters(in: CharacterSet(charactersIn: "=")) + "." + Base64FS.encodeString(str: body).trimmingCharacters(in: CharacterSet(charactersIn: "=")) let signature = twoParts.hmac(algorithm: .SHA256, key: Constants.reportLinkTokenSecret) return twoParts + "." + signature } }