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