diff --git a/Sources/ReplicantSwift/Polish/PolishClientModel.swift b/Sources/ReplicantSwift/Polish/PolishClientModel.swift index 611ed91..b9d43ff 100644 --- a/Sources/ReplicantSwift/Polish/PolishClientModel.swift +++ b/Sources/ReplicantSwift/Polish/PolishClientModel.swift @@ -25,7 +25,7 @@ public class PolishClientModel return nil } - guard let newKeyPair = controller.generateKeyPair() + guard let newKeyPair = controller.generateKeyPair(withAttributes: controller.generateKeyAttributesDictionary()) else { return nil diff --git a/Sources/ReplicantSwift/Polish/PolishController.swift b/Sources/ReplicantSwift/Polish/PolishController.swift index 3eee5bd..fee9a7d 100644 --- a/Sources/ReplicantSwift/Polish/PolishController.swift +++ b/Sources/ReplicantSwift/Polish/PolishController.swift @@ -14,6 +14,7 @@ public struct PolishController { let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM let polishTag = "org.operatorfoundation.replicant.polish".data(using: .utf8)! + let polishServerTag = "org.operatorfoundation.replicant.polishServer".data(using: .utf8)! /// Decode data to get public key. This only decodes key data that is NOT padded. public func decodeKey(fromData publicKeyData: Data) -> SecKey? @@ -34,11 +35,11 @@ public struct PolishController return decodedPublicKey } - public func generatePrivateKey() -> SecKey? + public func generatePrivateKey(withAttributes attributes: CFDictionary) -> SecKey? { // Generate private key var error: Unmanaged? - let attributes = self.generateKeyAttributesDictionary() + //let attributes = self.generateKeyAttributesDictionary() guard let privateKey = SecKeyCreateRandomKey(attributes, &error) else @@ -62,9 +63,9 @@ public struct PolishController return publicKey } - func generateKeyPair() -> (privateKey: SecKey, publicKey: SecKey)? + func generateKeyPair(withAttributes attributes: CFDictionary) -> (privateKey: SecKey, publicKey: SecKey)? { - guard let privateKey = generatePrivateKey() + guard let privateKey = generatePrivateKey(withAttributes: attributes) else { return nil @@ -79,6 +80,45 @@ public struct PolishController return (privateKey, publicKey) } + func fetchOrCreateServerKeyPair() ->(privateKey: SecKey, publicKey: SecKey)? + { + + // Do we already have a key? + var item: CFTypeRef? + let status = SecItemCopyMatching(generateServerKeySearchQuery(), &item) + + switch status + { + case errSecItemNotFound: + // We don't? + // Let's create some and return those + return generateKeyPair(withAttributes: generateServerKeyAttributesDictionary()) + case errSecSuccess: + // Return the pair + guard let itemDictionary = item as? [String: Any] + else + { + print("Received unexpected key data.") + return nil + } + + // FIXME: Casting issues here + let privateKey = itemDictionary[kSecValueRef as String] as! SecKey + guard let publicKey = generatePublicKey(usingPrivateKey: privateKey) + else + { + print("Unable to generate a public key uding the provided private key.") + return nil + } + + return (privateKey, publicKey) + + default: + print("\nReceived an unexpacted response while checking for an existing server key: \(status)\n") + return nil + } + } + func deleteKeys() { //Remove client keys from secure enclave @@ -110,6 +150,41 @@ public struct PolishController return attributes as CFDictionary } + func generateServerKeyAttributesDictionary() -> CFDictionary + { + let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, + kSecAttrAccessibleAlwaysThisDeviceOnly, + .privateKeyUsage, + nil)! + + let privateKeyAttributes: [String: Any] = [ + kSecAttrIsPermanent as String: true, + kSecAttrApplicationTag as String: polishServerTag, + //kSecAttrAccessControl as String: access + ] + + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom, + kSecAttrKeySizeInBits as String: 256, + //kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, + kSecPrivateKeyAttrs as String: privateKeyAttributes + ] + + return attributes as CFDictionary + } + + func generateServerKeySearchQuery() -> CFDictionary + { + let query: [String: Any] = [kSecClass as String: kSecClassKey, + kSecAttrApplicationTag as String: polishServerTag, + kSecMatchLimit as String: kSecMatchLimitOne, + kSecReturnRef as String: true, + kSecReturnAttributes as String: false, + kSecReturnData as String: false] + + return query as CFDictionary + } + /// This is the format needed to send the key to the server. public func generateAndEncryptPaddedKeyData(fromKey key: SecKey, withChunkSize chunkSize: Int, usingServerKey serverKey: SecKey) -> Data? { diff --git a/Sources/ReplicantSwift/Polish/PolishServerModel.swift b/Sources/ReplicantSwift/Polish/PolishServerModel.swift index dcc765a..a768e30 100644 --- a/Sources/ReplicantSwift/Polish/PolishServerModel.swift +++ b/Sources/ReplicantSwift/Polish/PolishServerModel.swift @@ -18,8 +18,7 @@ public class PolishServerModel public init?(clientPublicKeyData: Data? = nil) { - controller.deleteKeys() - + // The client's key if we get one on init if let publicKeyData = clientPublicKeyData { if let cPublicKey = controller.decodeKey(fromData: publicKeyData) @@ -28,7 +27,9 @@ public class PolishServerModel } } - guard let newKeyPair = controller.generateKeyPair() + // Check to see if the server already has a keypair first + // If not, create one. + guard let newKeyPair = controller.fetchOrCreateServerKeyPair() else { return nil