]> git.saurik.com Git - apple/security.git/blob - keychain/TrustedPeersHelper/BottledPeer/EscrowKeys.swift
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / TrustedPeersHelper / BottledPeer / EscrowKeys.swift
1 /*
2 * Copyright (c) 2019 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 import Foundation
25 import SecurityFoundation
26
27 let OT_ESCROW_SIGNING_HKDF_SIZE = 56
28 let OT_ESCROW_ENCRYPTION_HKDF_SIZE = 56
29 let OT_ESCROW_SYMMETRIC_HKDF_SIZE = 32
30
31 enum escrowKeyType: Int {
32 case kOTEscrowKeySigning = 1
33 case kOTEscrowKeyEncryption = 2
34 case kOTEscrowKeySymmetric = 3
35 }
36
37 class EscrowKeys: NSObject {
38 public var encryptionKey: _SFECKeyPair
39 public var signingKey: _SFECKeyPair
40 public var symmetricKey: _SFAESKey
41
42 public var secret: Data
43 public var bottleSalt: String
44
45 public init (secret: Data, bottleSalt: String) throws {
46 self.secret = secret
47 self.bottleSalt = bottleSalt
48
49 let encryptionKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret, bottleSalt: bottleSalt)
50 self.encryptionKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: encryptionKeyData))
51
52 let signingKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: secret, bottleSalt: bottleSalt)
53 self.signingKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: signingKeyData))
54
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)
58
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)
63 }
64
65 class func generateEscrowKey(keyType: escrowKeyType, masterSecret: Data, bottleSalt: String) throws -> (Data) {
66 var keyLength: Int
67 var info: Data
68 var infoLength: Int
69 var derivedKey: Data
70 var finalKey = Data()
71
72 switch keyType {
73 case escrowKeyType.kOTEscrowKeySymmetric:
74 keyLength = OT_ESCROW_SYMMETRIC_HKDF_SIZE
75
76 let infoString = Array("Escrow Symmetric Key".utf8)
77 info = Data(bytes: infoString, count: infoString.count)
78 infoLength = info.count
79
80 break
81 case escrowKeyType.kOTEscrowKeyEncryption:
82 keyLength = OT_ESCROW_ENCRYPTION_HKDF_SIZE
83
84 let infoString = Array("Escrow Encryption Private Key".utf8)
85 info = Data(bytes: infoString, count: infoString.count)
86 infoLength = info.count
87
88 break
89 case escrowKeyType.kOTEscrowKeySigning:
90 keyLength = OT_ESCROW_SIGNING_HKDF_SIZE
91
92 let infoString = Array("Escrow Signing Private Key".utf8)
93 info = Data(bytes: infoString, count: infoString.count)
94 infoLength = info.count
95
96 break
97 }
98
99 guard let cp = ccec_cp_384() else {
100 throw EscrowKeysError.keyGeneration
101 }
102 var status: Int32 = 0
103
104 let fullKey = TPHObjectiveC.ccec384Context()
105 defer { TPHObjectiveC.contextFree(fullKey) }
106
107 derivedKey = Data(count: keyLength)
108
109 var masterSecretMutable = masterSecret
110 let masterSecretLength = masterSecret.count
111 let derivedKeySize = derivedKey.count
112
113 let bottleSaltData = Data(bytes: Array(bottleSalt.utf8), count: bottleSalt.utf8.count)
114
115 try derivedKey.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutablePointer<UInt8>) throws ->Void in
116 try masterSecretMutable.withUnsafeMutableBytes { (masterSecretBytes: UnsafeMutablePointer<UInt8>) throws ->Void in
117 try bottleSaltData.withUnsafeBytes { (bottleSaltBytes: UnsafePointer<UInt8>) throws -> Void in
118 try info.withUnsafeBytes { (infoBytes: UnsafePointer<UInt8>) throws -> Void in
119 status = cchkdf(ccsha384_di(),
120 masterSecretLength, masterSecretBytes,
121 bottleSaltData.count, bottleSaltBytes,
122 infoLength, infoBytes,
123 keyLength, derivedKeyBytes)
124 if status != 0 {
125 throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
126 }
127
128 if(keyType == escrowKeyType.kOTEscrowKeySymmetric) {
129 finalKey = Data(buffer: UnsafeBufferPointer(start: derivedKeyBytes, count: derivedKeySize))
130 return
131 } else if(keyType == escrowKeyType.kOTEscrowKeyEncryption || keyType == escrowKeyType.kOTEscrowKeySigning) {
132 status = ccec_generate_key_deterministic(cp,
133 derivedKeySize, derivedKeyBytes,
134 ccDRBGGetRngState(),
135 UInt32(CCEC_GENKEY_DETERMINISTIC_FIPS),
136 fullKey)
137
138 guard status == 0 else {
139 throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
140 }
141
142 let space = ccec_x963_export_size(1, ccec_ctx_pub(fullKey))
143 var key = Data(count: space)
144 key.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) -> Void in
145 ccec_x963_export(1, bytes, fullKey)
146 }
147 finalKey = Data(key)
148 }
149 }
150 }
151 }
152 }
153 return finalKey
154 }
155
156 class func createSecKey(keyData: Data) throws -> (SecKey) {
157 let keyAttributes = [kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeyType: kSecAttrKeyTypeEC]
158 guard let key = SecKeyCreateWithData(keyData as CFData, keyAttributes as CFDictionary, nil) else {
159 throw EscrowKeysError.keyGeneration
160 }
161
162 return key
163 }
164
165 class func setKeyMaterialInKeychain(query: Dictionary<CFString, Any>) throws -> (Bool) {
166 var result = false
167
168 var results: CFTypeRef?
169 var status = SecItemAdd(query as CFDictionary, &results)
170
171 if status == errSecSuccess {
172 result = true
173 } else if status == errSecDuplicateItem {
174 var updateQuery: Dictionary<CFString, Any> = query
175 updateQuery[kSecClass] = nil
176
177 status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
178
179 if status != errSecSuccess {
180 throw EscrowKeysError.failedToSaveToKeychain(errorCode: status)
181 } else {
182 result = true
183 }
184 } else {
185 throw EscrowKeysError.failedToSaveToKeychain(errorCode: status)
186 }
187
188 return result
189 }
190
191 class func hashEscrowedSigningPublicKey(keyData: Data) throws -> (String) {
192 let di = ccsha384_di()
193 var result = Data(count: TPHObjectiveC.ccsha384_diSize())
194
195 let derivedKeySize = keyData.count
196 var keyDataMutable = keyData
197 result.withUnsafeMutableBytes {(resultBytes: UnsafeMutablePointer<UInt8>) -> Void in
198 keyDataMutable.withUnsafeMutableBytes {(keyDataBytes: UnsafeMutablePointer<UInt8>) -> Void in
199 ccdigest(di, derivedKeySize, keyDataBytes, resultBytes)
200 }
201 }
202 let hash = result.base64EncodedString(options: [])
203
204 return hash
205 }
206
207 class func storeEscrowedEncryptionKeyPair(keyData: Data, label: String) throws -> (Bool) {
208
209 let query: [CFString: Any] = [
210 kSecClass: kSecClassKey,
211 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
212 kSecUseDataProtectionKeychain: true,
213 kSecAttrAccessGroup: "com.apple.security.octagon",
214 kSecAttrSynchronizable: kCFBooleanFalse,
215 kSecAttrLabel: label,
216 kSecAttrApplicationLabel: String(format: "Escrowed Encryption Key-%@", NSUUID().uuidString),
217 kSecValueData: keyData,
218 ]
219 return try EscrowKeys.setKeyMaterialInKeychain(query: query)
220 }
221
222 class func storeEscrowedSigningKeyPair(keyData: Data, label: String) throws -> (Bool) {
223 let query: [CFString: Any] = [
224 kSecClass: kSecClassKey,
225 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
226 kSecUseDataProtectionKeychain: true,
227 kSecAttrAccessGroup: "com.apple.security.octagon",
228 kSecAttrSynchronizable: kCFBooleanFalse,
229 kSecAttrApplicationLabel: String(format: "Escrowed Signing Key-%@", NSUUID().uuidString),
230 kSecAttrLabel: label,
231 kSecValueData: keyData,
232 ]
233 return try EscrowKeys.setKeyMaterialInKeychain(query: query)
234 }
235
236 class func storeEscrowedSymmetricKey(keyData: Data, label: String) throws -> (Bool) {
237 let query: [CFString: Any] = [
238 kSecClass: kSecClassKey,
239 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
240 kSecUseDataProtectionKeychain: true,
241 kSecAttrAccessGroup: "com.apple.security.octagon",
242 kSecAttrSynchronizable: kCFBooleanFalse,
243 kSecAttrApplicationLabel: String(format: "Escrowed Symmetric Key-%@", NSUUID().uuidString),
244 kSecAttrLabel: label,
245 kSecValueData: keyData,
246 ]
247 return try EscrowKeys.setKeyMaterialInKeychain(query: query)
248 }
249
250 class func retrieveEscrowKeysFromKeychain(label: String) throws -> [Dictionary <CFString, Any>]? {
251 var keySet: [Dictionary<CFString, Any>]?
252
253 let query: [CFString: Any] = [
254 kSecClass: kSecClassKey,
255 kSecAttrAccessGroup: "com.apple.security.octagon",
256 kSecAttrLabel: label,
257 kSecReturnAttributes: true,
258 kSecReturnData: true,
259 kSecAttrSynchronizable: kCFBooleanFalse,
260 kSecMatchLimit: kSecMatchLimitAll,
261 ]
262
263 var result: CFTypeRef?
264 let status = SecItemCopyMatching(query as CFDictionary, &result)
265
266 if status != errSecSuccess || result == nil {
267 throw EscrowKeysError.itemDoesNotExist
268 }
269
270 if result != nil {
271 if let dictionaryArray = result as? [Dictionary<CFString, Any>] {
272 keySet = dictionaryArray
273 } else {
274 if let dictionary = result as? Dictionary<CFString, Any> {
275 keySet = [dictionary]
276 } else {
277 keySet = nil
278 }
279 }
280 }
281 return keySet
282 }
283
284 class func findEscrowKeysForLabel(label: String) throws -> (_SFECKeyPair?, _SFECKeyPair?, _SFAESKey?) {
285 var signingKey: _SFECKeyPair?
286 var encryptionKey: _SFECKeyPair?
287 var symmetricKey: _SFAESKey?
288
289 let keySet = try retrieveEscrowKeysFromKeychain(label: label)
290 if keySet == nil {
291 throw EscrowKeysError.itemDoesNotExist
292 }
293 for item in keySet! {
294 let keyTypeData = item[kSecAttrApplicationLabel as CFString] as! Data
295 let keyType = String(data: keyTypeData, encoding: .utf8)!
296
297 if keyType.range(of: "Symmetric") != nil {
298 let keyData = item[kSecValueData as CFString] as! Data
299 let specifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
300 symmetricKey = try _SFAESKey.init(data: keyData, specifier: specifier)
301 } else if keyType.range(of: "Encryption") != nil {
302 let keyData = item[kSecValueData as CFString] as! Data
303 let encryptionSecKey = try EscrowKeys.createSecKey(keyData: keyData)
304 encryptionKey = _SFECKeyPair.init(secKey: encryptionSecKey)
305 } else if keyType.range(of: "Signing") != nil {
306 let keyData = item[kSecValueData as CFString] as! Data
307 let signingSecKey = try EscrowKeys.createSecKey(keyData: keyData)
308 signingKey = _SFECKeyPair.init(secKey: signingSecKey)
309 } else {
310 throw EscrowKeysError.unsupportedKeyType(keyType: keyType)
311 }
312 }
313
314 return (signingKey, encryptionKey, symmetricKey)
315 }
316 }
317
318 enum EscrowKeysError: Error {
319 case keyGeneration
320 case itemDoesNotExist
321 case failedToSaveToKeychain(errorCode: OSStatus)
322 case unsupportedKeyType(keyType: String)
323 case corecryptoKeyGeneration(corecryptoError: Int32)
324 }
325
326 extension EscrowKeysError: LocalizedError {
327 public var errorDescription: String? {
328 switch self {
329 case .keyGeneration:
330 return "Key generation failed"
331 case .itemDoesNotExist:
332 return "Item does not exist"
333 case .failedToSaveToKeychain(errorCode: let osError):
334 return "Failed to save item to keychain: \(osError)"
335 case .unsupportedKeyType(keyType: let keyType):
336 return "Unsupported Key Type \(keyType)"
337 case .corecryptoKeyGeneration(corecryptoError: let corecryptoError):
338 return "Key generation error \(corecryptoError)"
339 }
340 }
341 }
342
343 extension EscrowKeysError: CustomNSError {
344
345 public static var errorDomain: String {
346 return "com.apple.security.trustedpeers.EscrowKeys"
347 }
348
349 public var errorCode: Int {
350 switch self {
351 case .keyGeneration:
352 return 1
353 case .itemDoesNotExist:
354 return 2
355 case .failedToSaveToKeychain:
356 return 3
357 case .unsupportedKeyType:
358 return 4
359 case .corecryptoKeyGeneration:
360 return 5
361 }
362 }
363
364 public var errorUserInfo: [String: Any] {
365 var userInfo: [String: Any] = [:]
366 if let desc = self.errorDescription {
367 userInfo[NSLocalizedDescriptionKey] = desc
368 }
369 switch self {
370 case .failedToSaveToKeychain(errorCode: let osError):
371 userInfo[NSUnderlyingErrorKey] = NSError.init(domain: NSOSStatusErrorDomain, code: Int(osError), userInfo: nil)
372 case .corecryptoKeyGeneration(corecryptoError: let corecryptoError):
373 userInfo[NSUnderlyingErrorKey] = NSError.init(domain: "corecrypto", code: Int(corecryptoError), userInfo: nil)
374 default:
375 break
376 }
377 return userInfo
378 }
379 }
380
381