131 lines
4.4 KiB
Swift
131 lines
4.4 KiB
Swift
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<UInt8>.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<CUnsignedChar>, length: Int) -> String {
|
|
let hash = NSMutableString()
|
|
for i in 0..<length {
|
|
hash.appendFormat("%02x", result[i])
|
|
}
|
|
return String(hash).lowercased()
|
|
}
|
|
}
|
|
|
|
public protocol JwtPayload: Codable {
|
|
var exp: Int { get }
|
|
}
|
|
|
|
extension JwtPayload {
|
|
public var exp: Int { 0 }
|
|
}
|
|
|
|
public struct EmptyPayload: JwtPayload { }
|
|
|
|
public struct FirebasePayload: JwtPayload {
|
|
public var email: String
|
|
public var name: String
|
|
}
|
|
|
|
public struct NumberPayload: JwtPayload {
|
|
public var plateNumber: String
|
|
}
|
|
|
|
public class JWT<T> 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
|
|
}
|
|
}
|