]> git.saurik.com Git - apple/security.git/blob - keychain/TrustedPeersHelper/Container.swift
Security-59306.101.1.tar.gz
[apple/security.git] / keychain / TrustedPeersHelper / Container.swift
1 /*
2 * Copyright (c) 2018 - 2020 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 CloudKitCode
25 import CoreData
26 import Foundation
27 import os
28 import Security
29 import SecurityFoundation
30
31 let tplogDebug = OSLog(subsystem: "com.apple.security.trustedpeers", category: "debug")
32 let tplogTrace = OSLog(subsystem: "com.apple.security.trustedpeers", category: "trace")
33
34 let egoIdentitiesAccessGroup = "com.apple.security.egoIdentities"
35
36 extension ResetReason {
37 static func from(cuttlefishResetReason: CuttlefishResetReason) -> ResetReason {
38 switch cuttlefishResetReason {
39 case .unknown:
40 return ResetReason.unknown
41 case .userInitiatedReset:
42 return ResetReason.userInitiatedReset
43 case .healthCheck:
44 return ResetReason.healthCheck
45 case .noBottleDuringEscrowRecovery:
46 return ResetReason.noBottleDuringEscrowRecovery
47 case .legacyJoinCircle:
48 return ResetReason.legacyJoinCircle
49 case .recoveryKey:
50 return ResetReason.recoveryKey
51 case .testGenerated:
52 return ResetReason.testGenerated
53 @unknown default:
54 fatalError()
55 }
56 }
57 }
58
59 public enum ContainerError: Error {
60 case unableToCreateKeyPair
61 case noPreparedIdentity
62 case failedToStoreIdentity
63 case needsAuthentication
64 case missingStableInfo
65 case missingDynamicInfo
66 case nonMember
67 case invalidPermanentInfoOrSig
68 case invalidStableInfoOrSig
69 case invalidVoucherOrSig
70 case sponsorNotRegistered(String)
71 case unknownPolicyVersion(UInt64)
72 case preparedIdentityNotOnAllowedList(String)
73 case couldNotLoadAllowedList
74 case noPeersPreapprovePreparedIdentity
75 case policyDocumentDoesNotValidate
76 case tooManyBottlesForPeer
77 case noBottleForPeer
78 case restoreBottleFailed
79 case noBottlesForEscrowRecordID
80 case bottleDoesNotContainContents
81 case bottleDoesNotContainEscrowKeySignature
82 case bottleDoesNotContainerPeerKeySignature
83 case bottleDoesNotContainPeerID
84 case failedToCreateBottledPeer
85 case signatureVerificationFailed
86 case bottleDoesNotContainerEscrowKeySPKI
87 case failedToFetchEscrowContents
88 case failedToCreateRecoveryKey
89 case untrustedRecoveryKeys
90 case noBottlesPresent
91 case recoveryKeysNotEnrolled
92 case bottleCreatingPeerNotFound
93 case unknownCloudKitError
94 case cloudkitResponseMissing
95 case failedToLoadSecret(errorCode: Int)
96 case failedToLoadSecretDueToType
97 case failedToAssembleBottle
98 case invalidPeerID
99 case failedToStoreSecret(errorCode: Int)
100 case unknownSecurityFoundationError
101 case failedToSerializeData
102 case unknownInternalError
103 }
104
105 extension ContainerError: LocalizedError {
106 public var errorDescription: String? {
107 switch self {
108 case .unableToCreateKeyPair:
109 return "unable to create key pair"
110 case .noPreparedIdentity:
111 return "no prepared identity"
112 case .failedToStoreIdentity:
113 return "failed to stored identity"
114 case .needsAuthentication:
115 return "needs authentication"
116 case .missingStableInfo:
117 return "missing stable info"
118 case .missingDynamicInfo:
119 return "missing dynamic info"
120 case .nonMember:
121 return "non member"
122 case .invalidPermanentInfoOrSig:
123 return "invalid permanent info or signature"
124 case .invalidStableInfoOrSig:
125 return "invalid stable info or signature"
126 case .invalidVoucherOrSig:
127 return "invalid voucher or signature"
128 case .sponsorNotRegistered(let s):
129 return "sponsor not registered: \(s)"
130 case .unknownPolicyVersion(let v):
131 return "unknown policy version: \(v)"
132 case .preparedIdentityNotOnAllowedList(let id):
133 return "prepared identity (\(id)) not on allowed machineID list"
134 case .couldNotLoadAllowedList:
135 return "could not load allowed machineID list"
136 case .noPeersPreapprovePreparedIdentity:
137 return "no peers preapprove prepared identity"
138 case .policyDocumentDoesNotValidate:
139 return "policy document from server doesn't validate"
140 case .tooManyBottlesForPeer:
141 return "too many bottles exist for peer"
142 case .noBottleForPeer:
143 return "no bottle exists for peer"
144 case .restoreBottleFailed:
145 return "failed to restore bottle"
146 case .noBottlesForEscrowRecordID:
147 return "0 bottles exist for escrow record id"
148 case .bottleDoesNotContainContents:
149 return "bottle does not contain encrypted contents"
150 case .bottleDoesNotContainEscrowKeySignature:
151 return "bottle does not contain escrow signature"
152 case .bottleDoesNotContainerPeerKeySignature:
153 return "bottle does not contain peer signature"
154 case .bottleDoesNotContainPeerID:
155 return "bottle does not contain peer id"
156 case .failedToCreateBottledPeer:
157 return "failed to create a bottled peer"
158 case .signatureVerificationFailed:
159 return "failed to verify signature"
160 case .bottleDoesNotContainerEscrowKeySPKI:
161 return "bottle does not contain escrowed key spki"
162 case .failedToFetchEscrowContents:
163 return "failed to fetch escrow contents"
164 case .failedToCreateRecoveryKey:
165 return "failed to create recovery keys"
166 case .untrustedRecoveryKeys:
167 return "untrusted recovery keys"
168 case .noBottlesPresent:
169 return "no bottle present"
170 case .recoveryKeysNotEnrolled:
171 return "recovery key is not enrolled with octagon"
172 case .bottleCreatingPeerNotFound:
173 return "The peer that created the bottle was not found"
174 case .unknownCloudKitError:
175 return "unknown error from cloudkit"
176 case .cloudkitResponseMissing:
177 return "Response missing from CloudKit"
178 case .failedToLoadSecret(errorCode: let errorCode):
179 return "failed to load secret: \(errorCode)"
180 case .failedToLoadSecretDueToType:
181 return "Failed to load secret due to type mismatch (value was not dictionary)"
182 case .failedToAssembleBottle:
183 return "failed to assemble bottle for peer"
184 case .invalidPeerID:
185 return "peerID is invalid"
186 case .failedToStoreSecret(errorCode: let errorCode):
187 return "failed to store the secret in the keychain \(errorCode)"
188 case .unknownSecurityFoundationError:
189 return "SecurityFoundation returned an unknown type"
190 case .failedToSerializeData:
191 return "Failed to encode protobuf data"
192 case .unknownInternalError:
193 return "Internal code failed, but didn't return error"
194 }
195 }
196 }
197
198 extension ContainerError: CustomNSError {
199
200 public static var errorDomain: String {
201 return "com.apple.security.trustedpeers.container"
202 }
203
204 public var errorCode: Int {
205 switch self {
206 case .unableToCreateKeyPair:
207 return 0
208 case .noPreparedIdentity:
209 return 1
210 case .failedToStoreIdentity:
211 return 2
212 case .needsAuthentication:
213 return 3
214 case .missingStableInfo:
215 return 4
216 case .missingDynamicInfo:
217 return 5
218 case .nonMember:
219 return 6
220 case .invalidPermanentInfoOrSig:
221 return 7
222 case .invalidVoucherOrSig:
223 return 8
224 case .invalidStableInfoOrSig:
225 return 9
226 case .sponsorNotRegistered:
227 return 11
228 case .unknownPolicyVersion:
229 return 12
230 case .preparedIdentityNotOnAllowedList:
231 return 13
232 case .noPeersPreapprovePreparedIdentity:
233 return 14
234 case .policyDocumentDoesNotValidate:
235 return 15
236 // 16 was invalidPeerID and failedToAssembleBottle
237 case .tooManyBottlesForPeer:
238 return 17
239 case .noBottleForPeer:
240 return 18
241 case .restoreBottleFailed:
242 return 19
243 case .noBottlesForEscrowRecordID:
244 return 20
245 case .bottleDoesNotContainContents:
246 return 21
247 case .bottleDoesNotContainEscrowKeySignature:
248 return 22
249 case .bottleDoesNotContainerPeerKeySignature:
250 return 23
251 case .bottleDoesNotContainPeerID:
252 return 24
253 case .failedToCreateBottledPeer:
254 return 25
255 case .signatureVerificationFailed:
256 return 26
257 // 27 was failedToLoadSecret*
258 case .bottleDoesNotContainerEscrowKeySPKI:
259 return 28
260 case .failedToFetchEscrowContents:
261 return 29
262 case .couldNotLoadAllowedList:
263 return 30
264 case .failedToCreateRecoveryKey:
265 return 31
266 case .untrustedRecoveryKeys:
267 return 32
268 case .noBottlesPresent:
269 return 33
270 case .recoveryKeysNotEnrolled:
271 return 34
272 case .bottleCreatingPeerNotFound:
273 return 35
274 case .unknownCloudKitError:
275 return 36
276 case .cloudkitResponseMissing:
277 return 37
278 case .failedToLoadSecret:
279 return 38
280 case .failedToLoadSecretDueToType:
281 return 39
282 case .failedToStoreSecret:
283 return 40
284 case .failedToAssembleBottle:
285 return 41
286 case .invalidPeerID:
287 return 42
288 case .unknownSecurityFoundationError:
289 return 43
290 case .failedToSerializeData:
291 return 44
292 case .unknownInternalError:
293 return 45
294 }
295 }
296
297 public var underlyingError: NSError? {
298 switch self {
299 case .failedToLoadSecret(errorCode: let errorCode):
300 return NSError(domain: "securityd", code: errorCode)
301 case .failedToStoreSecret(errorCode: let errorCode):
302 return NSError(domain: "securityd", code: errorCode)
303 default:
304 return nil
305 }
306 }
307
308 public var errorUserInfo: [String: Any] {
309 var ret = [String: Any]()
310 if let desc = self.errorDescription {
311 ret[NSLocalizedDescriptionKey] = desc
312 }
313 if let underlyingError = self.underlyingError {
314 ret[NSUnderlyingErrorKey] = underlyingError
315 }
316 return ret
317 }
318 }
319
320 internal func traceError(_ error: Error?) -> String {
321 if let error = error {
322 return "error: \(String(describing: error))"
323 } else {
324 return "success"
325 }
326 }
327
328 func saveSecret(_ secret: Data, label: String) throws {
329
330 let query: [CFString: Any] = [
331 kSecClass: kSecClassInternetPassword,
332 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
333 kSecUseDataProtectionKeychain: true,
334 kSecAttrAccessGroup: "com.apple.security.octagon",
335 kSecAttrSynchronizable: false,
336 kSecAttrDescription: label,
337 kSecAttrPath: label,
338 kSecValueData: secret,
339 ]
340
341 var results: CFTypeRef?
342 var status = SecItemAdd(query as CFDictionary, &results)
343
344 if status == errSecSuccess {
345 return
346 }
347
348 if status == errSecDuplicateItem {
349 // Add every primary key attribute to this find dictionary
350 var findQuery: [CFString: Any] = [:]
351 findQuery[kSecClass] = query[kSecClass]
352 findQuery[kSecAttrSynchronizable] = query[kSecAttrSynchronizable]
353 findQuery[kSecAttrAccessGroup] = query[kSecAttrAccessGroup]
354 findQuery[kSecAttrServer] = query[kSecAttrDescription]
355 findQuery[kSecAttrPath] = query[kSecAttrPath]
356 findQuery[kSecUseDataProtectionKeychain] = query[kSecUseDataProtectionKeychain]
357
358 var updateQuery: [CFString: Any] = query
359 updateQuery[kSecClass] = nil
360
361 status = SecItemUpdate(findQuery as CFDictionary, updateQuery as CFDictionary)
362
363 if status != errSecSuccess {
364 throw ContainerError.failedToStoreSecret(errorCode: Int(status))
365 }
366 } else {
367 throw ContainerError.failedToStoreSecret(errorCode: Int(status))
368 }
369 }
370
371 func loadSecret(label: String) throws -> (Data?) {
372 var secret: Data?
373
374 let query: [CFString: Any] = [
375 kSecClass: kSecClassInternetPassword,
376 kSecAttrAccessGroup: "com.apple.security.octagon",
377 kSecAttrDescription: label,
378 kSecReturnAttributes: true,
379 kSecReturnData: true,
380 kSecAttrSynchronizable: false,
381 kSecMatchLimit: kSecMatchLimitOne,
382 ]
383
384 var result: CFTypeRef?
385 let status = SecItemCopyMatching(query as CFDictionary, &result)
386
387 if status != errSecSuccess || result == nil {
388 throw ContainerError.failedToLoadSecret(errorCode: Int(status))
389 }
390
391 if result != nil {
392 if let dictionary = result as? [CFString: Any] {
393 secret = dictionary[kSecValueData] as? Data
394 } else {
395 throw ContainerError.failedToLoadSecretDueToType
396 }
397 }
398 return secret
399 }
400
401 func saveEgoKeyPair(_ keyPair: _SFECKeyPair, identifier: String, resultHandler: @escaping (Bool, Error?) -> Void) {
402 let keychainManager = _SFKeychainManager.default()
403 let signingIdentity = _SFIdentity(keyPair: keyPair)
404 let accessibility = SFAccessibilityMakeWithMode(SFAccessibilityMode.accessibleWhenUnlocked) // class A
405 let accessPolicy = _SFAccessPolicy(accessibility: accessibility,
406 sharingPolicy: SFSharingPolicy.thisDeviceOnly)
407 accessPolicy.accessGroup = egoIdentitiesAccessGroup
408 keychainManager.setIdentity(signingIdentity,
409 forIdentifier: identifier,
410 accessPolicy: accessPolicy,
411 resultHandler: resultHandler)
412 }
413
414 func loadEgoKeyPair(identifier: String, resultHandler: @escaping (_SFECKeyPair?, Error?) -> Void) {
415 let keychainManager = _SFKeychainManager.default()
416
417 // FIXME constrain to egoIdentitiesAccessGroup, <rdar://problem/39597940>
418 keychainManager.identity(forIdentifier: identifier) { result in
419 switch result.resultType {
420 case .valueAvailable:
421 resultHandler(result.value?.keyPair as? _SFECKeyPair, nil)
422 case .needsAuthentication:
423 resultHandler(nil, ContainerError.needsAuthentication)
424 case .error:
425 resultHandler(nil, result.error)
426 @unknown default:
427 resultHandler(nil, ContainerError.unknownSecurityFoundationError)
428 }
429 }
430 }
431
432 func loadEgoKeys(peerID: String, resultHandler: @escaping (OctagonSelfPeerKeys?, Error?) -> Void) {
433 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: peerID)) { signingKey, error in
434 guard let signingKey = signingKey else {
435 os_log("Unable to load signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
436 resultHandler(nil, error)
437 return
438 }
439
440 loadEgoKeyPair(identifier: encryptionKeyIdentifier(peerID: peerID)) { encryptionKey, error in
441 guard let encryptionKey = encryptionKey else {
442 os_log("Unable to load encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
443 resultHandler(nil, error)
444 return
445 }
446
447 do {
448 resultHandler(try OctagonSelfPeerKeys(peerID: peerID, signingKey: signingKey, encryptionKey: encryptionKey), nil)
449 } catch {
450 resultHandler(nil, error)
451 }
452 }
453 }
454 }
455
456 func removeEgoKeysSync(peerID: String) throws -> Bool {
457 var resultSema = DispatchSemaphore(value: 0)
458
459 let keychainManager = _SFKeychainManager.default()
460
461 var retresultForSigningDeletion: Bool = false
462 var reterrorForSigningDeletion: Error?
463
464 //remove signing keys
465 keychainManager.removeItem(withIdentifier: signingKeyIdentifier(peerID: peerID)) { result, error in
466 retresultForSigningDeletion = result
467 reterrorForSigningDeletion = error
468 resultSema.signal()
469 }
470
471 resultSema.wait()
472
473 if let error = reterrorForSigningDeletion {
474 throw error
475 }
476
477 if retresultForSigningDeletion == false {
478 return retresultForSigningDeletion
479 }
480
481 // now let's do the same thing with the encryption keys
482 resultSema = DispatchSemaphore(value: 0)
483 var retresultForEncryptionDeletion: Bool = false
484 var reterrorForEncryptionDeletion: Error?
485
486 keychainManager.removeItem(withIdentifier: encryptionKeyIdentifier(peerID: peerID)) { result, error in
487 retresultForEncryptionDeletion = result
488 reterrorForEncryptionDeletion = error
489 resultSema.signal()
490 }
491 resultSema.wait()
492
493 if let error = reterrorForEncryptionDeletion {
494 throw error
495 }
496
497 return retresultForEncryptionDeletion && retresultForSigningDeletion
498 }
499
500 func loadEgoKeysSync(peerID: String) throws -> OctagonSelfPeerKeys {
501 // Gotta promote to synchronous; 'antipattern' ahoy
502 let resultSema = DispatchSemaphore(value: 0)
503
504 var result: OctagonSelfPeerKeys?
505 var reserror: Error?
506
507 loadEgoKeys(peerID: peerID) { keys, error in
508 result = keys
509 reserror = error
510 resultSema.signal()
511 }
512 resultSema.wait()
513
514 if let error = reserror {
515 throw error
516 }
517
518 if let result = result {
519 return result
520 }
521
522 abort()
523 }
524
525 func signingKeyIdentifier(peerID: String) -> String {
526 return "signing-key " + peerID
527 }
528
529 func encryptionKeyIdentifier(peerID: String) -> String {
530 return "encryption-key " + peerID
531 }
532
533 func makeTLKShares(ckksTLKs: [CKKSKeychainBackedKey]?, asPeer: CKKSSelfPeer, toPeer: CKKSPeer, epoch: Int) throws -> [TLKShare] {
534 return try (ckksTLKs ?? []).map { tlk in
535 os_log("Making TLKShare for %@ for key %@", log: tplogDebug, type: .default, toPeer.description, tlk)
536 // Not being able to convert a TLK to a TLKShare is a failure, but not having a TLK is only half bad
537 do {
538 return TLKShare.convert(ckksTLKShare: try CKKSTLKShare(tlk, as: asPeer, to: toPeer, epoch: epoch, poisoned: 0))
539 } catch {
540 let nserror = error as NSError
541 if nserror.domain == "securityd" && nserror.code == errSecItemNotFound {
542 os_log("No TLK contents for %@, no TLK share possible", log: tplogDebug, type: .default, tlk)
543 return nil
544 } else {
545 throw error
546 }
547 }
548 }.compactMap { $0 }
549 }
550
551 @discardableResult
552 func extract(tlkShares: [CKKSTLKShare], peer: OctagonSelfPeerKeys, model: TPModel) -> (Int64, Int64) {
553 os_log("Attempting to recover %d TLK shares for peer %{public}@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
554 var tlksRecovered: Set<String> = Set()
555 var sharesRecovered: Int64 = 0
556
557 for share in tlkShares {
558 guard share.receiverPeerID == peer.peerID else {
559 os_log("Skipping %@ (wrong peerID)", log: tplogDebug, type: .default, share)
560 continue
561 }
562
563 do {
564 var trustedPeers: [AnyHashable] = [peer]
565
566 if let egoPeer = model.peer(withID: peer.peerID) {
567 egoPeer.trustedPeerIDs.forEach { trustedPeerID in
568 if let peer = model.peer(withID: trustedPeerID) {
569 let peerObj = CKKSActualPeer(peerID: trustedPeerID,
570 encryptionPublicKey: (peer.permanentInfo.encryptionPubKey as! _SFECPublicKey),
571 signing: (peer.permanentInfo.signingPubKey as! _SFECPublicKey),
572 viewList: [])
573
574 trustedPeers.append(peerObj)
575 } else {
576 os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
577 }
578 }
579 } else {
580 os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
581 }
582
583 let key = try share.recoverTLK(peer,
584 trustedPeers: Set(trustedPeers),
585 ckrecord: nil)
586
587 try key.saveMaterialToKeychain()
588 tlksRecovered.insert(key.uuid)
589 sharesRecovered += 1
590 os_log("Recovered %@ (from %@)", log: tplogDebug, type: .default, key, share)
591 } catch {
592 os_log("Failed to recover share %@: %{public}@", log: tplogDebug, type: .default, share, error as CVarArg)
593 }
594 }
595
596 return (Int64(tlksRecovered.count), sharesRecovered)
597 }
598
599 struct ContainerState {
600 var egoPeerID: String?
601 var peers: [String: TPPeer] = [:]
602 var vouchers: [TPVoucher] = []
603 var bottles = Set<BottleMO>()
604 var recoverySigningKey: Data?
605 var recoveryEncryptionKey: Data?
606 }
607
608 internal struct StableChanges {
609 let deviceName: String?
610 let serialNumber: String?
611 let osVersion: String?
612 let policyVersion: UInt64?
613 let policySecrets: [String: Data]?
614 }
615
616 // CoreData doesn't handle creating an identical model from an identical URL. Help it out.
617 private var nsObjectModels: [URL: NSManagedObjectModel] = [:]
618 private let nsObjectModelsQueue = DispatchQueue(label: "com.apple.security.TrustedPeersHelper.nsObjectModels")
619 func getOrMakeModel(url: URL) -> NSManagedObjectModel {
620 return nsObjectModelsQueue.sync {
621 if let model = nsObjectModels[url] {
622 return model
623 }
624 let newModel = NSManagedObjectModel(contentsOf: url)!
625 nsObjectModels[url] = newModel
626 return newModel
627 }
628 }
629
630 struct ContainerName: Hashable, CustomStringConvertible {
631 let container: String
632 let context: String
633
634 func asSingleString() -> String {
635 // Just to be nice, hide the fact that multiple contexts exist on most machines
636 if self.context == OTDefaultContext {
637 return self.container
638 } else {
639 return self.container + "-" + self.context
640 }
641 }
642
643 var description: String {
644 return "Container(\(self.container),\(self.context))"
645 }
646 }
647
648 extension ContainerMO {
649 func egoStableInfo() -> TPPeerStableInfo? {
650 guard let egoStableData = self.egoPeerStableInfo,
651 let egoStableSig = self.egoPeerStableInfoSig else {
652 return nil
653 }
654
655 return TPPeerStableInfo(data: egoStableData, sig: egoStableSig)
656 }
657 }
658
659 /// This maps to a Cuttlefish service backed by a CloudKit container,
660 /// and a corresponding local Core Data persistent container.
661 ///
662 /// Methods may be invoked concurrently.
663 class Container: NSObject {
664 let name: ContainerName
665
666 private let cuttlefish: CuttlefishAPIAsync
667
668 // Only one request (from Client) is permitted to be in progress at a time.
669 // That includes while waiting for network, i.e. one request must complete
670 // before the next can begin. Otherwise two requests could in parallel be
671 // fetching updates from Cuttlefish, with ensuing changeToken overwrites etc.
672 // This applies for mutating requests -- requests that can only read the current
673 // state (on the moc queue) do not need to.
674 internal let semaphore = DispatchSemaphore(value: 1)
675
676 // All Core Data access happens through moc: NSManagedObjectContext. The
677 // moc insists on having its own queue, and all operations must happen on
678 // that queue.
679 internal let moc: NSManagedObjectContext
680
681 // Rather than Container having its own dispatch queue, we use moc's queue
682 // to synchronise access to our own state as well. So the following instance
683 // variables must only be accessed within blocks executed by calling
684 // moc.perform() or moc.performAndWait().
685 internal var containerMO: ContainerMO
686 internal var model: TPModel
687 /**
688 Construct a Container.
689
690 - Parameter name: The name the CloudKit container to which requests will be routed.
691
692 The "real" container that drives CKKS etc should be named `"com.apple.security.keychain"`.
693 Use other names for test containers such as for rawfish (rawhide-style testing for cuttlefish)
694
695 - Parameter persistentStoreURL: The location the local Core Data database for this container will be stored.
696
697 - Parameter cuttlefish: Interface to cuttlefish.
698 */
699 init(name: ContainerName, persistentStoreDescription: NSPersistentStoreDescription, cuttlefish: CuttlefishAPIAsync) throws {
700 var initError: Error?
701 var containerMO: ContainerMO?
702 var model: TPModel?
703
704 // Set up Core Data stack
705 let url = Bundle(for: type(of: self)).url(forResource: "TrustedPeersHelper", withExtension: "momd")!
706 let mom = getOrMakeModel(url: url)
707 let persistentContainer = NSPersistentContainer(name: "TrustedPeersHelper", managedObjectModel: mom)
708 persistentContainer.persistentStoreDescriptions = [persistentStoreDescription]
709
710 persistentContainer.loadPersistentStores { _, error in
711 initError = error
712 }
713 if let initError = initError {
714 throw initError
715 }
716
717 let moc = persistentContainer.newBackgroundContext()
718 moc.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
719
720 moc.performAndWait {
721 // Fetch an existing ContainerMO record if it exists, or create and save one
722 do {
723 let containerFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Container")
724 containerFetch.predicate = NSPredicate(format: "name == %@", name.asSingleString())
725 let fetchedContainers = try moc.fetch(containerFetch)
726 if let container = fetchedContainers.first as? ContainerMO {
727 containerMO = container
728 } else {
729 containerMO = ContainerMO(context: moc)
730 containerMO!.name = name.asSingleString()
731 }
732
733 // Perform upgrades as needed
734 Container.onqueueUpgradeMachineIDSetToModel(container: containerMO!, moc: moc)
735 Container.onqueueUpgradeMachineIDSetToUseStatus(container: containerMO!, moc: moc)
736
737 model = Container.loadModel(from: containerMO!)
738 Container.ensureEgoConsistency(from: containerMO!, model: model!)
739 try moc.save()
740 } catch {
741 initError = error
742 return
743 }
744 }
745 if let initError = initError {
746 throw initError
747 }
748
749 self.name = name
750 self.moc = moc
751 self.containerMO = containerMO!
752 self.cuttlefish = cuttlefish
753 self.model = model!
754 super.init()
755 }
756
757 // Must be on containerMO's moc queue to call this
758 internal static func loadModel(from containerMO: ContainerMO) -> TPModel {
759 // Populate model from persistent store
760 let model = TPModel(decrypter: Decrypter())
761 let keyFactory = TPECPublicKeyFactory()
762 let peers = containerMO.peers as? Set<PeerMO>
763 peers?.forEach { peer in
764 guard let permanentInfo = TPPeerPermanentInfo(peerID: peer.peerID!,
765 data: peer.permanentInfo! as Data,
766 sig: peer.permanentInfoSig! as Data,
767 keyFactory: keyFactory) else {
768 return
769 }
770 model.registerPeer(with: permanentInfo)
771 if let data = peer.stableInfo, let sig = peer.stableInfoSig {
772 if let stableInfo = TPPeerStableInfo(data: data as Data, sig: sig as Data) {
773 do {
774 try model.update(stableInfo, forPeerWithID: permanentInfo.peerID)
775 } catch {
776 os_log("loadModel unable to update stable info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
777 }
778 } else {
779 os_log("loadModel: peer %{public}@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
780 }
781 } else {
782 os_log("loadModel: peer %{public}@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
783 }
784 if let data = peer.dynamicInfo, let sig = peer.dynamicInfoSig {
785 if let dynamicInfo = TPPeerDynamicInfo(data: data as Data, sig: sig as Data) {
786 do {
787 try model.update(dynamicInfo, forPeerWithID: permanentInfo.peerID)
788 } catch {
789 os_log("loadModel unable to update dynamic info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
790 }
791 } else {
792 os_log("loadModel: peer %{public}@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
793 }
794 } else {
795 os_log("loadModel: peer %{public}@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
796 }
797 peer.vouchers?.forEach {
798 let v = $0 as! VoucherMO
799 if let data = v.voucherInfo, let sig = v.voucherInfoSig {
800 if let voucher = TPVoucher(infoWith: data, sig: sig) {
801 model.register(voucher)
802 }
803 }
804 }
805 }
806
807 if let recoveryKeySigningSPKI = containerMO.recoveryKeySigningSPKI,
808 let recoveryKeyEncyryptionSPKI = containerMO.recoveryKeyEncryptionSPKI {
809 model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: recoveryKeySigningSPKI, encryptionSPKI: recoveryKeyEncyryptionSPKI))
810 } else {
811 // If the ego peer has an RK set, tell the model to use that one
812 // This is a hack to work around TPH databases which don't have the RK set on the container due to previously running old software
813 if let egoStableInfo = containerMO.egoStableInfo(),
814 egoStableInfo.recoverySigningPublicKey.count > 0,
815 egoStableInfo.recoveryEncryptionPublicKey.count > 0 {
816 os_log("loadModel: recovery key not set in model, but is set on ego peer", log: tplogDebug, type: .default)
817 model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: egoStableInfo.recoverySigningPublicKey, encryptionSPKI: egoStableInfo.recoveryEncryptionPublicKey))
818 }
819 }
820
821 // Register persisted policies (cached from cuttlefish)
822 let policies = containerMO.policies as? Set<PolicyMO>
823 policies?.forEach { policyMO in
824 if let policyHash = policyMO.policyHash,
825 let policyData = policyMO.policyData {
826 if let policyDoc = TPPolicyDocument.policyDoc(withHash: policyHash, data: policyData) {
827 model.register(policyDoc)
828 }
829 }
830 }
831
832 // Register built-in policies
833 builtInPolicyDocuments().forEach { policyDoc in
834 model.register(policyDoc)
835 }
836
837 let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
838 let allowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.allowed.rawValue }.compactMap { $0.machineID })
839 let disallowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.disallowed.rawValue }.compactMap { $0.machineID })
840
841 os_log("loadModel: allowedMachineIDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
842 os_log("loadModel: disallowedMachineIDs: %{public}@", log: tplogDebug, type: .default, disallowedMachineIDs)
843
844 if allowedMachineIDs.isEmpty {
845 os_log("loadModel: no allowedMachineIDs?", log: tplogDebug, type: .default)
846 }
847
848 return model
849 }
850
851 // Must be on containerMO's moc queue to call this
852 internal static func ensureEgoConsistency(from containerMO: ContainerMO, model: TPModel) {
853 guard let egoPeerID = containerMO.egoPeerID,
854 let egoStableData = containerMO.egoPeerStableInfo,
855 let egoStableSig = containerMO.egoPeerStableInfoSig
856 else {
857 os_log("ensureEgoConsistency failed to find ego peer information", log: tplogDebug, type: .error)
858 return
859 }
860
861 guard let containerEgoStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
862 os_log("ensureEgoConsistency failed to create TPPeerStableInfo from container", log: tplogDebug, type: .error)
863 return
864 }
865
866 guard let modelStableInfo = model.getStableInfoForPeer(withID: egoPeerID) else {
867 os_log("ensureEgoConsistency failed to create TPPeerStableInfo from model", log: tplogDebug, type: .error)
868 return
869 }
870
871 if modelStableInfo.clock > containerEgoStableInfo.clock {
872 containerMO.egoPeerStableInfo = modelStableInfo.data
873 containerMO.egoPeerStableInfoSig = modelStableInfo.sig
874 }
875
876 }
877
878 static func dictionaryRepresentation(bottle: BottleMO) -> [String: Any] {
879 var dict: [String: String] = [:]
880
881 dict["bottleID"] = bottle.bottleID
882 dict["peerID"] = bottle.peerID
883 dict["signingSPKI"] = bottle.escrowedSigningSPKI?.base64EncodedString()
884 dict["signatureUsingPeerKey"] = bottle.signatureUsingPeerKey?.base64EncodedString()
885 dict["signatureUsingSPKI"] = bottle.signatureUsingEscrowKey?.base64EncodedString()
886 // ignore the bottle contents; they're mostly unreadable
887
888 return dict
889 }
890
891 static func peerdictionaryRepresentation(peer: TPPeer) -> [String: Any] {
892 var peerDict: [String: Any] = [
893 "permanentInfo": peer.permanentInfo.dictionaryRepresentation(),
894 "peerID": peer.peerID,
895 ]
896 if let stableInfo = peer.stableInfo {
897 peerDict["stableInfo"] = stableInfo.dictionaryRepresentation()
898 }
899 if let dynamicInfo = peer.dynamicInfo {
900 peerDict["dynamicInfo"] = dynamicInfo.dictionaryRepresentation()
901 }
902
903 return peerDict
904 }
905
906 func onQueueDetermineLocalTrustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
907 let viablePeerCountsByModelID = self.model.viablePeerCountsByModelID()
908 let peerCountsByMachineID = self.model.peerCountsByMachineID()
909
910 if let egoPeerID = self.containerMO.egoPeerID {
911 var status = self.model.statusOfPeer(withID: egoPeerID)
912 var isExcluded: Bool = (status == .excluded)
913
914 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, loadError in
915 var returnError = loadError
916
917 guard returnError == nil else {
918 var isLocked = false
919 if let error = (loadError as NSError?) {
920 os_log("trust status: Unable to load ego keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
921 if error.code == errSecItemNotFound && error.domain == NSOSStatusErrorDomain {
922 os_log("trust status: Lost the ego key pair, returning 'excluded' in hopes of fixing up the identity", log: tplogDebug, type: .debug)
923 isExcluded = true
924 status = .excluded
925 } else if error.code == errSecInteractionNotAllowed && error.domain == NSOSStatusErrorDomain {
926 // we have an item, but can't read it, suppressing error
927 isLocked = true
928 returnError = nil
929 }
930 }
931
932 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
933 status: status,
934 viablePeerCountsByModelID: viablePeerCountsByModelID,
935 peerCountsByMachineID: peerCountsByMachineID,
936 isExcluded: isExcluded,
937 isLocked: isLocked)
938 reply(egoStatus, returnError)
939 return
940 }
941
942 //ensure egoPeerKeys are populated
943 guard egoPeerKeys != nil else {
944 os_log("trust status: No error but Ego Peer Keys are nil", log: tplogDebug, type: .default)
945 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
946 status: .excluded,
947 viablePeerCountsByModelID: viablePeerCountsByModelID,
948 peerCountsByMachineID: peerCountsByMachineID,
949 isExcluded: true,
950 isLocked: false)
951
952 reply(egoStatus, loadError)
953 return
954 }
955
956 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
957 status: status,
958 viablePeerCountsByModelID: viablePeerCountsByModelID,
959 peerCountsByMachineID: peerCountsByMachineID,
960 isExcluded: isExcluded,
961 isLocked: false)
962 reply(egoStatus, nil)
963 return
964 }
965
966 } else {
967 // With no ego peer ID, either return 'excluded' if there are extant peers, or 'unknown' to signal no peers at all
968 if self.model.allPeerIDs().isEmpty {
969 os_log("No existing peers in account", log: tplogDebug, type: .debug)
970 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
971 status: .unknown,
972 viablePeerCountsByModelID: viablePeerCountsByModelID,
973 peerCountsByMachineID: peerCountsByMachineID,
974 isExcluded: false,
975 isLocked: false)
976 reply(egoStatus, nil)
977 return
978 } else {
979 os_log("Existing peers in account, but we don't have a peer ID. We are excluded.", log: tplogDebug, type: .debug)
980 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
981 status: .excluded,
982 viablePeerCountsByModelID: viablePeerCountsByModelID,
983 peerCountsByMachineID: peerCountsByMachineID,
984 isExcluded: true,
985 isLocked: false)
986 reply(egoStatus, nil)
987 return
988 }
989 }
990 }
991
992 func trustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
993 self.semaphore.wait()
994 let reply: (TrustedPeersHelperEgoPeerStatus, Error?) -> Void = {
995 // Suppress logging of successful replies here; it's not that useful
996 let logType: OSLogType = $1 == nil ? .debug : .info
997 os_log("trustStatus complete: %{public}@ %{public}@",
998 log: tplogTrace, type: logType, TPPeerStatusToString($0.egoStatus), traceError($1))
999
1000 self.semaphore.signal()
1001 reply($0, $1)
1002 }
1003 self.moc.performAndWait {
1004 // Knowledge of your peer status only exists if you know about other peers. If you haven't fetched, fetch.
1005 if self.containerMO.changeToken == nil {
1006 self.fetchAndPersistChanges { fetchError in
1007 guard fetchError == nil else {
1008 if let error = fetchError {
1009 os_log("Unable to fetch changes, trust status is unknown: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1010 }
1011
1012 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
1013 status: .unknown,
1014 viablePeerCountsByModelID: [:],
1015 peerCountsByMachineID: [:],
1016 isExcluded: false,
1017 isLocked: false)
1018 reply(egoStatus, fetchError)
1019 return
1020 }
1021
1022 self.moc.performAndWait {
1023 self.onQueueDetermineLocalTrustStatus(reply: reply)
1024 }
1025 }
1026 } else {
1027 self.onQueueDetermineLocalTrustStatus(reply: reply)
1028 }
1029 }
1030 }
1031
1032 func fetchTrustState(reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
1033 let reply: (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void = {
1034 os_log("fetch trust state complete: %{public}@ %{public}@",
1035 log: tplogTrace, type: .info, String(reflecting: $0), traceError($2))
1036 reply($0, $1, $2)
1037 }
1038
1039 self.moc.performAndWait {
1040 if let egoPeerID = self.containerMO.egoPeerID,
1041 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1042 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig {
1043
1044 let keyFactory = TPECPublicKeyFactory()
1045 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1046 os_log("fetchTrustState failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
1047 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
1048 return
1049 }
1050
1051 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(permanentInfo.signingPubKey.spki())
1052 os_log("fetchTrustState: ego peer is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
1053
1054 let egoStableInfo = self.model.getStableInfoForPeer(withID: egoPeerID)
1055
1056 let egoPeerStatus = TrustedPeersHelperPeerState(peerID: egoPeerID,
1057 isPreapproved: isPreapproved,
1058 status: self.model.statusOfPeer(withID: egoPeerID),
1059 memberChanges: false,
1060 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
1061 osVersion: egoStableInfo?.osVersion)
1062
1063 var tphPeers: [TrustedPeersHelperPeer] = []
1064
1065 if let egoPeer = self.model.peer(withID: egoPeerID) {
1066 egoPeer.trustedPeerIDs.forEach { trustedPeerID in
1067 if let peer = self.model.peer(withID: trustedPeerID) {
1068 let peerViews = try? self.model.getViewsForPeer(peer.permanentInfo,
1069 stableInfo: peer.stableInfo)
1070
1071 tphPeers.append(TrustedPeersHelperPeer(peerID: trustedPeerID,
1072 signingSPKI: peer.permanentInfo.signingPubKey.spki(),
1073 encryptionSPKI: peer.permanentInfo.encryptionPubKey.spki(),
1074 viewList: peerViews ?? Set()))
1075 } else {
1076 os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
1077 }
1078 }
1079 } else {
1080 os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
1081 }
1082
1083 os_log("Returning trust state: %{public}@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
1084 reply(egoPeerStatus, tphPeers, nil)
1085 } else {
1086 // With no ego peer ID, there are no trusted peers
1087 os_log("No peer ID => no trusted peers", log: tplogDebug, type: .debug)
1088 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: false, unknownMachineIDs: false, osVersion: nil), [], nil)
1089 }
1090 }
1091 }
1092
1093 func dump(reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
1094 let reply: ([AnyHashable: Any]?, Error?) -> Void = {
1095 os_log("dump complete: %{public}@",
1096 log: tplogTrace, type: .info, traceError($1))
1097 reply($0, $1)
1098 }
1099 self.moc.performAndWait {
1100 var d: [AnyHashable: Any] = [:]
1101
1102 if let egoPeerID = self.containerMO.egoPeerID {
1103 if let peer = self.model.peer(withID: egoPeerID) {
1104 d["self"] = Container.peerdictionaryRepresentation(peer: peer)
1105 } else {
1106 d["self"] = ["peerID": egoPeerID]
1107 }
1108 } else {
1109 d["self"] = [:]
1110 }
1111
1112 d["peers"] = self.model.allPeers().filter { $0.peerID != self.containerMO.egoPeerID }.map { peer in
1113 Container.peerdictionaryRepresentation(peer: peer)
1114 }
1115
1116 d["vouchers"] = self.model.allVouchers().map { $0.dictionaryRepresentation() }
1117
1118 if let bottles = self.containerMO.bottles as? Set<BottleMO> {
1119 d["bottles"] = bottles.map { Container.dictionaryRepresentation(bottle: $0) }
1120 } else {
1121 d["bottles"] = []
1122 }
1123
1124 let midList = self.onqueueCurrentMIDList()
1125 d["machineIDsAllowed"] = midList.machineIDs(in: .allowed).sorted()
1126 d["machineIDsDisallowed"] = midList.machineIDs(in: .disallowed).sorted()
1127 d["modelRecoverySigningPublicKey"] = self.model.recoverySigningPublicKey()
1128 d["modelRecoveryEncryptionPublicKey"] = self.model.recoveryEncryptionPublicKey()
1129
1130 reply(d, nil)
1131 }
1132 }
1133
1134 func dumpEgoPeer(reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
1135 let reply: (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void = {
1136 os_log("dumpEgoPeer complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
1137 reply($0, $1, $2, $3, $4)
1138 }
1139 self.moc.performAndWait {
1140 guard let egoPeerID = self.containerMO.egoPeerID else {
1141 reply(nil, nil, nil, nil, ContainerError.noPreparedIdentity)
1142 return
1143 }
1144
1145 guard let peer = self.model.peer(withID: egoPeerID) else {
1146 reply(egoPeerID, nil, nil, nil, nil)
1147 return
1148 }
1149
1150 reply(egoPeerID, peer.permanentInfo, peer.stableInfo, peer.dynamicInfo, nil)
1151 }
1152 }
1153
1154 func validatePeers(request: ValidatePeersRequest, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
1155 self.semaphore.wait()
1156 let reply: ([AnyHashable: Any]?, Error?) -> Void = {
1157 os_log("validatePeers complete %{public}@", log: tplogTrace, type: .info, traceError($1))
1158 self.semaphore.signal()
1159 reply($0, $1)
1160 }
1161
1162 self.cuttlefish.validatePeers(request) { response, error in
1163 os_log("ValidatePeers(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1164 guard let response = response, error == nil else {
1165 os_log("validatePeers failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1166 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
1167 return
1168 }
1169
1170 var info: [AnyHashable: Any] = [:]
1171 info["health"] = response.validatorsHealth as AnyObject
1172 info["results"] = try? JSONSerialization.jsonObject(with: response.jsonUTF8Data())
1173
1174 reply(info, nil)
1175 }
1176 }
1177
1178 func reset(resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
1179 self.semaphore.wait()
1180 let reply: (Error?) -> Void = {
1181 os_log("reset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
1182 self.semaphore.signal()
1183 reply($0)
1184 }
1185
1186 self.moc.performAndWait {
1187 let resetReason = ResetReason.from(cuttlefishResetReason: resetReason)
1188 let request = ResetRequest.with {
1189 $0.resetReason = resetReason
1190 }
1191 self.cuttlefish.reset(request) { response, error in
1192 os_log("Reset(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1193 guard let response = response, error == nil else {
1194 os_log("reset failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1195 reply(error ?? ContainerError.cloudkitResponseMissing)
1196 return
1197 }
1198
1199 // Erase container's persisted state
1200 self.moc.performAndWait {
1201 self.moc.delete(self.containerMO)
1202 self.containerMO = ContainerMO(context: self.moc)
1203 self.containerMO.name = self.name.asSingleString()
1204 self.model = Container.loadModel(from: self.containerMO)
1205 do {
1206 try self.onQueuePersist(changes: response.changes)
1207 os_log("reset succeded", log: tplogDebug, type: .default)
1208 reply(nil)
1209 } catch {
1210 os_log("reset persist failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1211 reply(error)
1212 }
1213 }
1214 }
1215 }
1216 }
1217
1218 func localReset(reply: @escaping (Error?) -> Void) {
1219 self.semaphore.wait()
1220 let reply: (Error?) -> Void = {
1221 os_log("localReset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
1222 self.semaphore.signal()
1223 reply($0)
1224 }
1225
1226 self.moc.performAndWait {
1227 do {
1228 // Erase container's persisted state
1229 self.moc.delete(self.containerMO)
1230 self.containerMO = ContainerMO(context: self.moc)
1231 self.containerMO.name = self.name.asSingleString()
1232 self.model = Container.loadModel(from: self.containerMO)
1233 try self.moc.save()
1234 } catch {
1235 reply(error)
1236 return
1237 }
1238 reply(nil)
1239 }
1240 }
1241
1242 func loadOrCreateKeyPair(privateKeyPersistentRef: Data?) throws -> _SFECKeyPair {
1243 if let privateKeyPersistentRef = privateKeyPersistentRef {
1244 return try TPHObjectiveC.fetchKeyPair(withPrivateKeyPersistentRef: privateKeyPersistentRef)
1245 } else {
1246 let keySpecifier = _SFECKeySpecifier(curve: SFEllipticCurve.nistp384)
1247 guard let keyPair = _SFECKeyPair(randomKeyPairWith: keySpecifier) else {
1248 throw ContainerError.unableToCreateKeyPair
1249 }
1250 return keyPair
1251 }
1252 }
1253
1254 // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion.versionNumber
1255 func prepare(epoch: UInt64,
1256 machineID: String,
1257 bottleSalt: String,
1258 bottleID: String,
1259 modelID: String,
1260 deviceName: String?,
1261 serialNumber: String,
1262 osVersion: String,
1263 policyVersion: TPPolicyVersion?,
1264 policySecrets: [String: Data]?,
1265 signingPrivateKeyPersistentRef: Data?,
1266 encryptionPrivateKeyPersistentRef: Data?,
1267 reply: @escaping (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void) {
1268 self.semaphore.wait()
1269 let reply: (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void = {
1270 os_log("prepare complete peerID: %{public}@ %{public}@",
1271 log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($7))
1272 self.semaphore.signal()
1273 reply($0, $1, $2, $3, $4, $5, $6, $7)
1274 }
1275
1276 // Create a new peer identity with random keys, and store the keys in keychain
1277 let permanentInfo: TPPeerPermanentInfo
1278 let signingKeyPair: _SFECKeyPair
1279 let encryptionKeyPair: _SFECKeyPair
1280 do {
1281 signingKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: signingPrivateKeyPersistentRef)
1282 encryptionKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: encryptionPrivateKeyPersistentRef)
1283
1284 // <rdar://problem/56270219> Octagon: use epoch transmitted across pairing channel
1285 permanentInfo = try TPPeerPermanentInfo(machineID: machineID,
1286 modelID: modelID,
1287 epoch: 1,
1288 signing: signingKeyPair,
1289 encryptionKeyPair: encryptionKeyPair,
1290 peerIDHashAlgo: TPHashAlgo.SHA256)
1291
1292 } catch {
1293 reply(nil, nil, nil, nil, nil, nil, nil, error)
1294 return
1295 }
1296
1297 let peerID = permanentInfo.peerID
1298
1299 let bottle: BottledPeer
1300 do {
1301 bottle = try BottledPeer(peerID: peerID,
1302 bottleID: bottleID,
1303 peerSigningKey: signingKeyPair,
1304 peerEncryptionKey: encryptionKeyPair,
1305 bottleSalt: bottleSalt)
1306
1307 _ = try saveSecret(bottle.secret, label: peerID)
1308 } catch {
1309 os_log("bottle creation failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1310 reply(nil, nil, nil, nil, nil, nil, nil, error)
1311 return
1312 }
1313
1314 saveEgoKeyPair(signingKeyPair, identifier: signingKeyIdentifier(peerID: peerID)) { success, error in
1315 guard success else {
1316 os_log("Unable to save signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1317 reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
1318 return
1319 }
1320 saveEgoKeyPair(encryptionKeyPair, identifier: encryptionKeyIdentifier(peerID: peerID)) { success, error in
1321 guard success else {
1322 os_log("Unable to save encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1323 reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
1324 return
1325 }
1326
1327 let policyVersion = policyVersion ?? prevailingPolicyVersion
1328 self.fetchPolicyDocumentWithSemaphore(version: policyVersion) { policyDoc, policyFetchError in
1329 guard let policyDoc = policyDoc, policyFetchError == nil else {
1330 os_log("Unable to fetch policy: %{public}@", log: tplogDebug, type: .default, (policyFetchError as CVarArg?) ?? "error missing")
1331 reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.unknownInternalError)
1332 return
1333 }
1334
1335 // Save the prepared identity as containerMO.egoPeer* and its bottle
1336 self.moc.performAndWait {
1337 do {
1338
1339 let stableInfo = TPPeerStableInfo(clock: 1,
1340 frozenPolicyVersion: frozenPolicyVersion,
1341 flexiblePolicyVersion: policyDoc.version,
1342 policySecrets: policySecrets,
1343 deviceName: deviceName,
1344 serialNumber: serialNumber,
1345 osVersion: osVersion,
1346 signing: signingKeyPair,
1347 recoverySigningPubKey: nil,
1348 recoveryEncryptionPubKey: nil,
1349 error: nil)
1350
1351 self.containerMO.egoPeerID = permanentInfo.peerID
1352 self.containerMO.egoPeerPermanentInfo = permanentInfo.data
1353 self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
1354 self.containerMO.egoPeerStableInfo = stableInfo.data
1355 self.containerMO.egoPeerStableInfoSig = stableInfo.sig
1356
1357 let bottleMO = BottleMO(context: self.moc)
1358 bottleMO.peerID = bottle.peerID
1359 bottleMO.bottleID = bottle.bottleID
1360 bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
1361 bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
1362 bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
1363 bottleMO.contents = bottle.contents
1364
1365 self.containerMO.addToBottles(bottleMO)
1366
1367 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
1368
1369 try self.moc.save()
1370
1371 reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, syncingViews, policy, nil)
1372 } catch {
1373 os_log("Unable to save identity: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1374 reply(nil, nil, nil, nil, nil, nil, nil, error)
1375 }
1376 }
1377 }
1378 }
1379 }
1380 }
1381 func getEgoEpoch(reply: @escaping (UInt64, Error?) -> Void) {
1382 let reply: (UInt64, Error?) -> Void = {
1383 os_log("getEgoEpoch complete: %d %{public}@", log: tplogTrace, type: .info, $0, traceError($1))
1384 reply($0, $1)
1385 }
1386
1387 self.moc.performAndWait {
1388 guard let egoPeerID = self.containerMO.egoPeerID else {
1389 reply(0, ContainerError.noPreparedIdentity)
1390 return
1391 }
1392 guard let egoPeer = self.model.peer(withID: egoPeerID) else {
1393 reply(0, ContainerError.noPreparedIdentity)
1394 return
1395 }
1396
1397 reply(egoPeer.permanentInfo.epoch, nil)
1398 }
1399 }
1400 func establish(ckksKeys: [CKKSKeychainBackedKeySet],
1401 tlkShares: [CKKSTLKShare],
1402 preapprovedKeys: [Data]?,
1403 reply: @escaping (String?, [CKRecord], Error?) -> Void) {
1404 self.semaphore.wait()
1405 let reply: (String?, [CKRecord], Error?) -> Void = {
1406 os_log("establish complete peer: %{public}@ %{public}@",
1407 log: tplogTrace, type: .default, ($0 ?? "NULL") as CVarArg, traceError($2))
1408 self.semaphore.signal()
1409 reply($0, $1, $2)
1410 }
1411
1412 self.moc.performAndWait {
1413 self.onqueueEstablish(ckksKeys: ckksKeys,
1414 tlkShares: tlkShares,
1415 preapprovedKeys: preapprovedKeys,
1416 reply: { peerID, ckrecords, _, _, error in
1417 reply(peerID, ckrecords, error)
1418 })
1419 }
1420 }
1421
1422 func onqueueTTRUntrusted() {
1423 let ttr = SecTapToRadar(tapToRadar: "Device not IDMS trusted",
1424 description: "Device not IDMS trusted",
1425 radar: "52874119")
1426 ttr.trigger()
1427 }
1428
1429 func fetchAfterEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
1430 tlkShares: [CKKSTLKShare],
1431 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
1432 self.moc.performAndWait {
1433 do {
1434 try self.deleteLocalCloudKitData()
1435 } catch {
1436 os_log("fetchAfterEstablish failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1437 reply(nil, [], nil, nil, error)
1438 return
1439 }
1440 self.onqueueFetchAndPersistChanges { error in
1441 guard error == nil else {
1442 os_log("fetchAfterEstablish failed to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1443 reply(nil, [], nil, nil, error)
1444 return
1445 }
1446
1447 self.moc.performAndWait {
1448 guard let egoPeerID = self.containerMO.egoPeerID,
1449 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1450 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
1451 let egoStableData = self.containerMO.egoPeerStableInfo,
1452 let egoStableSig = self.containerMO.egoPeerStableInfoSig
1453 else {
1454 os_log("fetchAfterEstablish: failed to fetch egoPeerID", log: tplogDebug, type: .default)
1455 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
1456 return
1457 }
1458 guard self.model.hasPeer(withID: egoPeerID) else {
1459 os_log("fetchAfterEstablish: did not find peer %{public}@ in model", log: tplogDebug, type: .default, egoPeerID)
1460 reply(nil, [], nil, nil, ContainerError.invalidPeerID)
1461 return
1462 }
1463 let keyFactory = TPECPublicKeyFactory()
1464 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1465 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
1466 return
1467 }
1468 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
1469 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1470 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
1471 return
1472 }
1473 self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares) { ckrecords, error in
1474 guard error == nil else {
1475 os_log("fetchAfterEstablish failed to update TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1476 reply(nil, [], nil, nil, error)
1477 return
1478 }
1479
1480 do {
1481 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
1482 stableInfo: selfStableInfo)
1483 os_log("fetchAfterEstablish succeeded", log: tplogDebug, type: .default)
1484 reply(egoPeerID, ckrecords ?? [], syncingViews, policy, nil)
1485 } catch {
1486 os_log("fetchAfterEstablish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1487 reply(nil, [], nil, nil, error)
1488 }
1489 }
1490 }
1491 }
1492 }
1493 }
1494
1495 func onqueueEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
1496 tlkShares: [CKKSTLKShare],
1497 preapprovedKeys: [Data]?,
1498 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
1499 // Fetch ego peer identity from local storage.
1500 guard let egoPeerID = self.containerMO.egoPeerID,
1501 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1502 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
1503 let egoStableData = self.containerMO.egoPeerStableInfo,
1504 let egoStableSig = self.containerMO.egoPeerStableInfoSig
1505 else {
1506 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
1507 return
1508 }
1509
1510 let keyFactory = TPECPublicKeyFactory()
1511 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1512 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
1513 return
1514 }
1515 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
1516 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1517 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
1518 return
1519 }
1520 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
1521 os_log("establish: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
1522 self.onqueueTTRUntrusted()
1523 reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
1524 return
1525 }
1526
1527 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
1528 guard let egoPeerKeys = egoPeerKeys else {
1529 os_log("Don't have my own peer keys; can't establish: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1530 reply(nil, [], nil, nil, error)
1531 return
1532 }
1533 self.moc.performAndWait {
1534 let viewKeys: [ViewKeys] = ckksKeys.map(ViewKeys.convert)
1535 let allTLKShares: [TLKShare]
1536 do {
1537 let octagonShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk }, asPeer: egoPeerKeys, toPeer: egoPeerKeys, epoch: Int(selfPermanentInfo.epoch))
1538 let sosShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
1539
1540 allTLKShares = octagonShares + sosShares
1541 } catch {
1542 os_log("Unable to make TLKShares for self: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1543 reply(nil, [], nil, nil, error)
1544 return
1545 }
1546
1547 let dynamicInfo: TPPeerDynamicInfo
1548 do {
1549 dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
1550 peerPermanentInfo: selfPermanentInfo,
1551 peerStableInfo: selfStableInfo,
1552 sponsorID: nil,
1553 preapprovedKeys: preapprovedKeys,
1554 signing: egoPeerKeys.signingKey,
1555 currentMachineIDs: self.onqueueCurrentMIDList())
1556
1557 os_log("dynamic info: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
1558 } catch {
1559 reply(nil, [], nil, nil, error)
1560 return
1561 }
1562
1563 let peer = Peer.with {
1564 $0.peerID = egoPeerID
1565 $0.permanentInfoAndSig.peerPermanentInfo = egoPermData
1566 $0.permanentInfoAndSig.sig = egoPermSig
1567 $0.stableInfoAndSig.peerStableInfo = egoStableData
1568 $0.stableInfoAndSig.sig = egoStableSig
1569 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
1570 }
1571
1572 let bottle: Bottle
1573 do {
1574 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
1575 } catch {
1576 reply(nil, [], nil, nil, error)
1577 return
1578 }
1579 os_log("Beginning establish for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
1580 os_log("Establish permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
1581 os_log("Establish permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
1582 os_log("Establish stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
1583 os_log("Establish stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
1584 os_log("Establish dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
1585 os_log("Establish dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
1586
1587 os_log("Establish introducing %d key sets, %d tlk shares", log: tplogDebug, type: .default, viewKeys.count, allTLKShares.count)
1588
1589 do {
1590 os_log("Establish bottle: %{public}@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
1591 os_log("Establish peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
1592 } catch {
1593 os_log("Establish unable to encode bottle/peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
1594 }
1595
1596 let request = EstablishRequest.with {
1597 $0.peer = peer
1598 $0.bottle = bottle
1599 $0.viewKeys = viewKeys
1600 $0.tlkShares = allTLKShares
1601 }
1602 self.cuttlefish.establish(request) { response, error in
1603 os_log("Establish(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1604 os_log("Establish: viewKeys: %{public}@", String(describing: viewKeys))
1605 guard let response = response, error == nil else {
1606 switch error {
1607 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.establishFailed):
1608 os_log("establish returned failed, trying fetch", log: tplogDebug, type: .default)
1609 self.fetchAfterEstablish(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
1610 return
1611 default:
1612 os_log("establish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1613 reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
1614 return
1615 }
1616 }
1617
1618 do {
1619 os_log("Establish returned changes: %{public}@", log: tplogDebug, type: .default, try response.changes.jsonString())
1620 } catch {
1621 os_log("Establish returned changes, but they can't be serialized", log: tplogDebug, type: .default)
1622 }
1623
1624 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
1625
1626 do {
1627 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
1628 stableInfo: selfStableInfo)
1629
1630 try self.persist(changes: response.changes)
1631
1632 guard response.changes.more == false else {
1633 os_log("establish succeeded, but more changes need fetching...", log: tplogDebug, type: .default)
1634
1635 self.fetchAndPersistChanges { fetchError in
1636 guard fetchError == nil else {
1637 // This is an odd error condition: we might be able to fetch again and be in a good state...
1638 os_log("fetch-after-establish failed: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
1639 reply(nil, keyHierarchyRecords, nil, nil, fetchError)
1640 return
1641 }
1642
1643 os_log("fetch-after-establish succeeded", log: tplogDebug, type: .default)
1644 reply(egoPeerID, keyHierarchyRecords, nil, nil, nil)
1645 }
1646 return
1647 }
1648
1649 os_log("establish succeeded", log: tplogDebug, type: .default)
1650 reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
1651 } catch {
1652 os_log("establish handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1653 reply(nil, keyHierarchyRecords, nil, nil, error)
1654 }
1655 }
1656 }
1657 }
1658 }
1659
1660 func setRecoveryKey(recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
1661 self.semaphore.wait()
1662 let reply: (Error?) -> Void = {
1663 os_log("setRecoveryKey complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
1664 self.semaphore.signal()
1665 reply($0)
1666 }
1667
1668 os_log("beginning a setRecoveryKey", log: tplogDebug, type: .default)
1669
1670 self.moc.performAndWait {
1671 guard let egoPeerID = self.containerMO.egoPeerID else {
1672 os_log("no prepared identity, cannot set recovery key", log: tplogDebug, type: .default)
1673 reply(ContainerError.noPreparedIdentity)
1674 return
1675 }
1676
1677 var recoveryKeys: RecoveryKey
1678 do {
1679 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
1680 } catch {
1681 os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1682 reply(ContainerError.failedToCreateRecoveryKey)
1683 return
1684 }
1685
1686 let signingPublicKey: Data = recoveryKeys.peerKeys.signingVerificationKey.keyData
1687 let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionVerificationKey.keyData
1688
1689 os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
1690 os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
1691
1692 guard let stableInfoData = self.containerMO.egoPeerStableInfo else {
1693 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
1694 reply(ContainerError.nonMember)
1695 return
1696 }
1697 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
1698 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
1699 reply(ContainerError.nonMember)
1700 return
1701 }
1702 guard let permInfoData = self.containerMO.egoPeerPermanentInfo else {
1703 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
1704 reply(ContainerError.nonMember)
1705 return
1706 }
1707 guard let permInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
1708 os_log("permInfoSig does not exist", log: tplogDebug, type: .default)
1709 reply(ContainerError.nonMember)
1710 return
1711 }
1712 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
1713 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1714 reply(ContainerError.invalidStableInfoOrSig)
1715 return
1716 }
1717 let keyFactory = TPECPublicKeyFactory()
1718 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permInfoData, sig: permInfoSig, keyFactory: keyFactory) else {
1719 os_log("cannot create TPPeerPermanentInfo", log: tplogDebug, type: .default)
1720 reply(ContainerError.invalidStableInfoOrSig)
1721 return
1722 }
1723
1724 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
1725 guard let signingKeyPair = signingKeyPair else {
1726 os_log("handle: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1727 reply(error)
1728 return
1729 }
1730 self.moc.performAndWait {
1731 do {
1732 let tlkShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
1733 asPeer: recoveryKeys.peerKeys,
1734 toPeer: recoveryKeys.peerKeys,
1735 epoch: Int(permanentInfo.epoch))
1736
1737 let policyVersion = stableInfo.bestPolicyVersion()
1738 let policyDoc = try self.getPolicyDoc(policyVersion.versionNumber)
1739
1740 let updatedStableInfo = TPPeerStableInfo(clock: stableInfo.clock + 1,
1741 frozenPolicyVersion: frozenPolicyVersion,
1742 flexiblePolicyVersion: policyDoc.version,
1743 policySecrets: stableInfo.policySecrets,
1744 deviceName: stableInfo.deviceName,
1745 serialNumber: stableInfo.serialNumber,
1746 osVersion: stableInfo.osVersion,
1747 signing: signingKeyPair,
1748 recoverySigningPubKey: signingPublicKey,
1749 recoveryEncryptionPubKey: encryptionPublicKey,
1750 error: nil)
1751 let signedStableInfo = SignedPeerStableInfo(updatedStableInfo)
1752
1753 let request = SetRecoveryKeyRequest.with {
1754 $0.peerID = egoPeerID
1755 $0.recoverySigningPubKey = signingPublicKey
1756 $0.recoveryEncryptionPubKey = encryptionPublicKey
1757 $0.stableInfoAndSig = signedStableInfo
1758 $0.tlkShares = tlkShares
1759 $0.changeToken = self.containerMO.changeToken ?? ""
1760 }
1761
1762 self.cuttlefish.setRecoveryKey(request) { response, error in
1763 os_log("SetRecoveryKey(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1764 guard let response = response, error == nil else {
1765 os_log("setRecoveryKey failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1766 reply(error ?? ContainerError.cloudkitResponseMissing)
1767 return
1768 }
1769
1770 self.moc.performAndWait {
1771 do {
1772 self.containerMO.egoPeerStableInfo = updatedStableInfo.data
1773 self.containerMO.egoPeerStableInfoSig = updatedStableInfo.sig
1774 try self.onQueuePersist(changes: response.changes)
1775
1776 os_log("setRecoveryKey succeeded", log: tplogDebug, type: .default)
1777 reply(nil)
1778 } catch {
1779 os_log("setRecoveryKey handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1780 reply(error)
1781 }
1782 }
1783 }
1784 } catch {
1785 reply(error)
1786 }
1787 }
1788 }
1789 }
1790 }
1791
1792 func currentSetContainerBottleID(bottleMOs: Set<BottleMO>, bottleID: String) -> (Bool) {
1793 let bmos = bottleMOs.filter {
1794 $0.bottleID == bottleID
1795 }
1796 return !bmos.isEmpty
1797 }
1798
1799 func onqueueFindBottle(bottleID: String, reply: @escaping (BottleMO?, Error?) -> Void) {
1800
1801 var bmo: BottleMO?
1802 var bottles: Set<BottleMO> = []
1803 var shouldPerformFetch = false
1804
1805 if let containerBottles = self.containerMO.bottles as? Set<BottleMO> {
1806 if self.currentSetContainerBottleID(bottleMOs: containerBottles, bottleID: bottleID) == false {
1807 shouldPerformFetch = true
1808 } else {
1809 bottles = containerBottles
1810 }
1811 } else {
1812 shouldPerformFetch = true
1813 }
1814
1815 if shouldPerformFetch == true {
1816 self.fetchViableBottlesWithSemaphore { _, _, error in
1817 guard error == nil else {
1818 os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1819 reply(nil, error)
1820 return
1821 }
1822
1823 guard let newBottles = self.containerMO.bottles as? Set<BottleMO> else {
1824 os_log("no bottles on container: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1825 reply(nil, ContainerError.noBottlesPresent)
1826 return
1827 }
1828
1829 guard self.currentSetContainerBottleID(bottleMOs: newBottles, bottleID: bottleID) == true else {
1830 reply(nil, ContainerError.noBottlesForEscrowRecordID)
1831 return
1832 }
1833
1834 os_log("onqueueFindBottle found bottle: %{public}@", log: tplogDebug, type: .default, newBottles)
1835
1836 bottles = newBottles.filter {
1837 $0.bottleID == bottleID
1838 }
1839 if bottles.count > 1 {
1840 reply(nil, ContainerError.tooManyBottlesForPeer)
1841 return
1842 }
1843 bmo = bottles.removeFirst()
1844 reply(bmo, nil)
1845 }
1846 } else {
1847 var filteredBottles = bottles.filter {
1848 $0.bottleID == bottleID
1849 }
1850 if filteredBottles.count > 1 {
1851 reply(nil, ContainerError.tooManyBottlesForPeer)
1852 return
1853 }
1854 bmo = filteredBottles.removeFirst()
1855 reply(bmo, nil)
1856 }
1857 }
1858
1859 func onqueueRecoverBottle(managedBottle: BottleMO, entropy: Data, bottleSalt: String) throws -> BottledPeer {
1860 guard let bottledContents = managedBottle.contents else {
1861 throw ContainerError.bottleDoesNotContainContents
1862 }
1863 guard let signatureUsingEscrowKey = managedBottle.signatureUsingEscrowKey else {
1864 throw ContainerError.bottleDoesNotContainEscrowKeySignature
1865 }
1866
1867 guard let signatureUsingPeerKey = managedBottle.signatureUsingPeerKey else {
1868 throw ContainerError.bottleDoesNotContainerPeerKeySignature
1869 }
1870 guard let sponsorPeerID = managedBottle.peerID else {
1871 throw ContainerError.bottleDoesNotContainPeerID
1872 }
1873
1874 //verify bottle signature using peer
1875 do {
1876 guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
1877 os_log("recover bottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
1878 throw ContainerError.bottleCreatingPeerNotFound
1879 }
1880 guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
1881 os_log("recover bottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
1882 throw ContainerError.signatureVerificationFailed
1883 }
1884
1885 _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
1886 } catch {
1887 os_log("Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1888 throw ContainerError.failedToCreateBottledPeer
1889 }
1890
1891 do {
1892 return try BottledPeer(contents: bottledContents,
1893 secret: entropy,
1894 bottleSalt: bottleSalt,
1895 signatureUsingEscrow: signatureUsingEscrowKey,
1896 signatureUsingPeerKey: signatureUsingPeerKey)
1897 } catch {
1898 os_log("Creation of Bottled Peer failed with bottle salt: %@,\nAttempting with empty bottle salt", bottleSalt)
1899
1900 do {
1901 return try BottledPeer(contents: bottledContents,
1902 secret: entropy,
1903 bottleSalt: "",
1904 signatureUsingEscrow: signatureUsingEscrowKey,
1905 signatureUsingPeerKey: signatureUsingPeerKey)
1906 } catch {
1907 os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1908 throw ContainerError.failedToCreateBottledPeer
1909 }
1910 }
1911 }
1912
1913 func vouchWithBottle(bottleID: String,
1914 entropy: Data,
1915 bottleSalt: String,
1916 tlkShares: [CKKSTLKShare],
1917 reply: @escaping (Data?, Data?, Int64, Int64, Error?) -> Void) {
1918 self.semaphore.wait()
1919 let reply: (Data?, Data?, Int64, Int64, Error?) -> Void = {
1920 os_log("vouchWithBottle complete: %{public}@",
1921 log: tplogTrace, type: .info, traceError($4))
1922 self.semaphore.signal()
1923 reply($0, $1, $2, $3, $4)
1924 }
1925
1926 self.fetchAndPersistChangesIfNeeded { error in
1927 guard error == nil else {
1928 os_log("vouchWithBottle unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1929 reply(nil, nil, 0, 0, error)
1930 return
1931 }
1932
1933 self.onqueueFindBottle(bottleID: bottleID) { returnedBMO, error in
1934 self.moc.performAndWait {
1935 guard error == nil else {
1936 os_log("vouchWithBottle unable to find bottle for escrow record id: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1937 reply(nil, nil, 0, 0, error)
1938 return
1939 }
1940
1941 guard let bmo: BottleMO = returnedBMO else {
1942 os_log("vouchWithBottle bottle is nil: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1943 reply(nil, nil, 0, 0, error)
1944 return
1945 }
1946
1947 guard let bottledContents = bmo.contents else {
1948 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainContents)
1949 return
1950 }
1951 guard let signatureUsingEscrowKey = bmo.signatureUsingEscrowKey else {
1952 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainEscrowKeySignature)
1953 return
1954 }
1955
1956 guard let signatureUsingPeerKey = bmo.signatureUsingPeerKey else {
1957 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainerPeerKeySignature)
1958 return
1959 }
1960 guard let sponsorPeerID = bmo.peerID else {
1961 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainPeerID)
1962 return
1963 }
1964
1965 //verify bottle signature using peer
1966 do {
1967 guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
1968 os_log("vouchWithBottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
1969 reply(nil, nil, 0, 0, ContainerError.bottleCreatingPeerNotFound)
1970 return
1971 }
1972 guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
1973 os_log("vouchWithBottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
1974 reply(nil, nil, 0, 0, ContainerError.signatureVerificationFailed)
1975 return
1976 }
1977
1978 _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
1979 } catch {
1980 os_log("vouchWithBottle: Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1981 reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
1982 return
1983 }
1984
1985 //create bottled peer
1986 let bottledPeer: BottledPeer
1987 do {
1988 bottledPeer = try BottledPeer(contents: bottledContents,
1989 secret: entropy,
1990 bottleSalt: bottleSalt,
1991 signatureUsingEscrow: signatureUsingEscrowKey,
1992 signatureUsingPeerKey: signatureUsingPeerKey)
1993 } catch {
1994 os_log("Creation of Bottled Peer failed with bottle salt: %@,\nAttempting with empty bottle salt", bottleSalt)
1995
1996 do {
1997 bottledPeer = try BottledPeer(contents: bottledContents,
1998 secret: entropy,
1999 bottleSalt: "",
2000 signatureUsingEscrow: signatureUsingEscrowKey,
2001 signatureUsingPeerKey: signatureUsingPeerKey)
2002 } catch {
2003
2004 os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2005 reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
2006 return
2007 }
2008 }
2009
2010 os_log("Have a bottle for peer %{public}@", log: tplogDebug, type: .default, bottledPeer.peerID)
2011
2012 // Extract any TLKs we have been given
2013 let (uniqueTLKsRecovered, totalSharesRecovered) = extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys, model: self.model)
2014
2015 self.moc.performAndWait {
2016 // I must have an ego identity in order to vouch using bottle
2017 guard let egoPeerID = self.containerMO.egoPeerID else {
2018 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2019 reply(nil, nil, 0, 0, ContainerError.nonMember)
2020 return
2021 }
2022 guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
2023 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
2024 reply(nil, nil, 0, 0, ContainerError.nonMember)
2025 return
2026 }
2027 guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
2028 os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
2029 reply(nil, nil, 0, 0, ContainerError.nonMember)
2030 return
2031 }
2032 guard let stableInfo = self.containerMO.egoPeerStableInfo else {
2033 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
2034 reply(nil, nil, 0, 0, ContainerError.nonMember)
2035 return
2036 }
2037 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2038 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
2039 reply(nil, nil, 0, 0, ContainerError.nonMember)
2040 return
2041 }
2042 let keyFactory = TPECPublicKeyFactory()
2043 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2044 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2045 reply(nil, nil, 0, 0, ContainerError.invalidPermanentInfoOrSig)
2046 return
2047 }
2048 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2049 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2050 reply(nil, nil, 0, 0, ContainerError.invalidStableInfoOrSig)
2051 return
2052 }
2053
2054 do {
2055 let voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2056 stableInfo: beneficiaryStableInfo,
2057 withSponsorID: sponsorPeerID,
2058 reason: TPVoucherReason.restore,
2059 signing: bottledPeer.peerKeys.signingKey)
2060 reply(voucher.data, voucher.sig, uniqueTLKsRecovered, totalSharesRecovered, nil)
2061 return
2062 } catch {
2063 os_log("Error creating voucher with bottle: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2064 reply(nil, nil, 0, 0, error)
2065 return
2066 }
2067 }
2068 }
2069 }
2070 }
2071 }
2072
2073 func vouchWithRecoveryKey(recoveryKey: String,
2074 salt: String,
2075 tlkShares: [CKKSTLKShare],
2076 reply: @escaping (Data?, Data?, Error?) -> Void) {
2077 self.semaphore.wait()
2078 let reply: (Data?, Data?, Error?) -> Void = {
2079 os_log("vouchWithRecoveryKey complete: %{public}@",
2080 log: tplogTrace, type: .info, traceError($2))
2081 self.semaphore.signal()
2082 reply($0, $1, $2)
2083 }
2084
2085 self.moc.performAndWait {
2086 os_log("beginning a vouchWithRecoveryKey", log: tplogDebug, type: .default)
2087
2088 // I must have an ego identity in order to vouch using bottle
2089 guard let egoPeerID = self.containerMO.egoPeerID else {
2090 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2091 reply(nil, nil, ContainerError.nonMember)
2092 return
2093 }
2094 guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
2095 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
2096 reply(nil, nil, ContainerError.nonMember)
2097 return
2098 }
2099 guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
2100 os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
2101 reply(nil, nil, ContainerError.nonMember)
2102 return
2103 }
2104 guard let stableInfo = self.containerMO.egoPeerStableInfo else {
2105 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
2106 reply(nil, nil, ContainerError.nonMember)
2107 return
2108 }
2109 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2110 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
2111 reply(nil, nil, ContainerError.nonMember)
2112 return
2113 }
2114 let keyFactory = TPECPublicKeyFactory()
2115 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2116 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2117 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2118 return
2119 }
2120 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2121 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2122 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2123 return
2124 }
2125
2126 //create recovery key set
2127 var recoveryKeys: RecoveryKey
2128 do {
2129 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
2130 } catch {
2131 os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2132 reply(nil, nil, ContainerError.failedToCreateRecoveryKey)
2133 return
2134 }
2135
2136 extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys, model: self.model)
2137
2138 let signingPublicKey: Data = recoveryKeys.peerKeys.signingKey.publicKey.keyData
2139 let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionKey.publicKey.keyData
2140
2141 os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
2142 os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
2143
2144 guard self.model.isRecoveryKeyEnrolled() else {
2145 os_log("Recovery Key is not enrolled", log: tplogDebug, type: .default)
2146 reply(nil, nil, ContainerError.recoveryKeysNotEnrolled)
2147 return
2148 }
2149
2150 //find matching peer containing recovery keys
2151 guard let sponsorPeerID = self.model.peerIDThatTrustsRecoveryKeys(TPRecoveryKeyPair(signingSPKI: signingPublicKey, encryptionSPKI: encryptionPublicKey)) else {
2152 os_log("Untrusted recovery key set", log: tplogDebug, type: .default)
2153 reply(nil, nil, ContainerError.untrustedRecoveryKeys)
2154 return
2155 }
2156
2157 do {
2158 let voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2159 stableInfo: beneficiaryStableInfo,
2160 withSponsorID: sponsorPeerID,
2161 reason: TPVoucherReason.recoveryKey,
2162 signing: recoveryKeys.peerKeys.signingKey)
2163 reply(voucher.data, voucher.sig, nil)
2164 return
2165 } catch {
2166 os_log("Error creating voucher using recovery key set: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2167 reply(nil, nil, error)
2168 return
2169 }
2170 }
2171 }
2172
2173 func vouch(peerID: String,
2174 permanentInfo: Data,
2175 permanentInfoSig: Data,
2176 stableInfo: Data,
2177 stableInfoSig: Data,
2178 ckksKeys: [CKKSKeychainBackedKeySet],
2179 reply: @escaping (Data?, Data?, Error?) -> Void) {
2180 self.semaphore.wait()
2181 let reply: (Data?, Data?, Error?) -> Void = {
2182 os_log("vouch complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2183 self.semaphore.signal()
2184 reply($0, $1, $2)
2185 }
2186
2187 self.moc.performAndWait {
2188 // I must have an ego identity in order to vouch for someone else.
2189 guard let egoPeerID = self.containerMO.egoPeerID,
2190 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2191 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
2192 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2193 reply(nil, nil, ContainerError.nonMember)
2194 return
2195 }
2196
2197 let keyFactory = TPECPublicKeyFactory()
2198
2199 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2200 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2201 return
2202 }
2203
2204 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: peerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2205 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2206 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2207 return
2208 }
2209
2210 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2211 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2212 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2213 return
2214 }
2215
2216 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
2217 guard let egoPeerKeys = egoPeerKeys else {
2218 os_log("Don't have my own keys: can't vouch for %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, (error as CVarArg?) ?? "no error")
2219 reply(nil, nil, error)
2220 return
2221 }
2222
2223 self.fetchPolicyDocumentsWithSemaphore(versions: Set([beneficiaryStableInfo.bestPolicyVersion()])) { _, policyFetchError in
2224 guard policyFetchError == nil else {
2225 os_log("Unknown policy for beneficiary: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2226 reply(nil, nil, policyFetchError)
2227 return
2228 }
2229
2230 self.moc.performAndWait {
2231 let voucher: TPVoucher
2232 do {
2233 voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2234 stableInfo: beneficiaryStableInfo,
2235 withSponsorID: egoPeerID,
2236 reason: TPVoucherReason.secureChannel,
2237 signing: egoPeerKeys.signingKey)
2238 } catch {
2239 os_log("Error creating voucher: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2240 reply(nil, nil, error)
2241 return
2242 }
2243
2244 // And generate and upload any tlkShares
2245 let tlkShares: [TLKShare]
2246 do {
2247 // Note that this might not be the whole list, so filter some of them out
2248 let peerViews = try? self.model.getViewsForPeer(beneficiaryPermanentInfo,
2249 stableInfo: beneficiaryStableInfo)
2250
2251 // Note: we only want to send up TLKs for uploaded ckks zones
2252 let ckksTLKs = ckksKeys
2253 .filter { !$0.newUpload }
2254 .filter { peerViews?.contains($0.tlk.zoneID.zoneName) ?? false }
2255 .map { $0.tlk }
2256
2257 tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
2258 asPeer: egoPeerKeys,
2259 toPeer: beneficiaryPermanentInfo,
2260 epoch: Int(selfPermanentInfo.epoch))
2261 } catch {
2262 os_log("Unable to make TLKShares for beneficiary %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, error as CVarArg)
2263 reply(nil, nil, error)
2264 return
2265 }
2266
2267 guard !tlkShares.isEmpty else {
2268 os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
2269 reply(voucher.data, voucher.sig, nil)
2270 return
2271 }
2272
2273 self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
2274 peerID: egoPeerID,
2275 stableInfoAndSig: nil,
2276 dynamicInfoAndSig: nil,
2277 tlkShares: tlkShares,
2278 viewKeys: []) { response, error in
2279 guard let response = response, error == nil else {
2280 os_log("Unable to upload new tlkshares: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
2281 reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
2282 return
2283 }
2284
2285 let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
2286 os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
2287 // We don't need to save these; CKKS will refetch them as needed
2288
2289 reply(voucher.data, voucher.sig, nil)
2290 }
2291 }
2292 }
2293 }
2294 }
2295 }
2296
2297 func departByDistrustingSelf(reply: @escaping (Error?) -> Void) {
2298 self.semaphore.wait()
2299 let reply: (Error?) -> Void = {
2300 os_log("departByDistrustingSelf complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
2301 self.semaphore.signal()
2302 reply($0)
2303 }
2304
2305 self.moc.performAndWait {
2306 guard let egoPeerID = self.containerMO.egoPeerID else {
2307 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2308 reply(ContainerError.noPreparedIdentity)
2309 return
2310 }
2311
2312 self.onqueueDistrust(peerIDs: [egoPeerID], reply: reply)
2313 }
2314 }
2315
2316 func distrust(peerIDs: Set<String>,
2317 reply: @escaping (Error?) -> Void) {
2318 self.semaphore.wait()
2319 let reply: (Error?) -> Void = {
2320 os_log("distrust complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
2321 self.semaphore.signal()
2322 reply($0)
2323 }
2324
2325 self.moc.performAndWait {
2326 guard let egoPeerID = self.containerMO.egoPeerID else {
2327 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2328 reply(ContainerError.noPreparedIdentity)
2329 return
2330 }
2331
2332 guard !peerIDs.contains(egoPeerID) else {
2333 os_log("Self-distrust via peerID not allowed", log: tplogDebug, type: .default)
2334 reply(ContainerError.invalidPeerID)
2335 return
2336 }
2337
2338 self.onqueueDistrust(peerIDs: peerIDs, reply: reply)
2339 }
2340 }
2341
2342 func onqueueDistrust(peerIDs: Set<String>,
2343 reply: @escaping (Error?) -> Void) {
2344
2345 guard let egoPeerID = self.containerMO.egoPeerID else {
2346 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2347 reply(ContainerError.noPreparedIdentity)
2348 return
2349 }
2350
2351 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
2352 guard let signingKeyPair = signingKeyPair else {
2353 os_log("No longer have signing key pair; can't sign distrust: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
2354 reply(error)
2355 return
2356 }
2357
2358 self.moc.performAndWait {
2359 let dynamicInfo: TPPeerDynamicInfo
2360 do {
2361 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
2362 addingPeerIDs: nil,
2363 removingPeerIDs: Array(peerIDs),
2364 preapprovedKeys: nil,
2365 signing: signingKeyPair,
2366 currentMachineIDs: self.onqueueCurrentMIDList())
2367
2368 } catch {
2369 os_log("Error preparing dynamic info: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
2370 reply(error)
2371 return
2372 }
2373
2374 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
2375 os_log("attempting distrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
2376
2377 let request = UpdateTrustRequest.with {
2378 $0.changeToken = self.containerMO.changeToken ?? ""
2379 $0.peerID = egoPeerID
2380 $0.dynamicInfoAndSig = signedDynamicInfo
2381 }
2382 self.cuttlefish.updateTrust(request) { response, error in
2383 os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2384 guard let response = response, error == nil else {
2385 os_log("updateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2386 reply(error ?? ContainerError.cloudkitResponseMissing)
2387 return
2388 }
2389
2390 do {
2391 try self.persist(changes: response.changes)
2392 os_log("distrust succeeded", log: tplogDebug, type: .default)
2393 reply(nil)
2394 } catch {
2395 os_log("distrust handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
2396 reply(error)
2397 }
2398 }
2399 }
2400 }
2401 }
2402
2403 func fetchEscrowContents(reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
2404 self.semaphore.wait()
2405 let reply: (Data?, String?, Data?, Error?) -> Void = {
2406 os_log("fetchEscrowContents complete: %{public}@", log: tplogTrace, type: .info, traceError($3))
2407 self.semaphore.signal()
2408 reply($0, $1, $2, $3)
2409 }
2410 os_log("beginning a fetchEscrowContents", log: tplogDebug, type: .default)
2411
2412 self.moc.performAndWait {
2413 guard let egoPeerID = self.containerMO.egoPeerID else {
2414 os_log("fetchEscrowContents failed", log: tplogDebug, type: .default)
2415 reply(nil, nil, nil, ContainerError.noPreparedIdentity)
2416 return
2417 }
2418
2419 guard let bottles = self.containerMO.bottles as? Set<BottleMO> else {
2420 os_log("fetchEscrowContents failed", log: tplogDebug, type: .default)
2421 reply(nil, nil, nil, ContainerError.noBottleForPeer)
2422 return
2423 }
2424
2425 var bmoSet = bottles.filter { $0.peerID == egoPeerID }
2426 let bmo = bmoSet.removeFirst()
2427 let bottleID = bmo.bottleID
2428 var entropy: Data
2429
2430 do {
2431 guard let loaded = try loadSecret(label: egoPeerID) else {
2432 os_log("fetchEscrowContents failed to load entropy", log: tplogDebug, type: .default)
2433 reply(nil, nil, nil, ContainerError.failedToFetchEscrowContents)
2434 return
2435 }
2436 entropy = loaded
2437 } catch {
2438 os_log("fetchEscrowContents failed to load entropy: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2439 reply(nil, nil, nil, error)
2440 return
2441 }
2442
2443 guard let signingPublicKey = bmo.escrowedSigningSPKI else {
2444 os_log("fetchEscrowContents no escrow signing spki", log: tplogDebug, type: .default)
2445 reply(nil, nil, nil, ContainerError.bottleDoesNotContainerEscrowKeySPKI)
2446 return
2447 }
2448 reply(entropy, bottleID, signingPublicKey, nil)
2449 }
2450 }
2451
2452 func fetchViableBottles(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
2453 self.semaphore.wait()
2454 let reply: ([String]?, [String]?, Error?) -> Void = {
2455 os_log("fetchViableBottles complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2456 self.semaphore.signal()
2457 reply($0, $1, $2)
2458 }
2459
2460 self.fetchViableBottlesWithSemaphore(reply: reply)
2461 }
2462
2463 func onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: TPCachedViableBottles) -> Bool {
2464 guard let egoPeerID = self.containerMO.egoPeerID else {
2465 os_log("bottleForEgoPeer: No identity.", log: tplogDebug, type: .default)
2466 return false
2467 }
2468 guard let bottles: Set<BottleMO> = self.containerMO.bottles as? Set<BottleMO> else {
2469 os_log("bottleForEgoPeer: No Bottles.", log: tplogDebug, type: .default)
2470 return false
2471 }
2472 var matchesCached: Bool = false
2473 for bottle in bottles {
2474 guard let bottleID: String = bottle.bottleID else {
2475 continue
2476 }
2477 if bottle.peerID == egoPeerID && (cachedBottles.viableBottles.contains(bottleID) || cachedBottles.partialBottles.contains(bottleID)) {
2478 matchesCached = true
2479 break
2480 }
2481 }
2482 return matchesCached
2483 }
2484
2485 func fetchViableBottlesWithSemaphore(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
2486 os_log("beginning a fetchViableBottles", log: tplogDebug, type: .default)
2487
2488 let cachedBottles: TPCachedViableBottles = self.model.currentCachedViableBottlesSet()
2489 self.moc.performAndWait {
2490 if self.onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: cachedBottles)
2491 && (!cachedBottles.viableBottles.isEmpty || !cachedBottles.partialBottles.isEmpty) {
2492 os_log("returning from fetchViableBottles, using cached bottles", log: tplogDebug, type: .default)
2493 reply(cachedBottles.viableBottles, cachedBottles.partialBottles, nil)
2494 return
2495 }
2496
2497 self.cuttlefish.fetchViableBottles { response, error in
2498 guard error == nil else {
2499 os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2500 reply(nil, nil, error)
2501 return
2502 }
2503
2504 self.moc.performAndWait {
2505
2506 guard let escrowPairs = response?.viableBottles else {
2507 os_log("fetchViableBottles returned no viable bottles", log: tplogDebug, type: .default)
2508 reply([], [], nil)
2509 return
2510 }
2511
2512 var partialPairs: [EscrowPair] = []
2513 if let partial = response?.partialBottles {
2514 partialPairs = partial
2515 } else {
2516 os_log("fetchViableBottles returned no partially viable bottles, but that's ok", log: tplogDebug, type: .default)
2517 }
2518
2519 let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
2520 os_log("fetchViableBottles returned viable bottles: %{public}@", log: tplogDebug, type: .default, viableBottleIDs)
2521
2522 let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
2523 os_log("fetchViableBottles returned partial bottles: %{public}@", log: tplogDebug, type: .default, partialBottleIDs)
2524
2525 escrowPairs.forEach { pair in
2526 let bottle = pair.bottle
2527
2528 // Save this bottle only if we don't already have it
2529 if let existingBottles = self.containerMO.bottles as? Set<BottleMO> {
2530 let matchingBottles: Set<BottleMO> = existingBottles.filter { existing in
2531 existing.peerID == bottle.peerID &&
2532 existing.bottleID == bottle.bottleID &&
2533 existing.escrowedSigningSPKI == bottle.escrowedSigningSpki &&
2534 existing.signatureUsingEscrowKey == bottle.signatureUsingEscrowKey &&
2535 existing.signatureUsingPeerKey == bottle.signatureUsingPeerKey &&
2536 existing.contents == bottle.contents
2537 }
2538 if !matchingBottles.isEmpty {
2539 os_log("fetchViableBottles already knows about bottle", log: tplogDebug, type: .default, bottle.bottleID)
2540 return
2541 }
2542 }
2543
2544 let bmo = BottleMO(context: self.moc)
2545 bmo.peerID = bottle.peerID
2546 bmo.bottleID = bottle.bottleID
2547 bmo.escrowedSigningSPKI = bottle.escrowedSigningSpki
2548 bmo.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
2549 bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
2550 bmo.contents = bottle.contents
2551
2552 os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
2553 self.containerMO.addToBottles(bmo)
2554 }
2555
2556 partialPairs.forEach { pair in
2557 let bottle = pair.bottle
2558
2559 // Save this bottle only if we don't already have it
2560 if let existingBottles = self.containerMO.bottles as? Set<BottleMO> {
2561 let matchingBottles: Set<BottleMO> = existingBottles.filter { existing in
2562 existing.peerID == bottle.peerID &&
2563 existing.bottleID == bottle.bottleID &&
2564 existing.escrowedSigningSPKI == bottle.escrowedSigningSpki &&
2565 existing.signatureUsingEscrowKey == bottle.signatureUsingEscrowKey &&
2566 existing.signatureUsingPeerKey == bottle.signatureUsingPeerKey &&
2567 existing.contents == bottle.contents
2568 }
2569 if !matchingBottles.isEmpty {
2570 os_log("fetchViableBottles already knows about bottle", log: tplogDebug, type: .default, bottle.bottleID)
2571 return
2572 }
2573 }
2574
2575 let bmo = BottleMO(context: self.moc)
2576 bmo.peerID = bottle.peerID
2577 bmo.bottleID = bottle.bottleID
2578 bmo.escrowedSigningSPKI = bottle.escrowedSigningSpki
2579 bmo.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
2580 bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
2581 bmo.contents = bottle.contents
2582
2583 os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
2584 self.containerMO.addToBottles(bmo)
2585 }
2586
2587 do {
2588 try self.moc.save()
2589 os_log("fetchViableBottles saved bottles", log: tplogDebug, type: .default)
2590 let cached = TPCachedViableBottles(viableBottles: viableBottleIDs, partialBottles: partialBottleIDs)
2591 self.model.setViableBottles(cached)
2592 reply(viableBottleIDs, partialBottleIDs, nil)
2593 } catch {
2594 os_log("fetchViableBottles unable to save bottles: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2595 reply(nil, nil, error)
2596 }
2597
2598 }
2599 }
2600 }
2601 }
2602
2603 func fetchCurrentPolicy(reply: @escaping (Set<String>?, TPPolicy?, Error?) -> Void) {
2604 self.semaphore.wait()
2605 let reply: (Set<String>?, TPPolicy?, Error?) -> Void = {
2606 os_log("fetchCurrentPolicy complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2607 self.semaphore.signal()
2608 reply($0, $1, $2)
2609 }
2610
2611 self.moc.performAndWait {
2612 guard let egoPeerID = self.containerMO.egoPeerID,
2613 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2614 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
2615 let stableInfoData = self.containerMO.egoPeerStableInfo,
2616 let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2617 os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
2618 reply(nil, nil, ContainerError.noPreparedIdentity)
2619 return
2620 }
2621
2622 let keyFactory = TPECPublicKeyFactory()
2623 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2624 os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
2625 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2626 return
2627 }
2628 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
2629 os_log("fetchCurrentPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
2630 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2631 return
2632 }
2633
2634 do {
2635 let (views, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
2636 reply(views, policy, nil)
2637 return
2638 } catch {
2639 os_log("TPPolicyDocument failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2640 reply(nil, nil, error)
2641 return
2642 }
2643 }
2644 }
2645
2646 func policyAndViewsFor(permanentInfo: TPPeerPermanentInfo, stableInfo: TPPeerStableInfo) throws -> (Set<String>, TPPolicy) {
2647 let policyVersion = stableInfo.bestPolicyVersion()
2648 guard let policyDocument = self.model.policy(withVersion: policyVersion.versionNumber) else {
2649 os_log("current policy is missing?", log: tplogDebug, type: .default)
2650 throw ContainerError.unknownPolicyVersion(policyVersion.versionNumber)
2651 }
2652
2653 let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
2654 let views = try policy.views(forModel: permanentInfo.modelID)
2655
2656 return (views, policy)
2657 }
2658
2659 // All-or-nothing: return an error in case full list cannot be returned.
2660 // Completion handler data format: [version : [hash, data]]
2661 func fetchPolicyDocuments(versions: Set<TPPolicyVersion>,
2662 reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
2663 self.semaphore.wait()
2664 let reply: ([TPPolicyVersion: Data]?, Error?) -> Void = {
2665 os_log("fetchPolicyDocuments complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
2666 self.semaphore.signal()
2667 reply($0, $1)
2668 }
2669
2670 self.fetchPolicyDocumentsWithSemaphore(versions: versions) { policyDocuments, fetchError in
2671 reply(policyDocuments.flatMap { $0.mapValues { policyDoc in policyDoc.protobuf } }, fetchError)
2672 }
2673 }
2674
2675 func fetchPolicyDocumentWithSemaphore(version: TPPolicyVersion,
2676 reply: @escaping (TPPolicyDocument?, Error?) -> Void) {
2677 self.fetchPolicyDocumentsWithSemaphore(versions: Set([version])) { versions, fetchError in
2678 guard fetchError == nil else {
2679 reply(nil, fetchError)
2680 return
2681 }
2682
2683 guard let doc = versions?[version] else {
2684 os_log("fetchPolicyDocument: didn't return policy of version: %{public}@", log: tplogDebug, versions ?? "no versions")
2685 reply(nil, ContainerError.unknownPolicyVersion(version.versionNumber))
2686 return
2687 }
2688
2689 reply(doc, nil)
2690 }
2691 }
2692
2693 func fetchPolicyDocumentsWithSemaphore(versions: Set<TPPolicyVersion>,
2694 reply: @escaping ([TPPolicyVersion: TPPolicyDocument]?, Error?) -> Void) {
2695 var remaining = versions
2696 var docs: [TPPolicyVersion: TPPolicyDocument] = [:]
2697
2698 self.moc.performAndWait {
2699 for version in remaining {
2700 if let policydoc = try? self.getPolicyDoc(version.versionNumber), policydoc.version.policyHash == version.policyHash {
2701 docs[policydoc.version] = policydoc
2702 remaining.remove(version)
2703 }
2704 }
2705 }
2706
2707 guard !remaining.isEmpty else {
2708 reply(docs, nil)
2709 return
2710 }
2711
2712 let request = FetchPolicyDocumentsRequest.with {
2713 $0.keys = remaining.map { version in
2714 PolicyDocumentKey.with { $0.version = version.versionNumber; $0.hash = version.policyHash }}
2715 }
2716
2717 self.cuttlefish.fetchPolicyDocuments(request) { response, error in
2718 os_log("FetchPolicyDocuments(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2719 guard let response = response, error == nil else {
2720 os_log("FetchPolicyDocuments failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2721 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
2722 return
2723 }
2724
2725 self.moc.performAndWait {
2726 for mapEntry in response.entries {
2727 // TODO: validate the policy's signature
2728
2729 guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
2730 os_log("Can't make policy document with hash %{public}@ and data %{public}@",
2731 log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
2732 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2733 return
2734 }
2735
2736 guard let expectedVersion = (remaining.filter { $0.versionNumber == doc.version.versionNumber }.first) else {
2737 os_log("Received a policy version we didn't request: %d", log: tplogDebug, type: .default, doc.version.versionNumber)
2738 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2739 return
2740 }
2741
2742 guard expectedVersion.policyHash == doc.version.policyHash else {
2743 os_log("Requested hash %{public}@ does not match fetched hash %{public}@", log: tplogDebug, type: .default,
2744 expectedVersion.policyHash, doc.version.policyHash)
2745 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2746 return
2747 }
2748
2749 remaining.remove(expectedVersion) // Server responses should be unique, let's enforce
2750
2751 docs[doc.version] = doc
2752 self.model.register(doc)
2753 }
2754
2755 do {
2756 try self.moc.save() // if this fails callers might make bad data assumptions
2757 } catch {
2758 reply(nil, error)
2759 return
2760 }
2761
2762 // Determine if there's anything left to fetch
2763 guard let unfetchedVersion = remaining.first else {
2764 // Nothing remaining? Success!
2765 reply(docs, nil)
2766 return
2767 }
2768
2769 reply(nil, ContainerError.unknownPolicyVersion(unfetchedVersion.versionNumber))
2770 }
2771 }
2772 }
2773
2774 // Must be on moc queue to call this.
2775 // Caller is responsible for saving the moc afterwards.
2776 @discardableResult
2777 private func registerPeerMO(permanentInfo: TPPeerPermanentInfo,
2778 stableInfo: TPPeerStableInfo? = nil,
2779 dynamicInfo: TPPeerDynamicInfo? = nil,
2780 vouchers: [TPVoucher]? = nil,
2781 isEgoPeer: Bool = false) throws -> PeerMO {
2782 let peerID = permanentInfo.peerID
2783
2784 let peer = PeerMO(context: self.moc)
2785 peer.peerID = peerID
2786 peer.permanentInfo = permanentInfo.data
2787 peer.permanentInfoSig = permanentInfo.sig
2788 peer.stableInfo = stableInfo?.data
2789 peer.stableInfoSig = stableInfo?.sig
2790 peer.dynamicInfo = dynamicInfo?.data
2791 peer.dynamicInfoSig = dynamicInfo?.sig
2792 peer.isEgoPeer = isEgoPeer
2793 self.containerMO.addToPeers(peer)
2794
2795 self.model.registerPeer(with: permanentInfo)
2796 if let stableInfo = stableInfo {
2797 try self.model.update(stableInfo, forPeerWithID: peerID)
2798 }
2799 if let dynamicInfo = dynamicInfo {
2800 try self.model.update(dynamicInfo, forPeerWithID: peerID)
2801 }
2802 vouchers?.forEach { voucher in
2803 self.model.register(voucher)
2804 let voucherMO = VoucherMO(context: self.moc)
2805 voucherMO.voucherInfo = voucher.data
2806 voucherMO.voucherInfoSig = voucher.sig
2807 peer.addToVouchers(voucherMO)
2808 }
2809 return peer
2810 }
2811
2812 /* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
2813 func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
2814 tlkShares: [CKKSTLKShare],
2815 egoPeerKeys: OctagonSelfPeerKeys,
2816 egoPeerDynamicInfo: TPPeerDynamicInfo,
2817 epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
2818 let newCKKSKeys = ckksKeys.filter { $0.newUpload }
2819 let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
2820
2821 let octagonSelfShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
2822 asPeer: egoPeerKeys,
2823 toPeer: egoPeerKeys,
2824 epoch: epoch)
2825 let extraShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
2826
2827 var peerShares: [TLKShare] = []
2828
2829 for keyset in newCKKSKeys {
2830 do {
2831 let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
2832 toAccessView: keyset.tlk.zoneID.zoneName)
2833 os_log("Planning to share %@ with peers %{public}@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
2834
2835 let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
2836 let viewPeerShares = try peers.map { receivingPeer in
2837 TLKShare.convert(ckksTLKShare: try CKKSTLKShare(keyset.tlk,
2838 as: egoPeerKeys,
2839 to: receivingPeer.permanentInfo,
2840 epoch: epoch,
2841 poisoned: 0))
2842 }
2843
2844 peerShares += viewPeerShares
2845
2846 } catch {
2847 os_log("Unable to create TLKShares for keyset %@: %{public}@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
2848 }
2849 }
2850
2851 return (newViewKeys, octagonSelfShares + peerShares + extraShares)
2852 }
2853
2854 func onqueuePreparePeerForJoining(egoPeerID: String,
2855 peerPermanentInfo: TPPeerPermanentInfo,
2856 stableInfo: TPPeerStableInfo,
2857 sponsorID: String?,
2858 preapprovedKeys: [Data]?,
2859 vouchers: [SignedVoucher],
2860 egoPeerKeys: OctagonSelfPeerKeys) throws -> (Peer, TPPeerDynamicInfo) {
2861 let dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
2862 peerPermanentInfo: peerPermanentInfo,
2863 peerStableInfo: stableInfo,
2864 sponsorID: sponsorID,
2865 preapprovedKeys: preapprovedKeys,
2866 signing: egoPeerKeys.signingKey,
2867 currentMachineIDs: self.onqueueCurrentMIDList())
2868
2869 let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: nil,
2870 egoPeerID: egoPeerID,
2871 existingStableInfo: stableInfo,
2872 dynamicInfo: dynamicInfo,
2873 signingKeyPair: egoPeerKeys.signingKey)
2874
2875 let peer = Peer.with {
2876 $0.peerID = egoPeerID
2877 $0.permanentInfoAndSig = SignedPeerPermanentInfo(peerPermanentInfo)
2878 $0.stableInfoAndSig = SignedPeerStableInfo(newStableInfo ?? stableInfo)
2879 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
2880 $0.vouchers = vouchers
2881 }
2882
2883 return (peer, dynamicInfo)
2884 }
2885
2886 func join(voucherData: Data,
2887 voucherSig: Data,
2888 ckksKeys: [CKKSKeychainBackedKeySet],
2889 tlkShares: [CKKSTLKShare],
2890 preapprovedKeys: [Data]?,
2891 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
2892 self.semaphore.wait()
2893 let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
2894 os_log("join complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
2895 self.semaphore.signal()
2896 reply($0, $1, $2, $3, $4)
2897 }
2898
2899 self.fetchAndPersistChanges { error in
2900 guard error == nil else {
2901 reply(nil, [], nil, nil, error)
2902 return
2903 }
2904
2905 // To join, you must know all policies that exist
2906 let allPolicyVersions = self.model.allPolicyVersions()
2907 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
2908 if let error = policyFetchError {
2909 os_log("join: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2910 }
2911
2912 self.moc.performAndWait {
2913 guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
2914 reply(nil, [], nil, nil, ContainerError.invalidVoucherOrSig)
2915 return
2916 }
2917 guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
2918 reply(nil, [], nil, nil, ContainerError.sponsorNotRegistered(voucher.sponsorID))
2919 return
2920 }
2921
2922 // Fetch ego peer identity from local storage.
2923 guard let egoPeerID = self.containerMO.egoPeerID,
2924 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2925 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
2926 let egoStableData = self.containerMO.egoPeerStableInfo,
2927 let egoStableSig = self.containerMO.egoPeerStableInfoSig
2928 else {
2929 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
2930 return
2931 }
2932
2933 let keyFactory = TPECPublicKeyFactory()
2934 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2935 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
2936 return
2937 }
2938 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
2939 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
2940 return
2941 }
2942 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
2943 os_log("join: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
2944 self.onqueueTTRUntrusted()
2945 reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
2946 return
2947 }
2948
2949 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
2950 guard let egoPeerKeys = egoPeerKeys else {
2951 os_log("Don't have my own peer keys; can't join: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
2952 reply(nil, [], nil, nil, error)
2953 return
2954 }
2955 self.moc.performAndWait {
2956 let peer: Peer
2957 let newDynamicInfo: TPPeerDynamicInfo
2958 do {
2959 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
2960 peerPermanentInfo: selfPermanentInfo,
2961 stableInfo: selfStableInfo,
2962 sponsorID: sponsor.peerID,
2963 preapprovedKeys: preapprovedKeys,
2964 vouchers: [SignedVoucher.with {
2965 $0.voucher = voucher.data
2966 $0.sig = voucher.sig
2967 }, ],
2968 egoPeerKeys: egoPeerKeys)
2969 } catch {
2970 os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2971 reply(nil, [], nil, nil, error)
2972 return
2973 }
2974
2975 guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
2976 os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
2977 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
2978 return
2979 }
2980
2981 let allTLKShares: [TLKShare]
2982 let viewKeys: [ViewKeys]
2983 do {
2984 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
2985 tlkShares: tlkShares,
2986 egoPeerKeys: egoPeerKeys,
2987 egoPeerDynamicInfo: newDynamicInfo,
2988 epoch: Int(selfPermanentInfo.epoch))
2989 } catch {
2990 os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2991 reply(nil, [], nil, nil, error)
2992 return
2993 }
2994
2995 do {
2996 try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
2997 stableInfo: peer.stableInfoAndSig.toStableInfo(),
2998 withSponsorID: sponsor.peerID)
2999 } catch {
3000 os_log("Error checking introduction: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3001 reply(nil, [], nil, nil, error)
3002 return
3003 }
3004
3005 var bottle: Bottle
3006 do {
3007 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3008 } catch {
3009 reply(nil, [], nil, nil, error)
3010 return
3011 }
3012
3013 os_log("Beginning join for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
3014 os_log("Join permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3015 os_log("Join permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3016 os_log("Join stableInfo: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
3017 os_log("Join stableInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
3018 os_log("Join dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3019 os_log("Join dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3020
3021 os_log("Join vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3022 os_log("Join voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3023
3024 os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3025
3026 do {
3027 os_log("Join peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3028 } catch {
3029 os_log("Join unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
3030 }
3031
3032 let changeToken = self.containerMO.changeToken ?? ""
3033 let request = JoinWithVoucherRequest.with {
3034 $0.changeToken = changeToken
3035 $0.peer = peer
3036 $0.bottle = bottle
3037 $0.tlkShares = allTLKShares
3038 $0.viewKeys = viewKeys
3039 }
3040 self.cuttlefish.joinWithVoucher(request) { response, error in
3041 os_log("JoinWithVoucher(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3042 guard let response = response, error == nil else {
3043 os_log("joinWithVoucher failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3044 reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
3045 return
3046 }
3047
3048 self.moc.performAndWait {
3049 do {
3050 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3051 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3052
3053 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
3054 stableInfo: peerStableInfo)
3055
3056 try self.onQueuePersist(changes: response.changes)
3057 os_log("JoinWithVoucher succeeded", log: tplogDebug)
3058
3059 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3060 reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
3061 } catch {
3062 os_log("JoinWithVoucher failed: %{public}@", log: tplogDebug, String(describing: error))
3063 reply(nil, [], nil, nil, error)
3064 }
3065 }
3066 }
3067 }
3068 }
3069 }
3070 }
3071 }
3072 }
3073
3074 func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
3075 self.semaphore.wait()
3076 let reply: (Bool, Bool, Bool, Bool, Error?) -> Void = {
3077 os_log("health check complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
3078 self.semaphore.signal()
3079 reply($0, $1, $2, $3, $4)
3080 }
3081
3082 os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
3083
3084 self.moc.performAndWait {
3085 guard let egoPeerID = self.containerMO.egoPeerID else {
3086 // No identity, nothing to do
3087 os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
3088 reply(false, false, false, false, ContainerError.noPreparedIdentity)
3089 return
3090 }
3091 let request = GetRepairActionRequest.with {
3092 $0.peerID = egoPeerID
3093 $0.requiresEscrowCheck = requiresEscrowCheck
3094 }
3095
3096 self.cuttlefish.getRepairAction(request) { response, error in
3097 guard error == nil else {
3098 reply(false, false, false, false, error)
3099 return
3100 }
3101 guard let action = response?.repairAction else {
3102 os_log("repair response is empty, returning false", log: tplogDebug, type: .default)
3103 reply(false, false, false, false, nil)
3104 return
3105 }
3106 var postRepairAccount: Bool = false
3107 var postRepairEscrow: Bool = false
3108 var resetOctagon: Bool = false
3109 var leaveTrust: Bool = false
3110
3111 switch action {
3112 case .noAction:
3113 break
3114 case .postRepairAccount:
3115 postRepairAccount = true
3116 case .postRepairEscrow:
3117 postRepairEscrow = true
3118 case .resetOctagon:
3119 resetOctagon = true
3120 case .leaveTrust:
3121 leaveTrust = true
3122 case .UNRECOGNIZED:
3123 break
3124 }
3125 reply(postRepairAccount, postRepairEscrow, resetOctagon, leaveTrust, nil)
3126 }
3127 }
3128 }
3129
3130 func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
3131 self.semaphore.wait()
3132 let reply: (Data?, Error?) -> Void = {
3133 os_log("getSupportAppInfo complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3134 self.semaphore.signal()
3135 reply($0, $1)
3136 }
3137
3138 self.cuttlefish.getSupportAppInfo { response, error in
3139 os_log("getSupportAppInfo(): %{public}@, error: %{public}@", log: tplogDebug,
3140 "(\(String(describing: response))", "\(String(describing: error))")
3141 guard let response = response, error == nil else {
3142 os_log("getSupportAppInfo failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3143 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3144 return
3145 }
3146
3147 guard let data = try? response.serializedData() else {
3148 reply(nil, ContainerError.failedToSerializeData)
3149 return
3150 }
3151
3152 reply(data, nil)
3153 }
3154
3155 }
3156
3157 func preflightPreapprovedJoin(reply: @escaping (Bool, Error?) -> Void) {
3158 self.semaphore.wait()
3159 let reply: (Bool, Error?) -> Void = {
3160 os_log("preflightPreapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3161 self.semaphore.signal()
3162 reply($0, $1)
3163 }
3164
3165 self.fetchAndPersistChanges { error in
3166 guard error == nil else {
3167 os_log("preflightPreapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3168 reply(false, error)
3169 return
3170 }
3171
3172 // We need to try to have all policy versions that our peers claim to behave
3173 let allPolicyVersions = self.model.allPolicyVersions()
3174 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3175 if let error = policyFetchError {
3176 os_log("preflightPreapprovedJoin: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3177 }
3178
3179 // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
3180
3181 guard !self.model.allPeerIDs().isEmpty else {
3182 // If, after fetch and handle changes, there's no peers, then we can likely establish.
3183 reply(true, nil)
3184 return
3185 }
3186
3187 guard let egoPeerID = self.containerMO.egoPeerID,
3188 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3189 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3190 else {
3191 os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3192 reply(false, ContainerError.noPreparedIdentity)
3193 return
3194 }
3195
3196 let keyFactory = TPECPublicKeyFactory()
3197 guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3198 os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
3199 reply(false, ContainerError.invalidPermanentInfoOrSig)
3200 return
3201 }
3202
3203 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
3204 os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3205 reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
3206 return
3207 }
3208
3209 reply(true, nil)
3210 }
3211 }
3212 }
3213
3214 func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
3215 tlkShares: [CKKSTLKShare],
3216 preapprovedKeys: [Data]?,
3217 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
3218 self.semaphore.wait()
3219 let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
3220 os_log("preapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
3221 self.semaphore.signal()
3222 reply($0, $1, $2, $3, $4)
3223 }
3224
3225 self.fetchAndPersistChangesIfNeeded { error in
3226 guard error == nil else {
3227 os_log("preapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3228 reply(nil, [], nil, nil, error)
3229 return
3230 }
3231 self.moc.performAndWait {
3232 // If, after fetch and handle changes, there's no peers, then fire off an establish
3233 // Note that if the establish fails, retrying this call might work.
3234 // That's up to the caller.
3235 if self.model.allPeerIDs().isEmpty {
3236 os_log("preapprovedJoin but no existing peers, attempting establish", log: tplogDebug, type: .debug)
3237 self.onqueueEstablish(ckksKeys: ckksKeys,
3238 tlkShares: tlkShares,
3239 preapprovedKeys: preapprovedKeys,
3240 reply: reply)
3241 return
3242 }
3243
3244 // Fetch ego peer identity from local storage.
3245 guard let egoPeerID = self.containerMO.egoPeerID,
3246 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3247 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
3248 let egoStableData = self.containerMO.egoPeerStableInfo,
3249 let egoStableSig = self.containerMO.egoPeerStableInfoSig
3250 else {
3251 os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3252 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
3253 return
3254 }
3255
3256 let keyFactory = TPECPublicKeyFactory()
3257 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3258 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
3259 return
3260 }
3261 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
3262 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
3263 return
3264 }
3265
3266 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
3267 os_log("preapprovedJoin: self machineID %{public}@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
3268 self.onqueueTTRUntrusted()
3269 reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
3270 return
3271 }
3272 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3273 guard let egoPeerKeys = egoPeerKeys else {
3274 os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3275 reply(nil, [], nil, nil, error)
3276 return
3277 }
3278
3279 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
3280 os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3281 reply(nil, [], nil, nil, ContainerError.noPeersPreapprovePreparedIdentity)
3282 return
3283 }
3284
3285 self.moc.performAndWait {
3286
3287 let peer: Peer
3288 let newDynamicInfo: TPPeerDynamicInfo
3289 do {
3290 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
3291 peerPermanentInfo: selfPermanentInfo,
3292 stableInfo: selfStableInfo,
3293 sponsorID: nil,
3294 preapprovedKeys: preapprovedKeys,
3295 vouchers: [],
3296 egoPeerKeys: egoPeerKeys)
3297 } catch {
3298 os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3299 reply(nil, [], nil, nil, error)
3300 return
3301 }
3302
3303 guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3304 os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
3305 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
3306 return
3307 }
3308
3309 let allTLKShares: [TLKShare]
3310 let viewKeys: [ViewKeys]
3311 do {
3312 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3313 tlkShares: tlkShares,
3314 egoPeerKeys: egoPeerKeys,
3315 egoPeerDynamicInfo: newDynamicInfo,
3316 epoch: Int(selfPermanentInfo.epoch))
3317 } catch {
3318 os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3319 reply(nil, [], nil, nil, error)
3320 return
3321 }
3322
3323 var bottle: Bottle
3324 do {
3325 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3326 } catch {
3327 reply(nil, [], nil, nil, error)
3328 return
3329 }
3330
3331 os_log("Beginning preapprovedJoin for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
3332 os_log("preapprovedJoin permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3333 os_log("preapprovedJoin permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3334 os_log("preapprovedJoin stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
3335 os_log("preapprovedJoin stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
3336 os_log("preapprovedJoin dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3337 os_log("preapprovedJoin dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3338
3339 os_log("preapprovedJoin vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3340 os_log("preapprovedJoin voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3341
3342 os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3343
3344 do {
3345 os_log("preapprovedJoin peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3346 } catch {
3347 os_log("preapprovedJoin unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
3348 }
3349
3350 let changeToken = self.containerMO.changeToken ?? ""
3351 let request = JoinWithVoucherRequest.with {
3352 $0.changeToken = changeToken
3353 $0.peer = peer
3354 $0.bottle = bottle
3355 $0.tlkShares = allTLKShares
3356 $0.viewKeys = viewKeys
3357 }
3358 self.cuttlefish.joinWithVoucher(request) { response, error in
3359 os_log("preapprovedJoin(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3360 guard let response = response, error == nil else {
3361 os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3362 reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
3363 return
3364 }
3365
3366 self.moc.performAndWait {
3367 do {
3368 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3369 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3370
3371 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
3372 stableInfo: peerStableInfo)
3373
3374 try self.onQueuePersist(changes: response.changes)
3375 os_log("preapprovedJoin succeeded", log: tplogDebug)
3376
3377 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3378 reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
3379 } catch {
3380 os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, String(describing: error))
3381 reply(nil, [], nil, nil, error)
3382 }
3383 }
3384 }
3385 }
3386 }
3387 }
3388 }
3389 }
3390
3391 func update(deviceName: String?,
3392 serialNumber: String?,
3393 osVersion: String?,
3394 policyVersion: UInt64?,
3395 policySecrets: [String: Data]?,
3396 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3397 self.semaphore.wait()
3398 let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
3399 os_log("update complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3400 self.semaphore.signal()
3401 reply($0, $1)
3402 }
3403
3404 // Get (and save) the latest from cuttlefish
3405 let stableChanges = StableChanges(deviceName: deviceName,
3406 serialNumber: serialNumber,
3407 osVersion: osVersion,
3408 policyVersion: policyVersion,
3409 policySecrets: policySecrets)
3410 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
3411 }
3412
3413 func set(preapprovedKeys: [Data],
3414 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3415 self.semaphore.wait()
3416 let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
3417 os_log("setPreapprovedKeys complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3418 self.semaphore.signal()
3419 reply($0, $1)
3420 }
3421
3422 self.moc.performAndWait {
3423 os_log("setPreapprovedKeys: %@", log: tplogDebug, type: .default, preapprovedKeys)
3424
3425 guard let egoPeerID = self.containerMO.egoPeerID else {
3426 // No identity, nothing to do
3427 os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
3428 reply(nil, ContainerError.noPreparedIdentity)
3429 return
3430 }
3431 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3432 guard let signingKeyPair = signingKeyPair else {
3433 os_log("setPreapprovedKeys: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3434 reply(nil, error ?? ContainerError.unableToCreateKeyPair)
3435 return
3436 }
3437
3438 self.moc.performAndWait {
3439 let dynamicInfo: TPPeerDynamicInfo
3440 do {
3441 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3442 addingPeerIDs: nil,
3443 removingPeerIDs: nil,
3444 preapprovedKeys: preapprovedKeys,
3445 signing: signingKeyPair,
3446 currentMachineIDs: self.onqueueCurrentMIDList())
3447 } catch {
3448 os_log("setPreapprovedKeys: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3449 reply(nil, error)
3450 return
3451 }
3452
3453 os_log("setPreapprovedKeys: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
3454
3455 if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
3456 os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
3457
3458 // Calling this will fill in the peer status
3459 self.updateTrustIfNeeded(reply: reply)
3460 return
3461 }
3462
3463 os_log("setPreapprovedKeys: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3464 let request = UpdateTrustRequest.with {
3465 $0.changeToken = self.containerMO.changeToken ?? ""
3466 $0.peerID = egoPeerID
3467 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
3468 }
3469
3470 self.perform(updateTrust: request) { state, error in
3471 guard error == nil else {
3472 os_log("setPreapprovedKeys: failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
3473 reply(state, error)
3474 return
3475 }
3476
3477 os_log("setPreapprovedKeys: updateTrust succeeded", log: tplogDebug, type: .default)
3478 reply(state, nil)
3479 }
3480 }
3481 }
3482 }
3483 }
3484
3485 func updateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3486 tlkShares: [CKKSTLKShare],
3487 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3488 self.semaphore.wait()
3489 let reply: ([CKRecord]?, Error?) -> Void = {
3490 os_log("updateTLKs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3491 self.semaphore.signal()
3492 reply($0, $1)
3493 }
3494
3495 os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
3496
3497 self.moc.performAndWait {
3498 self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
3499 }
3500 }
3501
3502 func onqueueUpdateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3503 tlkShares: [CKKSTLKShare],
3504 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3505 guard let egoPeerID = self.containerMO.egoPeerID,
3506 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3507 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3508 else {
3509 os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
3510 reply(nil, ContainerError.noPreparedIdentity)
3511 return
3512 }
3513
3514 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
3515 os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
3516 reply(nil, ContainerError.invalidPermanentInfoOrSig)
3517 return
3518 }
3519
3520 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3521 guard let egoPeerKeys = egoPeerKeys else {
3522 os_log("Don't have my own peer keys; can't upload new TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
3523 reply(nil, error)
3524 return
3525 }
3526 self.moc.performAndWait {
3527 guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
3528 os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
3529 reply(nil, ContainerError.missingDynamicInfo)
3530 return
3531 }
3532
3533 let allTLKShares: [TLKShare]
3534 let viewKeys: [ViewKeys]
3535 do {
3536 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3537 tlkShares: tlkShares,
3538 egoPeerKeys: egoPeerKeys,
3539 egoPeerDynamicInfo: egoPeerDynamicInfo,
3540 epoch: Int(selfPermanentInfo.epoch))
3541 } catch {
3542 os_log("Unable to process keys before uploading: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3543 reply(nil, error)
3544 return
3545 }
3546
3547 let request = UpdateTrustRequest.with {
3548 $0.changeToken = self.containerMO.changeToken ?? ""
3549 $0.peerID = egoPeerID
3550 $0.tlkShares = allTLKShares
3551 $0.viewKeys = viewKeys
3552 }
3553
3554 self.cuttlefish.updateTrust(request) { response, error in
3555 os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3556
3557 guard error == nil else {
3558 reply(nil, error)
3559 return
3560 }
3561
3562 let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
3563 os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
3564 reply(keyHierarchyRecords, nil)
3565 }
3566 }
3567 }
3568 }
3569
3570 func getState(reply: @escaping (ContainerState) -> Void) {
3571 self.semaphore.wait()
3572 let reply: (ContainerState) -> Void = {
3573 os_log("getState complete: %{public}@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
3574 self.semaphore.signal()
3575 reply($0)
3576 }
3577
3578 self.moc.performAndWait {
3579 var state = ContainerState()
3580 state.egoPeerID = self.containerMO.egoPeerID
3581
3582 if self.containerMO.bottles != nil {
3583 self.containerMO.bottles!.forEach { bottle in
3584 state.bottles.insert(bottle as! BottleMO)
3585 }
3586 }
3587
3588 self.model.allPeers().forEach { peer in
3589 state.peers[peer.peerID] = peer
3590 }
3591 state.vouchers = Array(self.model.allVouchers())
3592 reply(state)
3593 }
3594 }
3595
3596 // This will only fetch changes if no changes have ever been fetched before
3597 func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
3598 self.moc.performAndWait {
3599 if self.containerMO.changeToken == nil {
3600 self.onqueueFetchAndPersistChanges(reply: reply)
3601 } else {
3602 reply(nil)
3603 }
3604 }
3605 }
3606
3607 private func fetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3608 self.moc.performAndWait {
3609 self.onqueueFetchAndPersistChanges(reply: reply)
3610 }
3611 }
3612
3613 private func onqueueFetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3614 let request = FetchChangesRequest.with {
3615 $0.changeToken = self.containerMO.changeToken ?? ""
3616 }
3617 os_log("Fetching with change token: %{public}@", log: tplogDebug, type: .default, !request.changeToken.isEmpty ? request.changeToken : "empty")
3618
3619 self.cuttlefish.fetchChanges(request) { response, error in
3620 os_log("FetchChanges(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3621 guard let response = response, error == nil else {
3622 switch error {
3623 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
3624 os_log("change token is expired; resetting local CK storage", log: tplogDebug, type: .default)
3625
3626 self.moc.performAndWait {
3627 do {
3628 try self.deleteLocalCloudKitData()
3629 } catch {
3630 os_log("Failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3631 reply(error)
3632 return
3633 }
3634
3635 self.fetchAndPersistChanges(reply: reply)
3636 }
3637
3638 return
3639 default:
3640 os_log("Fetch error is an unknown error: %{public}@", log: tplogDebug, type: .default, String(describing: error))
3641 }
3642
3643 os_log("Could not fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3644 reply(error)
3645 return
3646 }
3647
3648 do {
3649 try self.persist(changes: response.changes)
3650 } catch {
3651 os_log("Could not persist changes: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3652 reply(error)
3653 return
3654 }
3655
3656 if response.changes.more {
3657 os_log("persist: More changes indicated. Fetching...", log: tplogDebug, type: .default)
3658 self.fetchAndPersistChanges(reply: reply)
3659 return
3660 } else {
3661 os_log("persist: no more changes!", log: tplogDebug, type: .default)
3662 reply(nil)
3663 }
3664 }
3665 }
3666
3667 //
3668 // Check for delta update in trust lists, that should lead to update of TLKShares
3669 //
3670 private func haveTrustMemberChanges(newDynamicInfo: TPPeerDynamicInfo, oldDynamicInfo: TPPeerDynamicInfo?) -> Bool {
3671 guard let oldDynamicInfo = oldDynamicInfo else {
3672 return true
3673 }
3674 if newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs {
3675 return true
3676 }
3677 if newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs {
3678 return true
3679 }
3680 if newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals {
3681 return true
3682 }
3683 return false
3684 }
3685
3686 // Fetch and persist changes from Cuttlefish. If this results
3687 // in us calculating a new dynamicInfo then give the new dynamicInfo
3688 // to Cuttlefish. If Cuttlefish returns more changes then persist
3689 // them locally, update dynamicInfo if needed, and keep doing that
3690 // until dynamicInfo is unchanged and there are no more changes to fetch.
3691 //
3692 // Must be holding the semaphore to call this, and it remains
3693 // the caller's responsibility to release it after it completes
3694 // (i.e. after reply is invoked).
3695 internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
3696 peerChanges: Bool = false,
3697 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3698 self.fetchAndPersistChanges { error in
3699 if let error = error {
3700 os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3701 reply(nil, error)
3702 return
3703 }
3704
3705 self.updateTrustIfNeeded(stableChanges: stableChanges, peerChanges: peerChanges, reply: reply)
3706 }
3707 }
3708
3709 // If this results in us calculating a new dynamicInfo then,
3710 // upload the new dynamicInfo
3711 // to Cuttlefish. If Cuttlefish returns more changes then persist
3712 // them locally, update dynamicInfo if needed, and keep doing that
3713 // until dynamicInfo is unchanged and there are no more changes to fetch.
3714 //
3715 // Must be holding the semaphore to call this, and it remains
3716 // the caller's responsibility to release it after it completes
3717 // (i.e. after reply is invoked).
3718 private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
3719 peerChanges: Bool = false,
3720 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3721 self.moc.performAndWait {
3722 guard let egoPeerID = self.containerMO.egoPeerID else {
3723 // No identity, nothing to do
3724 os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
3725 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), nil)
3726 return
3727 }
3728 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3729 guard let signingKeyPair = signingKeyPair else {
3730 os_log("updateTrustIfNeeded: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3731 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), error)
3732 return
3733 }
3734 guard let currentSelfInModel = self.model.peer(withID: egoPeerID) else {
3735 // Not in circle, nothing to do
3736 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
3737 os_log("updateTrustIfNeeded: ego peer is not in model, is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
3738 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3739 isPreapproved: isPreapproved,
3740 status: .unknown,
3741 memberChanges: peerChanges,
3742 unknownMachineIDs: false,
3743 osVersion: nil),
3744 nil)
3745 return
3746 }
3747
3748 // We need to try to have all policy versions that our peers claim to behave
3749 let allPolicyVersions = self.model.allPolicyVersions()
3750 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3751 if let error = policyFetchError {
3752 os_log("updateTrustIfNeeded: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3753 }
3754
3755 self.moc.performAndWait {
3756 let dynamicInfo: TPPeerDynamicInfo
3757 var stableInfo: TPPeerStableInfo?
3758 do {
3759
3760 // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
3761 // and then only load the key if it has changed and we need to sign a new one. This would also
3762 // help make our detection of change immune from non-canonical serialization of dynamicInfo.
3763 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3764 addingPeerIDs: nil,
3765 removingPeerIDs: nil,
3766 preapprovedKeys: nil,
3767 signing: signingKeyPair,
3768 currentMachineIDs: self.onqueueCurrentMIDList())
3769
3770 stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
3771 egoPeerID: egoPeerID,
3772 existingStableInfo: currentSelfInModel.stableInfo,
3773 dynamicInfo: dynamicInfo,
3774 signingKeyPair: signingKeyPair)
3775 } catch {
3776 os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3777 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3778 isPreapproved: false,
3779 status: self.model.statusOfPeer(withID: egoPeerID),
3780 memberChanges: peerChanges,
3781 unknownMachineIDs: false,
3782 osVersion: nil),
3783 error)
3784 return
3785 }
3786
3787 os_log("updateTrustIfNeeded: produced a stableInfo: %{public}@", log: tplogDebug, type: .default, String(describing: stableInfo))
3788 os_log("updateTrustIfNeeded: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
3789
3790 let peer = self.model.peer(withID: egoPeerID)
3791 if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
3792 dynamicInfo == peer?.dynamicInfo {
3793 os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
3794 // No change to the dynamicInfo: update the MID list now that we've reached a steady state
3795 do {
3796 self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
3797 try self.moc.save()
3798 } catch {
3799 os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3800 }
3801
3802 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3803 isPreapproved: false,
3804 status: self.model.statusOfPeer(withID: egoPeerID),
3805 memberChanges: peerChanges,
3806 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
3807 osVersion: peer?.stableInfo?.osVersion),
3808 nil)
3809 return
3810 }
3811 // Check if we change that should trigger a notification that should trigger TLKShare updates
3812 let havePeerChanges = peerChanges || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
3813
3814 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
3815 os_log("updateTrustIfNeeded: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3816 var request = UpdateTrustRequest.with {
3817 $0.changeToken = self.containerMO.changeToken ?? ""
3818 $0.peerID = egoPeerID
3819 $0.dynamicInfoAndSig = signedDynamicInfo
3820 }
3821 if let stableInfo = stableInfo {
3822 request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
3823 }
3824
3825 self.perform(updateTrust: request, stableChanges: stableChanges, peerChanges: havePeerChanges, reply: reply)
3826 }
3827 }
3828 }
3829 }
3830 }
3831
3832 private func perform(updateTrust request: UpdateTrustRequest,
3833 stableChanges: StableChanges? = nil,
3834 peerChanges: Bool = false,
3835 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3836
3837 self.cuttlefish.updateTrust(request) { response, error in
3838 os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3839 guard let response = response, error == nil else {
3840 os_log("UpdateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3841 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3842 return
3843 }
3844
3845 do {
3846 try self.persist(changes: response.changes)
3847 } catch {
3848 os_log("UpdateTrust failed: %{public}@", log: tplogDebug, String(describing: error))
3849 reply(nil, error)
3850 return
3851 }
3852
3853 if response.changes.more {
3854 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
3855 peerChanges: peerChanges,
3856 reply: reply)
3857 } else {
3858 self.updateTrustIfNeeded(stableChanges: stableChanges,
3859 peerChanges: peerChanges,
3860 reply: reply)
3861 }
3862 }
3863 }
3864
3865 private func persist(changes: Changes) throws {
3866 // This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
3867 // So, do it ourself
3868 var outsideBlockError: Error?
3869
3870 self.moc.performAndWait {
3871 os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
3872 changes.differences.count,
3873 changes.more)
3874 os_log("persist: New change token: %{public}@", log: tplogDebug, type: .default, changes.changeToken)
3875
3876 do {
3877 try self.onQueuePersist(changes: changes)
3878 } catch {
3879 outsideBlockError = error
3880 }
3881 }
3882
3883 if let outsideBlockError = outsideBlockError {
3884 throw outsideBlockError
3885 }
3886 }
3887
3888 // Must be on moc queue to call this.
3889 // Changes are registered in the model and stored in moc.
3890 private func onQueuePersist(changes: Changes) throws {
3891 self.containerMO.changeToken = changes.changeToken
3892 self.containerMO.moreChanges = changes.more
3893
3894 if !changes.differences.isEmpty {
3895 self.model.clearViableBottles()
3896 }
3897
3898 try changes.differences.forEach { peerDifference in
3899 if let operation = peerDifference.operation {
3900 switch operation {
3901 case .add(let peer):
3902 try self.addOrUpdate(peer: peer)
3903
3904 case .update(let peer):
3905 try self.addOrUpdate(peer: peer)
3906 // Update containerMO ego data if it has changed.
3907 if peer.peerID == self.containerMO.egoPeerID {
3908 guard let stableInfoAndSig: TPPeerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3909 break
3910 }
3911 self.containerMO.egoPeerStableInfo = stableInfoAndSig.data
3912 self.containerMO.egoPeerStableInfoSig = stableInfoAndSig.sig
3913 }
3914
3915 case .remove(let peer):
3916 self.model.deletePeer(withID: peer.peerID)
3917 if let peerMO = try self.fetchPeerMO(peerID: peer.peerID) {
3918 self.moc.delete(peerMO)
3919 }
3920 }
3921 }
3922 }
3923
3924 let signingKey = changes.recoverySigningPubKey
3925 let encryptionKey = changes.recoveryEncryptionPubKey
3926
3927 if !signingKey.isEmpty && !encryptionKey.isEmpty {
3928 self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
3929 }
3930 try self.moc.save()
3931 }
3932
3933 // Must be on moc queue to call this
3934 // Deletes all things that should come back from CloudKit. Keeps the egoPeer data, though.
3935 private func deleteLocalCloudKitData() throws {
3936 os_log("Deleting all CloudKit data", log: tplogDebug, type: .default)
3937
3938 do {
3939 let peerRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
3940 peerRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
3941 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: peerRequest))
3942
3943 let bottleRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Bottle")
3944 bottleRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
3945 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: bottleRequest))
3946
3947 self.containerMO.peers = nil
3948 self.containerMO.bottles = nil
3949 self.containerMO.changeToken = nil
3950 self.containerMO.moreChanges = false
3951
3952 self.model = Container.loadModel(from: self.containerMO)
3953 try self.moc.save()
3954 } catch {
3955 os_log("Local delete failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3956 throw error
3957 }
3958
3959 os_log("Saved model with %d peers", log: tplogDebug, type: .default, self.model.allPeerIDs().count)
3960 }
3961
3962 // Must be on moc queue to call this.
3963 private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
3964 self.model.setRecoveryKeys(
3965 TPRecoveryKeyPair(signingSPKI: signingKey, encryptionSPKI: encryptionKey))
3966
3967 self.containerMO.recoveryKeySigningSPKI = signingKey
3968 self.containerMO.recoveryKeyEncryptionSPKI = encryptionKey
3969 }
3970
3971 // Must be on moc queue to call this.
3972 private func addOrUpdate(peer: Peer) throws {
3973 if !self.model.hasPeer(withID: peer.peerID) {
3974 // Add:
3975 guard let permanentInfo = peer.permanentInfoAndSig.toPermanentInfo(peerID: peer.peerID) else {
3976 // Ignoring bad peer
3977 return
3978 }
3979 let stableInfo = peer.stableInfoAndSig.toStableInfo()
3980 let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo()
3981 let vouchers = peer.vouchers.compactMap {
3982 TPVoucher(infoWith: $0.voucher, sig: $0.sig)
3983 }
3984 let isEgoPeer = peer.peerID == self.containerMO.egoPeerID
3985 try self.registerPeerMO(permanentInfo: permanentInfo,
3986 stableInfo: stableInfo,
3987 dynamicInfo: dynamicInfo,
3988 vouchers: vouchers,
3989 isEgoPeer: isEgoPeer)
3990 } else {
3991 // Update:
3992 // The assertion here is that every peer registered in model is also present in containerMO
3993 let peerMO = try self.fetchPeerMO(peerID: peer.peerID)!
3994
3995 if let stableInfo = peer.stableInfoAndSig.toStableInfo() {
3996 try self.model.update(stableInfo, forPeerWithID: peer.peerID)
3997 // Pull the stableInfo back out of the model, and persist that.
3998 // The model checks signatures and clocks to prevent replay attacks.
3999 let modelPeer = self.model.peer(withID: peer.peerID)
4000 peerMO.stableInfo = modelPeer?.stableInfo?.data
4001 peerMO.stableInfoSig = modelPeer?.stableInfo?.sig
4002 }
4003 if let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo() {
4004 try self.model.update(dynamicInfo, forPeerWithID: peer.peerID)
4005 // Pull the dynamicInfo back out of the model, and persist that.
4006 // The model checks signatures and clocks to prevent replay attacks.
4007 let modelPeer = self.model.peer(withID: peer.peerID)
4008 peerMO.dynamicInfo = modelPeer?.dynamicInfo?.data
4009 peerMO.dynamicInfoSig = modelPeer?.dynamicInfo?.sig
4010 }
4011 peer.vouchers.forEach {
4012 if let voucher = TPVoucher(infoWith: $0.voucher, sig: $0.sig) {
4013 self.model.register(voucher)
4014 let voucherMO = VoucherMO(context: self.moc)
4015 voucherMO.voucherInfo = voucher.data
4016 voucherMO.voucherInfoSig = voucher.sig
4017 peerMO.addToVouchers(voucherMO)
4018 }
4019 }
4020 }
4021 }
4022
4023 // Must be on moc queue to call this.
4024 private func fetchPeerMO(peerID: String) throws -> PeerMO? {
4025 let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
4026 fetch.predicate = NSPredicate(format: "peerID == %@ && container == %@", peerID, self.containerMO)
4027 let peers = try self.moc.fetch(fetch)
4028 return peers.first as? PeerMO
4029 }
4030
4031 // Must be on moc queue to call this.
4032 private func getPolicyDoc(_ policyVersion: UInt64) throws -> TPPolicyDocument {
4033 guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
4034 throw ContainerError.unknownPolicyVersion(policyVersion)
4035 }
4036 assert(policyVersion == policyDoc.version.versionNumber)
4037 if policyVersion == prevailingPolicyVersion.versionNumber {
4038 assert(policyDoc.version.policyHash == prevailingPolicyVersion.policyHash)
4039 }
4040 return policyDoc
4041 }
4042
4043 // Must be on moc queue to call this.
4044 private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
4045 egoPeerID: String,
4046 existingStableInfo: TPPeerStableInfo?,
4047 dynamicInfo: TPPeerDynamicInfo,
4048 signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
4049 func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
4050 return (nil == change) || change == existing
4051 }
4052
4053 let policyOfPeers = try? self.model.policy(forPeerIDs: dynamicInfo.includedPeerIDs,
4054 candidatePeerID: egoPeerID,
4055 candidateStableInfo: existingStableInfo)
4056
4057 // Pick the best version of:
4058 // 1. The policy version asked for by the client
4059 // 2. The max of our existing policyVersion, the highest policy used by our trusted peers, and the compile-time prevailing policy version
4060 let optimalPolicyVersionNumber = stableChanges?.policyVersion ??
4061 max(existingStableInfo?.bestPolicyVersion().versionNumber ?? prevailingPolicyVersion.versionNumber,
4062 policyOfPeers?.version.versionNumber ?? prevailingPolicyVersion.versionNumber,
4063 prevailingPolicyVersion.versionNumber)
4064
4065 // Determine which recovery key we'd like to be using, given our current idea of who to trust
4066 let optimalRecoveryKey = self.model.bestRecoveryKey(for: existingStableInfo, dynamicInfo: dynamicInfo)
4067
4068 if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
4069 noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
4070 noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
4071 noChange(optimalPolicyVersionNumber, existingStableInfo?.bestPolicyVersion().versionNumber) &&
4072 noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
4073 noChange(optimalRecoveryKey?.signingSPKI, existingStableInfo?.recoverySigningPublicKey) &&
4074 noChange(optimalRecoveryKey?.encryptionSPKI, existingStableInfo?.recoveryEncryptionPublicKey) {
4075 return nil
4076 }
4077
4078 let optimalPolicyVersion = try self.getPolicyDoc(optimalPolicyVersionNumber).version
4079
4080 return try self.model.createStableInfo(withFrozenPolicyVersion: frozenPolicyVersion,
4081 flexiblePolicyVersion: optimalPolicyVersion,
4082 policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
4083 deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
4084 serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
4085 osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
4086 signing: signingKeyPair,
4087 recoverySigningPubKey: optimalRecoveryKey?.signingSPKI,
4088 recoveryEncryptionPubKey: optimalRecoveryKey?.encryptionSPKI)
4089 }
4090
4091 private func assembleBottle(egoPeerID: String) throws -> Bottle {
4092 let bottleMoSet = containerMO.bottles as? Set<BottleMO>
4093
4094 var bottleMOs = bottleMoSet?.filter {
4095 $0.peerID == egoPeerID
4096 }
4097
4098 if let count = bottleMOs?.count {
4099 if count > 1 {
4100 throw ContainerError.tooManyBottlesForPeer
4101 } else if count == 0 {
4102 throw ContainerError.noBottleForPeer
4103 }
4104 } else {
4105 throw ContainerError.failedToAssembleBottle
4106 }
4107
4108 let BM: BottleMO? = (bottleMOs?.removeFirst())
4109
4110 let bottle = try Bottle.with {
4111 $0.peerID = egoPeerID
4112
4113 if let bottledPeerData = BM?.contents {
4114 $0.contents = bottledPeerData
4115 } else {
4116 throw ContainerError.failedToAssembleBottle
4117 }
4118
4119 if let bottledPeerEscrowSigningSPKI = BM?.escrowedSigningSPKI {
4120 $0.escrowedSigningSpki = bottledPeerEscrowSigningSPKI
4121 } else {
4122 throw ContainerError.failedToAssembleBottle
4123 }
4124
4125 if let bottledPeerSignatureUsingEscrowKey = BM?.signatureUsingEscrowKey {
4126 $0.signatureUsingEscrowKey = bottledPeerSignatureUsingEscrowKey
4127 } else {
4128 throw ContainerError.failedToAssembleBottle
4129 }
4130
4131 if let bottledPeerSignatureUsingPeerKey = BM?.signatureUsingPeerKey {
4132 $0.signatureUsingPeerKey = bottledPeerSignatureUsingPeerKey
4133 } else {
4134 throw ContainerError.failedToAssembleBottle
4135 }
4136
4137 if let bID = BM?.bottleID {
4138 $0.bottleID = bID
4139 } else {
4140 throw ContainerError.failedToAssembleBottle
4141 }
4142 }
4143
4144 return bottle
4145 }
4146
4147 func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
4148 self.semaphore.wait()
4149 let reply: (Error?) -> Void = {
4150 os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
4151 self.semaphore.signal()
4152 reply($0)
4153 }
4154
4155 var updatedRequest = request
4156
4157 if let egoPeerID = self.containerMO.egoPeerID {
4158 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, _ in
4159 updatedRequest.octagonEgoIdentity = (egoPeerKeys != nil)
4160 }
4161 }
4162
4163 self.moc.performAndWait {
4164 self.cuttlefish.reportHealth(updatedRequest) { response, error in
4165 os_log("reportHealth(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
4166 guard error == nil else {
4167 reply(error)
4168 return
4169 }
4170 reply(nil)
4171 }
4172 }
4173 }
4174
4175 func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
4176 self.semaphore.wait()
4177 let reply: (Error?) -> Void = {
4178 os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
4179 self.semaphore.signal()
4180 reply($0)
4181 }
4182
4183 self.moc.performAndWait {
4184 self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { response, error in
4185 os_log("pushHealthInquiry(): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
4186 guard error == nil else {
4187 reply(error)
4188 return
4189 }
4190 reply(nil)
4191 }
4192 }
4193 }
4194 }