From 5012129c9811617b25ca810275ca6a7815294a37 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Sun, 25 Apr 2021 22:49:23 +0200 Subject: [PATCH] Add symmetric encryption. --- DGCAVerifier.xcodeproj/project.pbxproj | 8 ++ DGCAVerifier/Models/LocalData.swift | 47 +++++++++ DGCAVerifier/Services/GatewayConnection.swift | 7 +- DGCAVerifier/Services/SecureStorage.swift | 95 +++++++++++++++++++ DGCAVerifier/SupportingFiles/Info.plist | 2 + DGCAVerifier/ViewControllers/Home.swift | 9 +- DGCAVerifier/ViewControllers/Scan.swift | 6 +- 7 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 DGCAVerifier/Models/LocalData.swift create mode 100644 DGCAVerifier/Services/SecureStorage.swift diff --git a/DGCAVerifier.xcodeproj/project.pbxproj b/DGCAVerifier.xcodeproj/project.pbxproj index 7adaf54..8a2b616 100644 --- a/DGCAVerifier.xcodeproj/project.pbxproj +++ b/DGCAVerifier.xcodeproj/project.pbxproj @@ -17,11 +17,13 @@ CE157F8D262E24F900FE4821 /* HCert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE157F8C262E24F900FE4821 /* HCert.swift */; }; CE157F9B262E2A9F00FE4821 /* SwiftCBOR.CBOR.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE157F9A262E2A9F00FE4821 /* SwiftCBOR.CBOR.swift */; }; CE1BDF99262A4CD600766F97 /* X509.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1BDF98262A4CD600766F97 /* X509.swift */; }; + CE1D1EF6263597A2004C8919 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1D1EF5263597A2004C8919 /* LocalData.swift */; }; CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC93B2628A7820079FB78 /* ASN1.swift */; }; CE3CC9442628C2130079FB78 /* CBOR.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC9432628C2130079FB78 /* CBOR.swift */; }; CE44798D26304D8F009A836B /* JSONSchema in Frameworks */ = {isa = PBXBuildFile; productRef = CE44798C26304D8F009A836B /* JSONSchema */; }; CE44799226306C86009A836B /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE44799126306C86009A836B /* String.swift */; }; CE44799726306C9B009A836B /* Data+Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE44799626306C9B009A836B /* Data+Base45.swift */; }; + CE582DC12635AE5F008F35D7 /* SecureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE582DC02635AE5F008F35D7 /* SecureStorage.swift */; }; CE7DE7FA2625EF18007E6694 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CE7DE7F92625EF18007E6694 /* SwiftCBOR */; }; CE8912E526321AA500CB92AF /* KID.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE8912E426321AA500CB92AF /* KID.swift */; }; CE8912EA26321DAA00CB92AF /* SHA256.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE8912E926321DAA00CB92AF /* SHA256.swift */; }; @@ -77,10 +79,12 @@ CE157F8C262E24F900FE4821 /* HCert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HCert.swift; sourceTree = ""; }; CE157F9A262E2A9F00FE4821 /* SwiftCBOR.CBOR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCBOR.CBOR.swift; sourceTree = ""; }; CE1BDF98262A4CD600766F97 /* X509.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509.swift; sourceTree = ""; }; + CE1D1EF5263597A2004C8919 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = ""; }; CE3CC93B2628A7820079FB78 /* ASN1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1.swift; sourceTree = ""; }; CE3CC9432628C2130079FB78 /* CBOR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBOR.swift; sourceTree = ""; }; CE44799126306C86009A836B /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; CE44799626306C9B009A836B /* Data+Base45.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Base45.swift"; sourceTree = ""; }; + CE582DC02635AE5F008F35D7 /* SecureStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureStorage.swift; sourceTree = ""; }; CE8912E426321AA500CB92AF /* KID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KID.swift; sourceTree = ""; }; CE8912E926321DAA00CB92AF /* SHA256.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHA256.swift; sourceTree = ""; }; CE8912F42634C60E00CB92AF /* GatewayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GatewayConnection.swift; sourceTree = ""; }; @@ -169,6 +173,7 @@ CE8912E926321DAA00CB92AF /* SHA256.swift */, CE8912F42634C60E00CB92AF /* GatewayConnection.swift */, CE8912FF263570CF00CB92AF /* Enclave.swift */, + CE582DC02635AE5F008F35D7 /* SecureStorage.swift */, ); path = Services; sourceTree = ""; @@ -223,6 +228,7 @@ isa = PBXGroup; children = ( CE157F8C262E24F900FE4821 /* HCert.swift */, + CE1D1EF5263597A2004C8919 /* LocalData.swift */, ); path = Models; sourceTree = ""; @@ -444,10 +450,12 @@ CE3CC9442628C2130079FB78 /* CBOR.swift in Sources */, CE44799226306C86009A836B /* String.swift in Sources */, CE8912F52634C60E00CB92AF /* GatewayConnection.swift in Sources */, + CE582DC12635AE5F008F35D7 /* SecureStorage.swift in Sources */, CE44799726306C9B009A836B /* Data+Base45.swift in Sources */, CE13CF0F262DD0D80070C80E /* FullFloatingPanelLayout.swift in Sources */, CEA1556B262F784E0024B7AC /* SelfSizedTableView.swift in Sources */, CE157F81262E1F7A00FE4821 /* Date.swift in Sources */, + CE1D1EF6263597A2004C8919 /* LocalData.swift in Sources */, CE1BDF99262A4CD600766F97 /* X509.swift in Sources */, CEA1555D262F63B30024B7AC /* EuDgcSchema.swift in Sources */, CEA6D6F0261F8D2700715333 /* Scan.swift in Sources */, diff --git a/DGCAVerifier/Models/LocalData.swift b/DGCAVerifier/Models/LocalData.swift new file mode 100644 index 0000000..8a13564 --- /dev/null +++ b/DGCAVerifier/Models/LocalData.swift @@ -0,0 +1,47 @@ +// +/*- + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-ios + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ +// +// LocalData.swift +// DGCAVerifier +// +// Created by Yannick Spreen on 4/25/21. +// + + +import Foundation + +struct LocalData: Codable { + static var sharedInstance = LocalData() + + var encodedPublicKeys = [String: String]() + var resumeToken: String? + + static func add(encodedPublicKey: String) { + let kid = KID.from(encodedPublicKey) + let kidStr = KID.string(from: kid) + + sharedInstance.encodedPublicKeys[kidStr] = encodedPublicKey + } + + static func set(resumeToken: String) { + sharedInstance.resumeToken = resumeToken + } +} diff --git a/DGCAVerifier/Services/GatewayConnection.swift b/DGCAVerifier/Services/GatewayConnection.swift index 6bee524..7b4b079 100644 --- a/DGCAVerifier/Services/GatewayConnection.swift +++ b/DGCAVerifier/Services/GatewayConnection.swift @@ -40,9 +40,9 @@ struct GatewayConnection { case let .success(result) = $0.result, let response = result, let responseStr = String(data: response, encoding: .utf8), - let pubKey = X509.pubKey(from: responseStr), let headers = $0.response?.headers, - let responseKid = headers["x-kid"] + let responseKid = headers["x-kid"], + let newResumeToken = headers["x-resume-token"] else { return } @@ -51,7 +51,8 @@ struct GatewayConnection { if kidStr != responseKid { return } - print(pubKey) + LocalData.add(encodedPublicKey: responseStr) + LocalData.set(resumeToken: newResumeToken) } } } diff --git a/DGCAVerifier/Services/SecureStorage.swift b/DGCAVerifier/Services/SecureStorage.swift new file mode 100644 index 0000000..cf4f6ca --- /dev/null +++ b/DGCAVerifier/Services/SecureStorage.swift @@ -0,0 +1,95 @@ +// +/*- + * ---license-start + * eu-digital-green-certificates / dgca-verifier-app-ios + * --- + * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ +// +// SecureStorage.swift +// DGCAVerifier +// +// Created by Yannick Spreen on 4/25/21. +// + + +import Foundation + +struct SecureStorage { + static let documents: URL! = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + static let path: URL! = URL(string: documents.absoluteString + "secure.db") + + public static func load(completion: @escaping (Bool) -> Void) { + if !FileManager.default.fileExists(atPath: path.path) { + save() + if FileManager.default.fileExists(atPath: path.path) { + load(completion: completion) + } else { + completion(false) + } + return + } + + guard + let data = read(), + let key = Enclave.symmetricKey + else { + completion(false) + return + } + Enclave.decrypt(data: data, with: key) { decrypted, err in + guard + let decrypted = decrypted, + err == nil, + let data = try? JSONDecoder().decode(LocalData.self, from: decrypted) + else { + completion(false) + return + } + LocalData.sharedInstance = data + completion(true) + } + } + + public static func save() { + guard + let data = try? JSONEncoder().encode(LocalData.sharedInstance), + let key = Enclave.symmetricKey, + let encrypted = Enclave.encrypt(data: data, with: key).0 + else { + return + } + print("write", write(data: encrypted)) + } + + static func write(data: Data) -> Bool { + do { + try data.write(to: path) + } catch { + return false + } + return true + } + + static func read() -> Data? { + do { + let savedData = try Data(contentsOf: path) + return savedData + } catch { + return nil + } + } +} diff --git a/DGCAVerifier/SupportingFiles/Info.plist b/DGCAVerifier/SupportingFiles/Info.plist index e24a474..4b7a065 100644 --- a/DGCAVerifier/SupportingFiles/Info.plist +++ b/DGCAVerifier/SupportingFiles/Info.plist @@ -2,6 +2,8 @@ + NSFaceIDUsageDescription + Unlock sensitive data. UIUserInterfaceStyle Light NSCameraUsageDescription diff --git a/DGCAVerifier/ViewControllers/Home.swift b/DGCAVerifier/ViewControllers/Home.swift index 3986a3f..f0ff2d4 100644 --- a/DGCAVerifier/ViewControllers/Home.swift +++ b/DGCAVerifier/ViewControllers/Home.swift @@ -33,6 +33,13 @@ class HomeVC: UIViewController { override func viewDidLoad() { super.viewDidLoad() - performSegue(withIdentifier: "scanner", sender: self) + SecureStorage.load { [weak self] success in + guard success else { + return + } + DispatchQueue.main.async { + self?.performSegue(withIdentifier: "scanner", sender: self) + } + } } } diff --git a/DGCAVerifier/ViewControllers/Scan.swift b/DGCAVerifier/ViewControllers/Scan.swift index f9b243a..7a1219b 100644 --- a/DGCAVerifier/ViewControllers/Scan.swift +++ b/DGCAVerifier/ViewControllers/Scan.swift @@ -47,9 +47,9 @@ class ScanVC: UIViewController { override func viewDidLoad() { super.viewDidLoad() - checkPermissions() - setupCameraLiveView() - return; +// checkPermissions() +// setupCameraLiveView() +// return; DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.observationHandler(payloadS: nil) // let reason = "Log in to your account"