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