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