diff --git a/DGCAVerifier/Services/Enclave.swift b/DGCAVerifier/Services/Enclave.swift index de74587..bb5ad34 100644 --- a/DGCAVerifier/Services/Enclave.swift +++ b/DGCAVerifier/Services/Enclave.swift @@ -29,7 +29,8 @@ import Foundation struct Enclave { - static let algorithm = SecKeyAlgorithm.eciesEncryptionCofactorVariableIVX963SHA256AESGCM + static let encryptAlg = SecKeyAlgorithm.eciesEncryptionCofactorVariableIVX963SHA256AESGCM + static let signAlg = SecKeyAlgorithm.ecdsaSignatureMessageX962SHA512 static let symmetricKey = generateOrLoadKey(with: "symmetricKey") static func tag(for name: String) -> Data { @@ -105,21 +106,19 @@ struct Enclave { guard let publicKey = SecKeyCopyPublicKey(key) else { return (nil, "Cannot retrieve public key.") } - guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else { + guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, encryptAlg) else { return (nil, "Algorithm not supported.") } var error: Unmanaged? let cipherData = SecKeyCreateEncryptedData( publicKey, - algorithm, + encryptAlg, data as CFData, &error ) as Data? - guard cipherData != nil else { - return (nil, error?.takeRetainedValue().localizedDescription) - } + let err = error?.takeRetainedValue().localizedDescription error?.release() - return (cipherData, nil) + return (cipherData, err) } static func decrypt(data: Data, with key: SecKey, completion: @escaping (Data?, String?) -> Void) { @@ -130,20 +129,61 @@ struct Enclave { } static func syncDecrypt(data: Data, with key: SecKey) -> (Data?, String?) { - guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else { + guard SecKeyIsAlgorithmSupported(key, .decrypt, encryptAlg) else { return (nil, "Algorithm not supported.") } var error: Unmanaged? let clearData = SecKeyCreateDecryptedData( key, - algorithm, + encryptAlg, data as CFData, &error ) as Data? - guard clearData != nil else { - return (nil, error?.takeRetainedValue().localizedDescription) + let err = error?.takeRetainedValue().localizedDescription + error?.release() + return (clearData, err) + } + + static func verify(data: Data, signature: Data, with key: SecKey) -> (Bool, String?) { + guard let publicKey = SecKeyCopyPublicKey(key) else { + return (false, "Cannot retrieve public key.") } + guard SecKeyIsAlgorithmSupported(publicKey, .verify, signAlg) else { + return (false, "Algorithm not supported.") + } + var error: Unmanaged? + let isValid = SecKeyVerifySignature( + publicKey, + signAlg, + data as CFData, + signature as CFData, + &error + ) + let err = error?.takeRetainedValue().localizedDescription + error?.release() + return (isValid, err) + } + + static func sign(data: Data, with key: SecKey, completion: @escaping (Data?, String?) -> Void) { + DispatchQueue.global(qos: .userInitiated).async { + let (result, error) = syncSign(data: data, with: key) + completion(result, error) + } + } + + static func syncSign(data: Data, with key: SecKey) -> (Data?, String?) { + guard SecKeyIsAlgorithmSupported(key, .sign, signAlg) else { + return (nil, "Algorithm not supported.") + } + var error: Unmanaged? + let signature = SecKeyCreateSignature( + key, + signAlg, + data as CFData, + &error + ) as Data? + let err = error?.takeRetainedValue().localizedDescription error?.release() - return (clearData, nil) + return (signature, err) } } diff --git a/DGCAVerifier/Services/SecureStorage.swift b/DGCAVerifier/Services/SecureStorage.swift index cf4f6ca..b0f196f 100644 --- a/DGCAVerifier/Services/SecureStorage.swift +++ b/DGCAVerifier/Services/SecureStorage.swift @@ -28,26 +28,32 @@ import Foundation +struct SecureDB: Codable { + let data: Data + let signature: Data +} + 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) { + public static func load(completion: ((Bool) -> Void)? = nil) { if !FileManager.default.fileExists(atPath: path.path) { save() if FileManager.default.fileExists(atPath: path.path) { load(completion: completion) } else { - completion(false) + completion?(false) } return } guard - let data = read(), - let key = Enclave.symmetricKey + let (data, signature) = read(), + let key = Enclave.symmetricKey, + Enclave.verify(data: data, signature: signature, with: key).0 else { - completion(false) + completion?(false) return } Enclave.decrypt(data: data, with: key) { decrypted, err in @@ -56,40 +62,52 @@ struct SecureStorage { err == nil, let data = try? JSONDecoder().decode(LocalData.self, from: decrypted) else { - completion(false) + completion?(false) return } LocalData.sharedInstance = data - completion(true) + completion?(true) } } - public static func save() { + public static func save(completion: ((Bool) -> Void)? = nil) { guard let data = try? JSONEncoder().encode(LocalData.sharedInstance), let key = Enclave.symmetricKey, let encrypted = Enclave.encrypt(data: data, with: key).0 else { + completion?(false) return } - print("write", write(data: encrypted)) + Enclave.sign(data: data, with: key) { signature, err in + guard + let signature = signature, + err == nil + else { + completion?(false) + return + } + completion?(write(data: encrypted, signature: signature)) + } } - static func write(data: Data) -> Bool { - do { - try data.write(to: path) - } catch { + static func write(data: Data, signature: Data) -> Bool { + guard + let rawData = try? JSONEncoder().encode(SecureDB(data: data, signature: signature)), + let _ = try? rawData.write(to: path) + else { return false } return true } - static func read() -> Data? { - do { - let savedData = try Data(contentsOf: path) - return savedData - } catch { + static func read() -> (Data, Data)? { + guard + let rawData = try? Data(contentsOf: path), + let result = try? JSONDecoder().decode(SecureDB.self, from: rawData) + else { return nil } + return (result.data, result.signature) } }