diff --git a/DGCAVerifier/Models/LocalData.swift b/DGCAVerifier/Models/LocalData.swift index cef538d..459e450 100644 --- a/DGCAVerifier/Models/LocalData.swift +++ b/DGCAVerifier/Models/LocalData.swift @@ -33,17 +33,30 @@ struct LocalData: Codable { var encodedPublicKeys = [String: String]() var resumeToken: String? + var lastFetch_: Date? + var lastFetch: Date { + get { + lastFetch_ ?? .init(timeIntervalSince1970: 0) + } + set(v) { + lastFetch_ = v + } + } - static func add(encodedPublicKey: String) { + mutating func add(encodedPublicKey: String) { let kid = KID.from(encodedPublicKey) let kidStr = KID.string(from: kid) - sharedInstance.encodedPublicKeys[kidStr] = encodedPublicKey + encodedPublicKeys[kidStr] = encodedPublicKey } static func set(resumeToken: String) { sharedInstance.resumeToken = resumeToken } + public func save() { + Self.storage.save(self) + } + static let storage = SecureStorage() } diff --git a/DGCAVerifier/Services/GatewayConnection.swift b/DGCAVerifier/Services/GatewayConnection.swift index 7b4b079..a05caa9 100644 --- a/DGCAVerifier/Services/GatewayConnection.swift +++ b/DGCAVerifier/Services/GatewayConnection.swift @@ -28,14 +28,25 @@ import Foundation import Alamofire +import SwiftyJSON struct GatewayConnection { static let serverURI = "https://dgca-verifier-service.cfapps.eu10.hana.ondemand.com/" static let updateEndpoint = "signercertificateUpdate" static let statusEndpoint = "signercertificateStatus" - public static func fetchCert(resume resumeToken: String? = nil) { - AF.request(serverURI + updateEndpoint).response { + public static func certUpdate(resume resumeToken: String? = nil, completion: ((String?, String?) -> Void)?) { + var headers = [String: String]() + if let token = resumeToken { + headers["x-resume-token"] = token + } + AF.request(serverURI + updateEndpoint, method: .get, parameters: nil, encoding: URLEncoding(), headers: .init(headers), interceptor: nil, requestModifier: nil).response { + if + let status = $0.response?.statusCode, + status == 204 { + completion?(nil, nil) + return + } guard case let .success(result) = $0.result, let response = result, @@ -51,8 +62,67 @@ struct GatewayConnection { if kidStr != responseKid { return } - LocalData.add(encodedPublicKey: responseStr) - LocalData.set(resumeToken: newResumeToken) + completion?(responseStr, newResumeToken) + } + } + public static func certStatus(resume resumeToken: String? = nil, completion: (([String]) -> Void)?) { + AF.request(serverURI + statusEndpoint).response { + guard + case let .success(result) = $0.result, + let response = result, + let responseStr = String(data: response, encoding: .utf8), + let json = JSON(parseJSON: responseStr).array + else { + return + } + let kids = json.compactMap { $0.string } + completion?(kids) + } + } + + static var timer: Timer? + + public static func initialize() { + timer?.invalidate() + timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { + _ in trigger() + } + timer?.tolerance = 1.0 + trigger() + } + + static func trigger() { + guard LocalData.sharedInstance.lastFetch.timeIntervalSinceNow < -24 * 60 * 60 else { + return + } + update() + } + + static func update() { + certUpdate(resume: LocalData.sharedInstance.resumeToken) { encodedCert, token in + LocalData.sharedInstance.lastFetch = Date() + guard let encodedCert = encodedCert else { + status() + return + } + LocalData.sharedInstance.add(encodedPublicKey: encodedCert) + LocalData.sharedInstance.resumeToken = token + update() + } + } + + static func status() { + certStatus { validKids in + var invalid = [String]() + for key in LocalData.sharedInstance.encodedPublicKeys.keys { + if !validKids.contains(key) { + invalid.append(key) + } + } + for key in invalid { + LocalData.sharedInstance.encodedPublicKeys.removeValue(forKey: key) + } + LocalData.sharedInstance.save() } } } diff --git a/DGCAVerifier/Services/SecureStorage.swift b/DGCAVerifier/Services/SecureStorage.swift index 64316c0..54f8cc0 100644 --- a/DGCAVerifier/Services/SecureStorage.swift +++ b/DGCAVerifier/Services/SecureStorage.swift @@ -75,6 +75,7 @@ struct SecureStorage { completion?(nil) return } +// print(path, "read:", String(data: decrypted, encoding: .utf8)!) completion?(data) } } @@ -88,6 +89,7 @@ struct SecureStorage { completion?(false) return } +// print(path, "write:", String(data: data, encoding: .utf8)!) Enclave.sign(data: encrypted, with: key) { signature, err in guard let signature = signature, @@ -96,7 +98,8 @@ struct SecureStorage { completion?(false) return } - completion?(write(data: encrypted, signature: signature)) + let success = write(data: encrypted, signature: signature) + completion?(success) } } @@ -112,11 +115,14 @@ struct SecureStorage { func read() -> (Data, Data)? { guard - let rawData = try? Data(contentsOf: path), + let rawData = try? Data(contentsOf: path, options: [.uncached]), let result = try? JSONDecoder().decode(SecureDB.self, from: rawData) else { return nil } +// print("read") +// print(String(data: rawData, encoding: .utf8)!) +// print(result) return (result.data, result.signature) } } diff --git a/DGCAVerifier/ViewControllers/Home.swift b/DGCAVerifier/ViewControllers/Home.swift index db7cc5c..8865980 100644 --- a/DGCAVerifier/ViewControllers/Home.swift +++ b/DGCAVerifier/ViewControllers/Home.swift @@ -30,13 +30,15 @@ import Foundation import UIKit class HomeVC: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + GatewayConnection.timer?.invalidate() LocalData.storage.loadOverride(fallback: LocalData.sharedInstance) { [weak self] success in guard let result = success else { return } +// print("loaded:", result) LocalData.sharedInstance = result DispatchQueue.main.async { self?.performSegue(withIdentifier: "scanner", sender: self) diff --git a/DGCAVerifier/ViewControllers/Scan.swift b/DGCAVerifier/ViewControllers/Scan.swift index af85ba7..d9af9f1 100644 --- a/DGCAVerifier/ViewControllers/Scan.swift +++ b/DGCAVerifier/ViewControllers/Scan.swift @@ -55,6 +55,7 @@ class ScanVC: UIViewController { checkPermissions() setupCameraLiveView() #endif + GatewayConnection.initialize() } override func viewWillDisappear(_ animated: Bool) {