2 * Copyright (c) 2019 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 import SecurityFoundation
27 let OT_ESCROW_SIGNING_HKDF_SIZE = 56
28 let OT_ESCROW_ENCRYPTION_HKDF_SIZE = 56
29 let OT_ESCROW_SYMMETRIC_HKDF_SIZE = 32
31 enum EscrowKeyType: Int {
32 case kOTEscrowKeySigning = 1
33 case kOTEscrowKeyEncryption = 2
34 case kOTEscrowKeySymmetric = 3
37 class EscrowKeys: NSObject {
38 var encryptionKey: _SFECKeyPair
39 var signingKey: _SFECKeyPair
40 var symmetricKey: _SFAESKey
43 var bottleSalt: String
45 init (secret: Data, bottleSalt: String) throws {
47 self.bottleSalt = bottleSalt
49 let encryptionKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret, bottleSalt: bottleSalt)
50 self.encryptionKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: encryptionKeyData))
52 let signingKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: secret, bottleSalt: bottleSalt)
53 self.signingKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: signingKeyData))
55 let symmetricKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret, bottleSalt: bottleSalt)
56 let specifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
57 self.symmetricKey = try _SFAESKey.init(data: symmetricKeyData, specifier: specifier)
59 let escrowSigningPubKeyHash = try EscrowKeys.hashEscrowedSigningPublicKey(keyData: self.signingKey.publicKey().spki())
60 _ = try EscrowKeys.storeEscrowedSigningKeyPair(keyData: self.signingKey.keyData, label: escrowSigningPubKeyHash)
61 _ = try EscrowKeys.storeEscrowedEncryptionKeyPair(keyData: self.encryptionKey.keyData, label: escrowSigningPubKeyHash)
62 _ = try EscrowKeys.storeEscrowedSymmetricKey(keyData: self.symmetricKey.keyData, label: escrowSigningPubKeyHash)
65 class func generateEscrowKey(keyType: EscrowKeyType, masterSecret: Data, bottleSalt: String) throws -> (Data) {
72 case EscrowKeyType.kOTEscrowKeySymmetric:
73 keyLength = OT_ESCROW_SYMMETRIC_HKDF_SIZE
75 let infoString = Array("Escrow Symmetric Key".utf8)
76 info = Data(bytes: infoString, count: infoString.count)
78 case EscrowKeyType.kOTEscrowKeyEncryption:
79 keyLength = OT_ESCROW_ENCRYPTION_HKDF_SIZE
81 let infoString = Array("Escrow Encryption Private Key".utf8)
82 info = Data(bytes: infoString, count: infoString.count)
84 case EscrowKeyType.kOTEscrowKeySigning:
85 keyLength = OT_ESCROW_SIGNING_HKDF_SIZE
87 let infoString = Array("Escrow Signing Private Key".utf8)
88 info = Data(bytes: infoString, count: infoString.count)
91 guard let cp = ccec_cp_384() else {
92 throw EscrowKeysError.keyGeneration
96 let fullKey = TPHObjectiveC.ccec384Context()
97 defer { TPHObjectiveC.contextFree(fullKey) }
99 derivedKey = Data(count: keyLength)
101 var masterSecretMutable = masterSecret
103 let bottleSaltData = Data(bytes: Array(bottleSalt.utf8), count: bottleSalt.utf8.count)
105 try derivedKey.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutableRawBufferPointer) throws -> Void in
106 try masterSecretMutable.withUnsafeMutableBytes { (masterSecretBytes: UnsafeMutableRawBufferPointer) throws -> Void in
107 try bottleSaltData.withUnsafeBytes { (bottleSaltBytes: UnsafeRawBufferPointer) throws -> Void in
108 try info.withUnsafeBytes { (infoBytes: UnsafeRawBufferPointer) throws -> Void in
109 status = cchkdf(ccsha384_di(),
110 masterSecretBytes.count, masterSecretBytes.baseAddress!,
111 bottleSaltBytes.count, bottleSaltBytes.baseAddress!,
112 infoBytes.count, infoBytes.baseAddress!,
113 derivedKeyBytes.count, derivedKeyBytes.baseAddress!)
115 throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
118 if keyType == EscrowKeyType.kOTEscrowKeySymmetric {
119 finalKey = Data(buffer: derivedKeyBytes.bindMemory(to: UInt8.self))
121 } else if keyType == EscrowKeyType.kOTEscrowKeyEncryption || keyType == EscrowKeyType.kOTEscrowKeySigning {
122 status = ccec_generate_key_deterministic(cp,
123 derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
125 UInt32(CCEC_GENKEY_DETERMINISTIC_FIPS),
128 guard status == 0 else {
129 throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
132 let space = ccec_x963_export_size(1, ccec_ctx_pub(fullKey))
133 var key = Data(count: space)
134 key.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) -> Void in
135 ccec_x963_export(1, bytes.baseAddress!, fullKey)
146 class func createSecKey(keyData: Data) throws -> (SecKey) {
147 let keyAttributes = [kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeyType: kSecAttrKeyTypeEC]
148 guard let key = SecKeyCreateWithData(keyData as CFData, keyAttributes as CFDictionary, nil) else {
149 throw EscrowKeysError.keyGeneration
155 class func setKeyMaterialInKeychain(query: [CFString: Any]) throws -> (Bool) {
158 var results: CFTypeRef?
159 var status = SecItemAdd(query as CFDictionary, &results)
161 if status == errSecSuccess {
163 } else if status == errSecDuplicateItem {
164 var updateQuery: [CFString: Any] = query
165 updateQuery[kSecClass] = nil
167 status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
169 if status != errSecSuccess {
170 throw EscrowKeysError.failedToSaveToKeychain(errorCode: status)
175 throw EscrowKeysError.failedToSaveToKeychain(errorCode: status)
181 class func hashEscrowedSigningPublicKey(keyData: Data) throws -> (String) {
182 let di = ccsha384_di()
183 var result = Data(count: TPHObjectiveC.ccsha384_diSize())
185 var keyDataMutable = keyData
186 result.withUnsafeMutableBytes {(resultBytes: UnsafeMutableRawBufferPointer) -> Void in
187 keyDataMutable.withUnsafeMutableBytes {(keyDataBytes: UnsafeMutableRawBufferPointer) -> Void in
188 ccdigest(di, keyDataBytes.count, keyDataBytes.baseAddress!, resultBytes.baseAddress!)
191 let hash = result.base64EncodedString(options: [])
196 class func storeEscrowedEncryptionKeyPair(keyData: Data, label: String) throws -> (Bool) {
197 let query: [CFString: Any] = [
198 kSecClass: kSecClassKey,
199 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
200 kSecUseDataProtectionKeychain: true,
201 kSecAttrAccessGroup: "com.apple.security.octagon",
202 kSecAttrSynchronizable: false,
203 kSecAttrLabel: label,
204 kSecAttrApplicationLabel: String(format: "Escrowed Encryption Key-%@", NSUUID().uuidString),
205 kSecValueData: keyData,
207 return try EscrowKeys.setKeyMaterialInKeychain(query: query)
210 class func storeEscrowedSigningKeyPair(keyData: Data, label: String) throws -> (Bool) {
211 let query: [CFString: Any] = [
212 kSecClass: kSecClassKey,
213 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
214 kSecUseDataProtectionKeychain: true,
215 kSecAttrAccessGroup: "com.apple.security.octagon",
216 kSecAttrSynchronizable: false,
217 kSecAttrApplicationLabel: String(format: "Escrowed Signing Key-%@", NSUUID().uuidString),
218 kSecAttrLabel: label,
219 kSecValueData: keyData,
221 return try EscrowKeys.setKeyMaterialInKeychain(query: query)
224 class func storeEscrowedSymmetricKey(keyData: Data, label: String) throws -> (Bool) {
225 let query: [CFString: Any] = [
226 kSecClass: kSecClassKey,
227 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
228 kSecUseDataProtectionKeychain: true,
229 kSecAttrAccessGroup: "com.apple.security.octagon",
230 kSecAttrSynchronizable: false,
231 kSecAttrApplicationLabel: String(format: "Escrowed Symmetric Key-%@", NSUUID().uuidString),
232 kSecAttrLabel: label,
233 kSecValueData: keyData,
235 return try EscrowKeys.setKeyMaterialInKeychain(query: query)
238 class func retrieveEscrowKeysFromKeychain(label: String) throws -> [ [CFString: Any]]? {
239 var keySet: [[CFString: Any]]?
241 let query: [CFString: Any] = [
242 kSecClass: kSecClassKey,
243 kSecAttrAccessGroup: "com.apple.security.octagon",
244 kSecAttrLabel: label,
245 kSecReturnAttributes: true,
246 kSecReturnData: true,
247 kSecAttrSynchronizable: false,
248 kSecMatchLimit: kSecMatchLimitAll,
251 var result: CFTypeRef?
252 let status = SecItemCopyMatching(query as CFDictionary, &result)
254 if status != errSecSuccess || result == nil {
255 throw EscrowKeysError.itemDoesNotExist
259 if let dictionaryArray = result as? [[CFString: Any]] {
260 keySet = dictionaryArray
262 if let dictionary = result as? [CFString: Any] {
263 keySet = [dictionary]
272 class func findEscrowKeysForLabel(label: String) throws -> (_SFECKeyPair?, _SFECKeyPair?, _SFAESKey?) {
273 var signingKey: _SFECKeyPair?
274 var encryptionKey: _SFECKeyPair?
275 var symmetricKey: _SFAESKey?
277 let keySet = try retrieveEscrowKeysFromKeychain(label: label)
279 throw EscrowKeysError.itemDoesNotExist
281 for item in keySet! {
282 let keyTypeData = item[kSecAttrApplicationLabel as CFString] as! Data
283 let keyType = String(data: keyTypeData, encoding: .utf8)!
285 if keyType.contains("Symmetric") {
286 let keyData = item[kSecValueData as CFString] as! Data
287 let specifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
288 symmetricKey = try _SFAESKey.init(data: keyData, specifier: specifier)
289 } else if keyType.contains("Encryption") {
290 let keyData = item[kSecValueData as CFString] as! Data
291 let encryptionSecKey = try EscrowKeys.createSecKey(keyData: keyData)
292 encryptionKey = _SFECKeyPair.init(secKey: encryptionSecKey)
293 } else if keyType.contains("Signing") {
294 let keyData = item[kSecValueData as CFString] as! Data
295 let signingSecKey = try EscrowKeys.createSecKey(keyData: keyData)
296 signingKey = _SFECKeyPair.init(secKey: signingSecKey)
298 throw EscrowKeysError.unsupportedKeyType(keyType: keyType)
302 return (signingKey, encryptionKey, symmetricKey)
306 enum EscrowKeysError: Error {
308 case itemDoesNotExist
309 case failedToSaveToKeychain(errorCode: OSStatus)
310 case unsupportedKeyType(keyType: String)
311 case corecryptoKeyGeneration(corecryptoError: Int32)
314 extension EscrowKeysError: LocalizedError {
315 var errorDescription: String? {
318 return "Key generation failed"
319 case .itemDoesNotExist:
320 return "Item does not exist"
321 case .failedToSaveToKeychain(errorCode: let osError):
322 return "Failed to save item to keychain: \(osError)"
323 case .unsupportedKeyType(keyType: let keyType):
324 return "Unsupported Key Type \(keyType)"
325 case .corecryptoKeyGeneration(corecryptoError: let corecryptoError):
326 return "Key generation error \(corecryptoError)"
331 extension EscrowKeysError: CustomNSError {
332 static var errorDomain: String {
333 return "com.apple.security.trustedpeers.EscrowKeys"
340 case .itemDoesNotExist:
342 case .failedToSaveToKeychain:
344 case .unsupportedKeyType:
346 case .corecryptoKeyGeneration:
351 var errorUserInfo: [String: Any] {
352 var userInfo: [String: Any] = [:]
353 if let desc = self.errorDescription {
354 userInfo[NSLocalizedDescriptionKey] = desc
357 case .failedToSaveToKeychain(errorCode: let osError):
358 userInfo[NSUnderlyingErrorKey] = NSError(domain: NSOSStatusErrorDomain, code: Int(osError), userInfo: nil)
359 case .corecryptoKeyGeneration(corecryptoError: let corecryptoError):
360 userInfo[NSUnderlyingErrorKey] = NSError(domain: "corecrypto", code: Int(corecryptoError), userInfo: nil)