Skip to content

Commit

Permalink
Add signing of encrypted local storage.
Browse files Browse the repository at this point in the history
  • Loading branch information
yspreen committed Apr 26, 2021
1 parent 535aade commit 9978ff5
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 30 deletions.
64 changes: 52 additions & 12 deletions DGCAVerifier/Services/Enclave.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<CFError>?
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) {
Expand All @@ -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<CFError>?
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<CFError>?
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<CFError>?
let signature = SecKeyCreateSignature(
key,
signAlg,
data as CFData,
&error
) as Data?
let err = error?.takeRetainedValue().localizedDescription
error?.release()
return (clearData, nil)
return (signature, err)
}
}
54 changes: 36 additions & 18 deletions DGCAVerifier/Services/SecureStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}

0 comments on commit 9978ff5

Please sign in to comment.