]> git.saurik.com Git - apple/security.git/blob - keychain/TrustedPeersHelper/Container.swift
Security-59754.80.3.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 let request = FetchViableBottlesRequest.with {
2563 $0.filterRequest = OctagonPlatformSupportsSOS() ? .unknown : .byOctagonOnly
2564 }
2565 if request.filterRequest == .byOctagonOnly {
2566 os_log("Requesting Cuttlefish sort records by Octagon Only", log: tplogDebug, type: .default)
2567 }
2568
2569 self.cuttlefish.fetchViableBottles(request) { response, error in
2570 guard error == nil else {
2571 os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2572 reply(nil, nil, error)
2573 return
2574 }
2575
2576 self.moc.performAndWait {
2577 guard let escrowPairs = response?.viableBottles else {
2578 os_log("fetchViableBottles returned no viable bottles", log: tplogDebug, type: .default)
2579 reply([], [], nil)
2580 return
2581 }
2582
2583 var partialPairs: [EscrowPair] = []
2584 if let partial = response?.partialBottles {
2585 partialPairs = partial
2586 } else {
2587 os_log("fetchViableBottles returned no partially viable bottles, but that's ok", log: tplogDebug, type: .default)
2588 }
2589
2590 let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
2591 os_log("fetchViableBottles returned viable bottles: %{public}@", log: tplogDebug, type: .default, viableBottleIDs)
2592
2593 let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
2594 os_log("fetchViableBottles returned partial bottles: %{public}@", log: tplogDebug, type: .default, partialBottleIDs)
2595
2596 self.handleFetchViableBottlesResponseWithSemaphore(response: response)
2597
2598 do {
2599 try self.moc.save()
2600 os_log("fetchViableBottles saved bottles", log: tplogDebug, type: .default)
2601 let cached = TPCachedViableBottles(viableBottles: viableBottleIDs, partialBottles: partialBottleIDs)
2602 self.model.setViableBottles(cached)
2603 self.containerMO.escrowFetchDate = Date()
2604 reply(viableBottleIDs, partialBottleIDs, nil)
2605 } catch {
2606 os_log("fetchViableBottles unable to save bottles: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2607 reply(nil, nil, error)
2608 }
2609 }
2610 }
2611 }
2612 }
2613
2614 func removeEscrowCache(reply: @escaping (Error?) -> Void) {
2615 os_log("beginning a removeEscrowCache", log: tplogDebug, type: .default)
2616
2617 self.semaphore.wait()
2618 let reply: (Error?) -> Void = {
2619 os_log("removeEscrowCache complete %{public}@", log: tplogTrace, type: .info, traceError($0))
2620 self.semaphore.signal()
2621 reply($0)
2622 }
2623
2624 self.moc.performAndWait {
2625 self.onQueueRemoveEscrowCache()
2626 reply(nil)
2627 }
2628 }
2629
2630 private func onQueueRemoveEscrowCache() {
2631 if let records = self.containerMO.fullyViableEscrowRecords {
2632 self.containerMO.removeFromFullyViableEscrowRecords(records)
2633 }
2634 if let records = self.containerMO.partiallyViableEscrowRecords {
2635 self.containerMO.removeFromPartiallyViableEscrowRecords(records)
2636 }
2637 if let records = self.containerMO.legacyEscrowRecords {
2638 self.containerMO.removeFromLegacyEscrowRecords(records)
2639 }
2640 self.containerMO.escrowFetchDate = nil
2641 }
2642
2643 func fetchEscrowRecordsWithSemaphore(forceFetch: Bool, reply: @escaping ([Data]?, Error?) -> Void) {
2644 os_log("beginning a fetchEscrowRecords", log: tplogDebug, type: .default)
2645
2646 self.moc.performAndWait {
2647 var cachedRecords: [OTEscrowRecord] = []
2648
2649 if forceFetch == false {
2650 os_log("fetchEscrowRecords: force fetch flag is off", log: tplogDebug, type: .default)
2651 if let lastDate = self.containerMO.escrowFetchDate {
2652 if Date() < lastDate.addingTimeInterval(escrowCacheTimeout) {
2653 os_log("escrow cache still valid", log: tplogDebug, type: .default)
2654 cachedRecords = onqueueCachedEscrowRecords()
2655 } else {
2656 os_log("escrow cache no longer valid", log: tplogDebug, type: .default)
2657 self.onQueueRemoveEscrowCache()
2658 }
2659 }
2660 } else {
2661 os_log("fetchEscrowRecords: force fetch flag is on, removing escrow cache", log: tplogDebug, type: .default)
2662 self.onQueueRemoveEscrowCache()
2663 }
2664
2665 if !cachedRecords.isEmpty {
2666 os_log("returning from fetchEscrowRecords, using cached escrow records", log: tplogDebug, type: .default)
2667 let recordData: [Data] = cachedRecords.map { $0.data }
2668 reply(recordData, nil)
2669 return
2670 }
2671
2672 let request = FetchViableBottlesRequest.with {
2673 $0.filterRequest = OctagonPlatformSupportsSOS() ? .unknown : .byOctagonOnly
2674 }
2675 if request.filterRequest == .byOctagonOnly {
2676 os_log("Requesting Cuttlefish sort records by Octagon Only", log: tplogDebug, type: .default)
2677 }
2678
2679 self.cuttlefish.fetchViableBottles(request) { response, error in
2680 guard error == nil else {
2681 os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2682 reply(nil, error)
2683 return
2684 }
2685
2686 self.moc.performAndWait {
2687 guard response?.viableBottles != nil else {
2688 os_log("fetchViableBottles returned no viable bottles", log: tplogDebug, type: .default)
2689 reply([], nil)
2690 return
2691 }
2692
2693 self.handleFetchViableBottlesResponseWithSemaphore(response: response)
2694 }
2695
2696 do {
2697 try self.moc.save()
2698 os_log("fetchViableBottles saved bottles and records", log: tplogDebug, type: .default)
2699 self.containerMO.escrowFetchDate = Date()
2700
2701 var allEscrowRecordData: [Data] = []
2702 if let fullyViableRecords = self.containerMO.fullyViableEscrowRecords as? Set<EscrowRecordMO> {
2703 for record in fullyViableRecords {
2704 if let r = self.escrowRecordMOToEscrowRecords(record: record, viability: .full) {
2705 allEscrowRecordData.append(r.data)
2706 }
2707 }
2708 }
2709 if let partiallyViableRecords = self.containerMO.partiallyViableEscrowRecords as? Set<EscrowRecordMO> {
2710 for record in partiallyViableRecords {
2711 if let r = self.escrowRecordMOToEscrowRecords(record: record, viability: .partial) {
2712 allEscrowRecordData.append(r.data)
2713 }
2714 }
2715 }
2716 if let legacyRecords = self.containerMO.legacyEscrowRecords as? Set<EscrowRecordMO> {
2717 for record in legacyRecords {
2718 if let r = self.escrowRecordMOToEscrowRecords(record: record, viability: .none) {
2719 allEscrowRecordData.append(r.data)
2720 }
2721 }
2722 }
2723 reply(allEscrowRecordData, nil)
2724 } catch {
2725 os_log("fetchViableBottles unable to save bottles and records: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2726 reply(nil, error)
2727 }
2728 }
2729 }
2730 }
2731
2732 func fetchCurrentPolicy(modelIDOverride: String?, reply: @escaping (TPSyncingPolicy?, TPPBPeerStableInfo_UserControllableViewStatus, Error?) -> Void) {
2733 self.semaphore.wait()
2734 let reply: (TPSyncingPolicy?, TPPBPeerStableInfo_UserControllableViewStatus, Error?) -> Void = {
2735 os_log("fetchCurrentPolicy complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2736 self.semaphore.signal()
2737 reply($0, $1, $2)
2738 }
2739
2740 self.moc.performAndWait {
2741 guard let egoPeerID = self.containerMO.egoPeerID,
2742 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2743 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
2744 let stableInfoData = self.containerMO.egoPeerStableInfo,
2745 let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2746 os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
2747 // 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
2748
2749 guard let modelID = modelIDOverride else {
2750 os_log("no model ID override; returning error", log: tplogDebug, type: .default)
2751 reply(nil, .UNKNOWN, ContainerError.noPreparedIdentity)
2752 return
2753 }
2754
2755 guard let policyDocument = self.model.policy(withVersion: prevailingPolicyVersion.versionNumber) else {
2756 os_log("prevailing policy is missing?", log: tplogDebug, type: .default)
2757 reply(nil, .UNKNOWN, ContainerError.noPreparedIdentity)
2758 return
2759 }
2760
2761 do {
2762 let prevailingPolicy = try policyDocument.policy(withSecrets: [:], decrypter: Decrypter())
2763 let syncingPolicy = try prevailingPolicy.syncingPolicy(forModel: modelID, syncUserControllableViews: .UNKNOWN)
2764
2765 os_log("returning a policy for model ID %{public}@", log: tplogDebug, type: .default, modelID)
2766 reply(syncingPolicy, .UNKNOWN, nil)
2767 return
2768 } catch {
2769 os_log("fetchCurrentPolicy failed to prevailing policy: %{public}@", log: tplogDebug, type: .error)
2770 reply(nil, .UNKNOWN, ContainerError.noPreparedIdentity)
2771 return
2772 }
2773 }
2774
2775 let keyFactory = TPECPublicKeyFactory()
2776 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2777 os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
2778 reply(nil, .UNKNOWN, ContainerError.invalidPermanentInfoOrSig)
2779 return
2780 }
2781 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
2782 os_log("fetchCurrentPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
2783 reply(nil, .UNKNOWN, ContainerError.invalidStableInfoOrSig)
2784 return
2785 }
2786
2787 do {
2788 let syncingPolicy = try self.syncingPolicyFor(modelID: modelIDOverride ?? permanentInfo.modelID, stableInfo: stableInfo)
2789
2790 guard let peer = self.model.peer(withID: permanentInfo.peerID), let dynamicInfo = peer.dynamicInfo else {
2791 os_log("fetchCurrentPolicy with no dynamic info", log: tplogDebug, type: .error)
2792 reply(syncingPolicy, .UNKNOWN, nil)
2793 return
2794 }
2795
2796 // Note: we specifically do not want to sanitize this value for the platform: returning FOLLOWING here isn't that helpful
2797 let peersUserViewSyncability = self.model.userViewSyncabilityConsensusAmongTrustedPeers(dynamicInfo)
2798 reply(syncingPolicy, peersUserViewSyncability, nil)
2799 return
2800 } catch {
2801 os_log("Fetching the syncing policy failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2802 reply(nil, .UNKNOWN, error)
2803 return
2804 }
2805 }
2806 }
2807
2808 func syncingPolicyFor(modelID: String, stableInfo: TPPeerStableInfo) throws -> TPSyncingPolicy {
2809 let bestPolicyVersion : TPPolicyVersion
2810
2811 let peerPolicyVersion = stableInfo.bestPolicyVersion()
2812 if peerPolicyVersion.versionNumber < frozenPolicyVersion.versionNumber {
2813 // This peer was from before CKKS4All, and we shouldn't listen to them when it comes to Syncing Policies
2814 bestPolicyVersion = prevailingPolicyVersion
2815 os_log("Ignoring policy version from pre-CKKS4All peer", log: tplogDebug, type: .default)
2816
2817 } else {
2818 bestPolicyVersion = peerPolicyVersion
2819 }
2820
2821 guard let policyDocument = self.model.policy(withVersion: bestPolicyVersion.versionNumber) else {
2822 os_log("best policy is missing?", log: tplogDebug, type: .default)
2823 throw ContainerError.unknownPolicyVersion(prevailingPolicyVersion.versionNumber)
2824 }
2825
2826 let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
2827 return try policy.syncingPolicy(forModel: modelID, syncUserControllableViews: stableInfo.syncUserControllableViews)
2828 }
2829
2830 // All-or-nothing: return an error in case full list cannot be returned.
2831 // Completion handler data format: [version : [hash, data]]
2832 func fetchPolicyDocuments(versions: Set<TPPolicyVersion>,
2833 reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
2834 self.semaphore.wait()
2835 let reply: ([TPPolicyVersion: Data]?, Error?) -> Void = {
2836 os_log("fetchPolicyDocuments complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
2837 self.semaphore.signal()
2838 reply($0, $1)
2839 }
2840
2841 self.fetchPolicyDocumentsWithSemaphore(versions: versions) { policyDocuments, fetchError in
2842 reply(policyDocuments.flatMap { $0.mapValues { policyDoc in policyDoc.protobuf } }, fetchError)
2843 }
2844 }
2845
2846 func fetchPolicyDocumentWithSemaphore(version: TPPolicyVersion,
2847 reply: @escaping (TPPolicyDocument?, Error?) -> Void) {
2848 self.fetchPolicyDocumentsWithSemaphore(versions: Set([version])) { versions, fetchError in
2849 guard fetchError == nil else {
2850 reply(nil, fetchError)
2851 return
2852 }
2853
2854 guard let doc = versions?[version] else {
2855 os_log("fetchPolicyDocument: didn't return policy of version: %{public}@", log: tplogDebug, versions ?? "no versions")
2856 reply(nil, ContainerError.unknownPolicyVersion(version.versionNumber))
2857 return
2858 }
2859
2860 reply(doc, nil)
2861 }
2862 }
2863
2864 func fetchPolicyDocumentsWithSemaphore(versions: Set<TPPolicyVersion>,
2865 reply: @escaping ([TPPolicyVersion: TPPolicyDocument]?, Error?) -> Void) {
2866 var remaining = versions
2867 var docs: [TPPolicyVersion: TPPolicyDocument] = [:]
2868
2869 self.moc.performAndWait {
2870 for version in remaining {
2871 if let policydoc = try? self.getPolicyDoc(version.versionNumber), policydoc.version.policyHash == version.policyHash {
2872 docs[policydoc.version] = policydoc
2873 remaining.remove(version)
2874 }
2875 }
2876 }
2877
2878 guard !remaining.isEmpty else {
2879 reply(docs, nil)
2880 return
2881 }
2882
2883 let request = FetchPolicyDocumentsRequest.with {
2884 $0.keys = remaining.map { version in
2885 PolicyDocumentKey.with { $0.version = version.versionNumber; $0.hash = version.policyHash }}
2886 }
2887
2888 self.cuttlefish.fetchPolicyDocuments(request) { response, error in
2889 guard let response = response, error == nil else {
2890 os_log("FetchPolicyDocuments failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2891 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
2892 return
2893 }
2894
2895 self.moc.performAndWait {
2896 for mapEntry in response.entries {
2897 // TODO: validate the policy's signature
2898
2899 guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
2900 os_log("Can't make policy document with hash %{public}@ and data %{public}@",
2901 log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
2902 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2903 return
2904 }
2905
2906 guard let expectedVersion = (remaining.first { $0.versionNumber == doc.version.versionNumber }) else {
2907 os_log("Received a policy version we didn't request: %d", log: tplogDebug, type: .default, doc.version.versionNumber)
2908 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2909 return
2910 }
2911
2912 guard expectedVersion.policyHash == doc.version.policyHash else {
2913 os_log("Requested hash %{public}@ does not match fetched hash %{public}@", log: tplogDebug, type: .default,
2914 expectedVersion.policyHash, doc.version.policyHash)
2915 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2916 return
2917 }
2918
2919 remaining.remove(expectedVersion) // Server responses should be unique, let's enforce
2920
2921 docs[doc.version] = doc
2922 self.model.register(doc)
2923 }
2924
2925 do {
2926 try self.moc.save() // if this fails callers might make bad data assumptions
2927 } catch {
2928 reply(nil, error)
2929 return
2930 }
2931
2932 // Determine if there's anything left to fetch
2933 guard let unfetchedVersion = remaining.first else {
2934 // Nothing remaining? Success!
2935 reply(docs, nil)
2936 return
2937 }
2938
2939 reply(nil, ContainerError.unknownPolicyVersion(unfetchedVersion.versionNumber))
2940 }
2941 }
2942 }
2943
2944 // Must be on moc queue to call this.
2945 // Caller is responsible for saving the moc afterwards.
2946 @discardableResult
2947 private func registerPeerMO(permanentInfo: TPPeerPermanentInfo,
2948 stableInfo: TPPeerStableInfo? = nil,
2949 dynamicInfo: TPPeerDynamicInfo? = nil,
2950 vouchers: [TPVoucher]? = nil,
2951 isEgoPeer: Bool = false) throws -> PeerMO {
2952 let peerID = permanentInfo.peerID
2953
2954 let peer = PeerMO(context: self.moc)
2955 peer.peerID = peerID
2956 peer.permanentInfo = permanentInfo.data
2957 peer.permanentInfoSig = permanentInfo.sig
2958 peer.stableInfo = stableInfo?.data
2959 peer.stableInfoSig = stableInfo?.sig
2960 peer.dynamicInfo = dynamicInfo?.data
2961 peer.dynamicInfoSig = dynamicInfo?.sig
2962 peer.isEgoPeer = isEgoPeer
2963 self.containerMO.addToPeers(peer)
2964
2965 self.model.registerPeer(with: permanentInfo)
2966 if let stableInfo = stableInfo {
2967 try self.model.update(stableInfo, forPeerWithID: peerID)
2968 }
2969 if let dynamicInfo = dynamicInfo {
2970 try self.model.update(dynamicInfo, forPeerWithID: peerID)
2971 }
2972 vouchers?.forEach { voucher in
2973 self.model.register(voucher)
2974
2975 if (peer.vouchers as? Set<TPVoucher> ?? Set()).filter({ $0.data == voucher.data && $0.sig == voucher.sig }).isEmpty {
2976 let voucherMO = VoucherMO(context: self.moc)
2977 voucherMO.voucherInfo = voucher.data
2978 voucherMO.voucherInfoSig = voucher.sig
2979 peer.addToVouchers(voucherMO)
2980 }
2981 }
2982 return peer
2983 }
2984
2985 /* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
2986 func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
2987 tlkShares: [CKKSTLKShare],
2988 egoPeerKeys: OctagonSelfPeerKeys,
2989 egoPeerDynamicInfo: TPPeerDynamicInfo,
2990 epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
2991 let newCKKSKeys = ckksKeys.filter { $0.newUpload }
2992 let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
2993
2994 let octagonSelfShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
2995 asPeer: egoPeerKeys,
2996 toPeer: egoPeerKeys,
2997 epoch: epoch)
2998 let extraShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
2999
3000 var peerShares: [TLKShare] = []
3001
3002 for keyset in newCKKSKeys {
3003 do {
3004 let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
3005 toAccessView: keyset.tlk.zoneID.zoneName)
3006 os_log("Planning to share %@ with peers %{public}@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
3007
3008 let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
3009 let viewPeerShares = try peers.map { receivingPeer in
3010 TLKShare.convert(ckksTLKShare: try CKKSTLKShare(keyset.tlk,
3011 as: egoPeerKeys,
3012 to: receivingPeer.permanentInfo,
3013 epoch: epoch,
3014 poisoned: 0))
3015 }
3016
3017 peerShares += viewPeerShares
3018 } catch {
3019 os_log("Unable to create TLKShares for keyset %@: %{public}@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
3020 }
3021 }
3022
3023 return (newViewKeys, octagonSelfShares + peerShares + extraShares)
3024 }
3025
3026 func onqueuePreparePeerForJoining(egoPeerID: String,
3027 peerPermanentInfo: TPPeerPermanentInfo,
3028 stableInfo: TPPeerStableInfo,
3029 sponsorID: String?,
3030 preapprovedKeys: [Data]?,
3031 vouchers: [SignedVoucher],
3032 egoPeerKeys: OctagonSelfPeerKeys) throws -> (Peer, TPPeerDynamicInfo) {
3033 let dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
3034 peerPermanentInfo: peerPermanentInfo,
3035 peerStableInfo: stableInfo,
3036 sponsorID: sponsorID,
3037 preapprovedKeys: preapprovedKeys,
3038 signing: egoPeerKeys.signingKey,
3039 currentMachineIDs: self.onqueueCurrentMIDList())
3040
3041 let userViewSyncability: TPPBPeerStableInfo_UserControllableViewStatus?
3042 if [.ENABLED, .DISABLED].contains(stableInfo.syncUserControllableViews) {
3043 // No change!
3044 userViewSyncability = nil
3045 } else {
3046 let newUserViewSyncability: TPPBPeerStableInfo_UserControllableViewStatus
3047
3048 if peerPermanentInfo.modelID.hasPrefix("AppleTV") ||
3049 peerPermanentInfo.modelID.hasPrefix("AudioAccessory") ||
3050 peerPermanentInfo.modelID.hasPrefix("Watch") {
3051 // Watches, TVs, and AudioAccessories always join as FOLLOWING.
3052 newUserViewSyncability = .FOLLOWING
3053 } else {
3054 // All other platforms select what the other devices say to do
3055 newUserViewSyncability = self.model.userViewSyncabilityConsensusAmongTrustedPeers(dynamicInfo)
3056 }
3057
3058 os_log("join: setting 'user view sync' control as: %{public}@", log: tplogDebug, type: .default,
3059 TPPBPeerStableInfo_UserControllableViewStatusAsString(newUserViewSyncability))
3060 userViewSyncability = newUserViewSyncability
3061 }
3062
3063 let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: StableChanges.change(viewStatus: userViewSyncability),
3064 permanentInfo: peerPermanentInfo,
3065 existingStableInfo: stableInfo,
3066 dynamicInfo: dynamicInfo,
3067 signingKeyPair: egoPeerKeys.signingKey)
3068
3069 let peer = Peer.with {
3070 $0.peerID = egoPeerID
3071 $0.permanentInfoAndSig = SignedPeerPermanentInfo(peerPermanentInfo)
3072 $0.stableInfoAndSig = SignedPeerStableInfo(newStableInfo ?? stableInfo)
3073 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
3074 $0.vouchers = vouchers
3075 }
3076
3077 return (peer, dynamicInfo)
3078 }
3079
3080 func join(voucherData: Data,
3081 voucherSig: Data,
3082 ckksKeys: [CKKSKeychainBackedKeySet],
3083 tlkShares: [CKKSTLKShare],
3084 preapprovedKeys: [Data]?,
3085 reply: @escaping (String?, [CKRecord], TPSyncingPolicy?, Error?) -> Void) {
3086 self.semaphore.wait()
3087 let reply: (String?, [CKRecord], TPSyncingPolicy?, Error?) -> Void = {
3088 os_log("join complete: %{public}@", log: tplogTrace, type: .info, traceError($3))
3089 self.semaphore.signal()
3090 reply($0, $1, $2, $3)
3091 }
3092
3093 self.fetchAndPersistChanges { error in
3094 guard error == nil else {
3095 reply(nil, [], nil, error)
3096 return
3097 }
3098
3099 // To join, you must know all policies that exist
3100 let allPolicyVersions = self.model.allPolicyVersions()
3101 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3102 if let error = policyFetchError {
3103 os_log("join: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3104 }
3105
3106 self.moc.performAndWait {
3107 guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
3108 reply(nil, [], nil, ContainerError.invalidVoucherOrSig)
3109 return
3110 }
3111 guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
3112 reply(nil, [], nil, ContainerError.sponsorNotRegistered(voucher.sponsorID))
3113 return
3114 }
3115
3116 // Fetch ego peer identity from local storage.
3117 guard let egoPeerID = self.containerMO.egoPeerID,
3118 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3119 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
3120 let egoStableData = self.containerMO.egoPeerStableInfo,
3121 let egoStableSig = self.containerMO.egoPeerStableInfoSig
3122 else {
3123 reply(nil, [], nil, ContainerError.noPreparedIdentity)
3124 return
3125 }
3126
3127 let keyFactory = TPECPublicKeyFactory()
3128 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3129 reply(nil, [], nil, ContainerError.invalidPermanentInfoOrSig)
3130 return
3131 }
3132 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
3133 reply(nil, [], nil, ContainerError.invalidStableInfoOrSig)
3134 return
3135 }
3136 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
3137 os_log("join: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
3138 self.onqueueTTRUntrusted()
3139 reply(nil, [], nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
3140 return
3141 }
3142
3143 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3144 guard let egoPeerKeys = egoPeerKeys else {
3145 os_log("Don't have my own peer keys; can't join: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
3146 reply(nil, [], nil, error)
3147 return
3148 }
3149 self.moc.performAndWait {
3150 let peer: Peer
3151 let newDynamicInfo: TPPeerDynamicInfo
3152 do {
3153 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
3154 peerPermanentInfo: selfPermanentInfo,
3155 stableInfo: selfStableInfo,
3156 sponsorID: sponsor.peerID,
3157 preapprovedKeys: preapprovedKeys,
3158 vouchers: [SignedVoucher.with {
3159 $0.voucher = voucher.data
3160 $0.sig = voucher.sig
3161 }, ],
3162 egoPeerKeys: egoPeerKeys)
3163 } catch {
3164 os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3165 reply(nil, [], nil, error)
3166 return
3167 }
3168
3169 guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3170 os_log("Unable to create new peer stable info for joining", log: tplogDebug, type: .default)
3171 reply(nil, [], nil, ContainerError.invalidStableInfoOrSig)
3172 return
3173 }
3174
3175 let allTLKShares: [TLKShare]
3176 let viewKeys: [ViewKeys]
3177 do {
3178 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3179 tlkShares: tlkShares,
3180 egoPeerKeys: egoPeerKeys,
3181 egoPeerDynamicInfo: newDynamicInfo,
3182 epoch: Int(selfPermanentInfo.epoch))
3183 } catch {
3184 os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3185 reply(nil, [], nil, error)
3186 return
3187 }
3188
3189 do {
3190 try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
3191 stableInfo: peer.stableInfoAndSig.toStableInfo(),
3192 withSponsorID: sponsor.peerID)
3193 } catch {
3194 os_log("Error checking introduction: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3195 reply(nil, [], nil, error)
3196 return
3197 }
3198
3199 var bottle: Bottle
3200 do {
3201 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3202 } catch {
3203 reply(nil, [], nil, error)
3204 return
3205 }
3206
3207 os_log("Beginning join for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
3208 os_log("Join permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3209 os_log("Join permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3210 os_log("Join stableInfo: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
3211 os_log("Join stableInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
3212 os_log("Join dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3213 os_log("Join dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3214
3215 os_log("Join vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3216 os_log("Join voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3217
3218 os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3219
3220 do {
3221 os_log("Join peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3222 } catch {
3223 os_log("Join unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
3224 }
3225
3226 let changeToken = self.containerMO.changeToken ?? ""
3227 let request = JoinWithVoucherRequest.with {
3228 $0.changeToken = changeToken
3229 $0.peer = peer
3230 $0.bottle = bottle
3231 $0.tlkShares = allTLKShares
3232 $0.viewKeys = viewKeys
3233 }
3234 self.cuttlefish.joinWithVoucher(request) { response, error in
3235 guard let response = response, error == nil else {
3236 os_log("joinWithVoucher failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3237 reply(nil, [], nil, error ?? ContainerError.cloudkitResponseMissing)
3238 return
3239 }
3240
3241 self.moc.performAndWait {
3242 do {
3243 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3244 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3245
3246 let syncingPolicy = try self.syncingPolicyFor(modelID: selfPermanentInfo.modelID,
3247 stableInfo: peerStableInfo)
3248
3249 try self.onQueuePersist(changes: response.changes)
3250 os_log("JoinWithVoucher succeeded", log: tplogDebug)
3251
3252 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3253 reply(egoPeerID, keyHierarchyRecords, syncingPolicy, nil)
3254 } catch {
3255 os_log("JoinWithVoucher failed: %{public}@", log: tplogDebug, String(describing: error))
3256 reply(nil, [], nil, error)
3257 }
3258 }
3259 }
3260 }
3261 }
3262 }
3263 }
3264 }
3265 }
3266
3267 func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
3268 self.semaphore.wait()
3269 let reply: (Bool, Bool, Bool, Bool, Error?) -> Void = {
3270 os_log("health check complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
3271 self.semaphore.signal()
3272 reply($0, $1, $2, $3, $4)
3273 }
3274
3275 os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
3276
3277 self.moc.performAndWait {
3278 guard let egoPeerID = self.containerMO.egoPeerID else {
3279 // No identity, nothing to do
3280 os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
3281 reply(false, false, false, false, ContainerError.noPreparedIdentity)
3282 return
3283 }
3284 let request = GetRepairActionRequest.with {
3285 $0.peerID = egoPeerID
3286 $0.requiresEscrowCheck = requiresEscrowCheck
3287 }
3288
3289 self.cuttlefish.getRepairAction(request) { response, error in
3290 guard error == nil else {
3291 reply(false, false, false, false, error)
3292 return
3293 }
3294 guard let action = response?.repairAction else {
3295 os_log("repair response is empty, returning false", log: tplogDebug, type: .default)
3296 reply(false, false, false, false, nil)
3297 return
3298 }
3299 var postRepairAccount: Bool = false
3300 var postRepairEscrow: Bool = false
3301 var resetOctagon: Bool = false
3302 var leaveTrust: Bool = false
3303
3304 switch action {
3305 case .noAction:
3306 break
3307 case .postRepairAccount:
3308 postRepairAccount = true
3309 case .postRepairEscrow:
3310 postRepairEscrow = true
3311 case .resetOctagon:
3312 resetOctagon = true
3313 case .leaveTrust:
3314 leaveTrust = true
3315 case .UNRECOGNIZED:
3316 break
3317 }
3318 reply(postRepairAccount, postRepairEscrow, resetOctagon, leaveTrust, nil)
3319 }
3320 }
3321 }
3322
3323 func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
3324 self.semaphore.wait()
3325 let reply: (Data?, Error?) -> Void = {
3326 os_log("getSupportAppInfo complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3327 self.semaphore.signal()
3328 reply($0, $1)
3329 }
3330
3331 self.cuttlefish.getSupportAppInfo { response, error in
3332 guard let response = response, error == nil else {
3333 os_log("getSupportAppInfo failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3334 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3335 return
3336 }
3337
3338 guard let data = try? response.serializedData() else {
3339 reply(nil, ContainerError.failedToSerializeData)
3340 return
3341 }
3342
3343 reply(data, nil)
3344 }
3345 }
3346
3347 func preflightPreapprovedJoin(preapprovedKeys: [Data]?,
3348 reply: @escaping (Bool, Error?) -> Void) {
3349 self.semaphore.wait()
3350 let reply: (Bool, Error?) -> Void = {
3351 os_log("preflightPreapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3352 self.semaphore.signal()
3353 reply($0, $1)
3354 }
3355
3356 self.fetchAndPersistChanges { error in
3357 guard error == nil else {
3358 os_log("preflightPreapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3359 reply(false, error)
3360 return
3361 }
3362
3363 // We need to try to have all policy versions that our peers claim to behave
3364 let allPolicyVersions = self.model.allPolicyVersions()
3365 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3366 if let error = policyFetchError {
3367 os_log("preflightPreapprovedJoin: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3368 }
3369
3370 // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
3371
3372 guard !self.model.allPeerIDs().isEmpty else {
3373 // If, after fetch and handle changes, there's no peers, then we can likely establish.
3374 reply(true, nil)
3375 return
3376 }
3377
3378 guard let egoPeerID = self.containerMO.egoPeerID,
3379 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3380 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3381 else {
3382 os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3383 reply(false, ContainerError.noPreparedIdentity)
3384 return
3385 }
3386
3387 let keyFactory = TPECPublicKeyFactory()
3388 guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3389 os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
3390 reply(false, ContainerError.invalidPermanentInfoOrSig)
3391 return
3392 }
3393
3394 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
3395 os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3396 reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
3397 return
3398 }
3399
3400 let keysApprovingPeers = preapprovedKeys?.filter { key in
3401 self.model.hasPotentiallyTrustedPeer(withSigningKey: key)
3402 }
3403
3404 guard (keysApprovingPeers?.count ?? 0) > 0 else {
3405 os_log("preflightPreapprovedJoin: no reciprocal trust for existing peers", log: tplogDebug, type: .debug)
3406 reply(false, ContainerError.noPeersPreapprovedBySelf)
3407 return
3408 }
3409
3410 reply(true, nil)
3411 }
3412 }
3413 }
3414
3415 func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
3416 tlkShares: [CKKSTLKShare],
3417 preapprovedKeys: [Data]?,
3418 reply: @escaping (String?, [CKRecord], TPSyncingPolicy?, Error?) -> Void) {
3419 self.semaphore.wait()
3420 let reply: (String?, [CKRecord], TPSyncingPolicy?, Error?) -> Void = {
3421 os_log("preapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($3))
3422 self.semaphore.signal()
3423 reply($0, $1, $2, $3)
3424 }
3425
3426 self.fetchAndPersistChangesIfNeeded { error in
3427 guard error == nil else {
3428 os_log("preapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3429 reply(nil, [], nil, error)
3430 return
3431 }
3432 self.moc.performAndWait {
3433 // If, after fetch and handle changes, there's no peers, then fire off an establish
3434 // Note that if the establish fails, retrying this call might work.
3435 // That's up to the caller.
3436 if self.model.allPeerIDs().isEmpty {
3437 os_log("preapprovedJoin but no existing peers, attempting establish", log: tplogDebug, type: .debug)
3438
3439 self.onqueueEstablish(ckksKeys: ckksKeys,
3440 tlkShares: tlkShares,
3441 preapprovedKeys: preapprovedKeys,
3442 reply: reply)
3443 return
3444 }
3445
3446 // Fetch ego peer identity from local storage.
3447 guard let egoPeerID = self.containerMO.egoPeerID,
3448 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3449 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
3450 let egoStableData = self.containerMO.egoPeerStableInfo,
3451 let egoStableSig = self.containerMO.egoPeerStableInfoSig
3452 else {
3453 os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3454 reply(nil, [], nil, ContainerError.noPreparedIdentity)
3455 return
3456 }
3457
3458 let keyFactory = TPECPublicKeyFactory()
3459 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3460 reply(nil, [], nil, ContainerError.invalidPermanentInfoOrSig)
3461 return
3462 }
3463 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
3464 reply(nil, [], nil, ContainerError.invalidStableInfoOrSig)
3465 return
3466 }
3467
3468 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
3469 os_log("preapprovedJoin: self machineID %{public}@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
3470 self.onqueueTTRUntrusted()
3471 reply(nil, [], nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
3472 return
3473 }
3474 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3475 guard let egoPeerKeys = egoPeerKeys else {
3476 os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3477 reply(nil, [], nil, error)
3478 return
3479 }
3480
3481 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
3482 os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3483 reply(nil, [], nil, ContainerError.noPeersPreapprovePreparedIdentity)
3484 return
3485 }
3486
3487 self.moc.performAndWait {
3488 let peer: Peer
3489 let newDynamicInfo: TPPeerDynamicInfo
3490 do {
3491 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
3492 peerPermanentInfo: selfPermanentInfo,
3493 stableInfo: selfStableInfo,
3494 sponsorID: nil,
3495 preapprovedKeys: preapprovedKeys,
3496 vouchers: [],
3497 egoPeerKeys: egoPeerKeys)
3498 } catch {
3499 os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3500 reply(nil, [], nil, error)
3501 return
3502 }
3503
3504 guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3505 os_log("Unable to create new peer stable info for joining", log: tplogDebug, type: .default)
3506 reply(nil, [], nil, ContainerError.invalidStableInfoOrSig)
3507 return
3508 }
3509
3510 let allTLKShares: [TLKShare]
3511 let viewKeys: [ViewKeys]
3512 do {
3513 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3514 tlkShares: tlkShares,
3515 egoPeerKeys: egoPeerKeys,
3516 egoPeerDynamicInfo: newDynamicInfo,
3517 epoch: Int(selfPermanentInfo.epoch))
3518 } catch {
3519 os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3520 reply(nil, [], nil, error)
3521 return
3522 }
3523
3524 var bottle: Bottle
3525 do {
3526 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3527 } catch {
3528 reply(nil, [], nil, error)
3529 return
3530 }
3531
3532 os_log("Beginning preapprovedJoin for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
3533 os_log("preapprovedJoin permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3534 os_log("preapprovedJoin permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3535 os_log("preapprovedJoin stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
3536 os_log("preapprovedJoin stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
3537 os_log("preapprovedJoin dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3538 os_log("preapprovedJoin dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3539
3540 os_log("preapprovedJoin vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3541 os_log("preapprovedJoin voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3542
3543 os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3544
3545 do {
3546 os_log("preapprovedJoin peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3547 } catch {
3548 os_log("preapprovedJoin unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
3549 }
3550
3551 let changeToken = self.containerMO.changeToken ?? ""
3552 let request = JoinWithVoucherRequest.with {
3553 $0.changeToken = changeToken
3554 $0.peer = peer
3555 $0.bottle = bottle
3556 $0.tlkShares = allTLKShares
3557 $0.viewKeys = viewKeys
3558 }
3559 self.cuttlefish.joinWithVoucher(request) { response, error in
3560 guard let response = response, error == nil else {
3561 os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3562 reply(nil, [], nil, error ?? ContainerError.cloudkitResponseMissing)
3563 return
3564 }
3565
3566 self.moc.performAndWait {
3567 do {
3568 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3569 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3570
3571 let syncingPolicy = try self.syncingPolicyFor(modelID: selfPermanentInfo.modelID,
3572 stableInfo: peerStableInfo)
3573
3574 try self.onQueuePersist(changes: response.changes)
3575 os_log("preapprovedJoin succeeded", log: tplogDebug)
3576
3577 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3578 reply(egoPeerID, keyHierarchyRecords, syncingPolicy, nil)
3579 } catch {
3580 os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, String(describing: error))
3581 reply(nil, [], nil, error)
3582 }
3583 }
3584 }
3585 }
3586 }
3587 }
3588 }
3589 }
3590
3591 func update(deviceName: String?,
3592 serialNumber: String?,
3593 osVersion: String?,
3594 policyVersion: UInt64?,
3595 policySecrets: [String: Data]?,
3596 syncUserControllableViews: TPPBPeerStableInfo_UserControllableViewStatus?,
3597 reply: @escaping (TrustedPeersHelperPeerState?, TPSyncingPolicy?, Error?) -> Void) {
3598 self.semaphore.wait()
3599 let reply: (TrustedPeersHelperPeerState?, TPSyncingPolicy?, Error?) -> Void = {
3600 os_log("update complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
3601 self.semaphore.signal()
3602 reply($0, $1, $2)
3603 }
3604
3605 // Get (and save) the latest from cuttlefish
3606 let stableChanges = StableChanges(deviceName: deviceName,
3607 serialNumber: serialNumber,
3608 osVersion: osVersion,
3609 policyVersion: policyVersion,
3610 policySecrets: policySecrets,
3611 setSyncUserControllableViews: syncUserControllableViews)
3612 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
3613 }
3614
3615 func set(preapprovedKeys: [Data],
3616 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3617 self.semaphore.wait()
3618 let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
3619 os_log("setPreapprovedKeys complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3620 self.semaphore.signal()
3621 reply($0, $1)
3622 }
3623
3624 self.moc.performAndWait {
3625 os_log("setPreapprovedKeys: %@", log: tplogDebug, type: .default, preapprovedKeys)
3626
3627 guard let egoPeerID = self.containerMO.egoPeerID else {
3628 // No identity, nothing to do
3629 os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
3630 reply(nil, ContainerError.noPreparedIdentity)
3631 return
3632 }
3633 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3634 guard let signingKeyPair = signingKeyPair else {
3635 os_log("setPreapprovedKeys: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3636 reply(nil, error ?? ContainerError.unableToCreateKeyPair)
3637 return
3638 }
3639
3640 self.moc.performAndWait {
3641 let dynamicInfo: TPPeerDynamicInfo
3642 do {
3643 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3644 addingPeerIDs: nil,
3645 removingPeerIDs: nil,
3646 preapprovedKeys: preapprovedKeys,
3647 signing: signingKeyPair,
3648 currentMachineIDs: self.onqueueCurrentMIDList())
3649 } catch {
3650 os_log("setPreapprovedKeys: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3651 reply(nil, error)
3652 return
3653 }
3654
3655 os_log("setPreapprovedKeys: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
3656
3657 if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
3658 os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
3659
3660 // Calling this will fill in the peer status
3661 self.updateTrustIfNeeded { status, _, error in
3662 reply(status, error)
3663 }
3664 return
3665 }
3666
3667 os_log("setPreapprovedKeys: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3668 let request = UpdateTrustRequest.with {
3669 $0.changeToken = self.containerMO.changeToken ?? ""
3670 $0.peerID = egoPeerID
3671 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
3672 }
3673
3674 self.perform(updateTrust: request) { state, _, error in
3675 guard error == nil else {
3676 os_log("setPreapprovedKeys: failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
3677 reply(state, error)
3678 return
3679 }
3680
3681 os_log("setPreapprovedKeys: updateTrust succeeded", log: tplogDebug, type: .default)
3682 reply(state, nil)
3683 }
3684 }
3685 }
3686 }
3687 }
3688
3689 func updateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3690 tlkShares: [CKKSTLKShare],
3691 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3692 self.semaphore.wait()
3693 let reply: ([CKRecord]?, Error?) -> Void = {
3694 os_log("updateTLKs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3695 self.semaphore.signal()
3696 reply($0, $1)
3697 }
3698
3699 os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
3700
3701 self.moc.performAndWait {
3702 self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
3703 }
3704 }
3705
3706 func onqueueUpdateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3707 tlkShares: [CKKSTLKShare],
3708 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3709 guard let egoPeerID = self.containerMO.egoPeerID,
3710 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3711 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3712 else {
3713 os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
3714 reply(nil, ContainerError.noPreparedIdentity)
3715 return
3716 }
3717
3718 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
3719 os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
3720 reply(nil, ContainerError.invalidPermanentInfoOrSig)
3721 return
3722 }
3723
3724 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3725 guard let egoPeerKeys = egoPeerKeys else {
3726 os_log("Don't have my own peer keys; can't upload new TLKs: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
3727 reply(nil, error)
3728 return
3729 }
3730 self.moc.performAndWait {
3731 guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
3732 os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
3733 reply(nil, ContainerError.missingDynamicInfo)
3734 return
3735 }
3736
3737 let allTLKShares: [TLKShare]
3738 let viewKeys: [ViewKeys]
3739 do {
3740 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3741 tlkShares: tlkShares,
3742 egoPeerKeys: egoPeerKeys,
3743 egoPeerDynamicInfo: egoPeerDynamicInfo,
3744 epoch: Int(selfPermanentInfo.epoch))
3745 } catch {
3746 os_log("Unable to process keys before uploading: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3747 reply(nil, error)
3748 return
3749 }
3750
3751 let request = UpdateTrustRequest.with {
3752 $0.changeToken = self.containerMO.changeToken ?? ""
3753 $0.peerID = egoPeerID
3754 $0.tlkShares = allTLKShares
3755 $0.viewKeys = viewKeys
3756 }
3757
3758 self.cuttlefish.updateTrust(request) { response, error in
3759 guard error == nil else {
3760 reply(nil, error)
3761 return
3762 }
3763
3764 let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
3765 os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
3766 reply(keyHierarchyRecords, nil)
3767 }
3768 }
3769 }
3770 }
3771
3772 func getState(reply: @escaping (ContainerState) -> Void) {
3773 self.semaphore.wait()
3774 let reply: (ContainerState) -> Void = {
3775 os_log("getState complete: %{public}@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
3776 self.semaphore.signal()
3777 reply($0)
3778 }
3779
3780 self.moc.performAndWait {
3781 var state = ContainerState()
3782 state.egoPeerID = self.containerMO.egoPeerID
3783
3784 if self.containerMO.bottles != nil {
3785 self.containerMO.bottles!.forEach { bottle in
3786 state.bottles.insert(bottle as! BottleMO)
3787 }
3788 }
3789
3790 if self.containerMO.fullyViableEscrowRecords != nil {
3791 self.containerMO.fullyViableEscrowRecords!.forEach { record in
3792 state.escrowRecords.insert(record as! EscrowRecordMO)
3793 }
3794 }
3795
3796 if self.containerMO.partiallyViableEscrowRecords != nil {
3797 self.containerMO.partiallyViableEscrowRecords!.forEach { record in
3798 state.escrowRecords.insert(record as! EscrowRecordMO)
3799 }
3800 }
3801
3802 self.model.allPeers().forEach { peer in
3803 state.peers[peer.peerID] = peer
3804 }
3805 state.vouchers = Array(self.model.allVouchers())
3806 reply(state)
3807 }
3808 }
3809
3810 // This will only fetch changes if no changes have ever been fetched before
3811 func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
3812 self.moc.performAndWait {
3813 if self.containerMO.changeToken == nil {
3814 self.onqueueFetchAndPersistChanges(reply: reply)
3815 } else {
3816 reply(nil)
3817 }
3818 }
3819 }
3820
3821 func fetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3822 self.moc.performAndWait {
3823 self.onqueueFetchAndPersistChanges(reply: reply)
3824 }
3825 }
3826
3827 private func onqueueFetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3828 let request = FetchChangesRequest.with {
3829 $0.changeToken = self.containerMO.changeToken ?? ""
3830 }
3831 os_log("Fetching with change token: %{public}@", log: tplogDebug, type: .default, !request.changeToken.isEmpty ? request.changeToken : "empty")
3832
3833 self.cuttlefish.fetchChanges(request) { response, error in
3834 guard let response = response, error == nil else {
3835 switch error {
3836 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
3837 os_log("change token is expired; resetting local CK storage", log: tplogDebug, type: .default)
3838
3839 self.moc.performAndWait {
3840 do {
3841 try self.deleteLocalCloudKitData()
3842 } catch {
3843 os_log("Failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3844 reply(error)
3845 return
3846 }
3847
3848 self.fetchAndPersistChanges(reply: reply)
3849 }
3850
3851 return
3852 default:
3853 os_log("Fetch error is an unknown error: %{public}@", log: tplogDebug, type: .default, String(describing: error))
3854 }
3855
3856 os_log("Could not fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3857 reply(error)
3858 return
3859 }
3860
3861 do {
3862 try self.persist(changes: response.changes)
3863 } catch {
3864 os_log("Could not persist changes: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3865 reply(error)
3866 return
3867 }
3868
3869 if response.changes.more {
3870 os_log("persist: More changes indicated. Fetching...", log: tplogDebug, type: .default)
3871 self.fetchAndPersistChanges(reply: reply)
3872 return
3873 } else {
3874 os_log("persist: no more changes!", log: tplogDebug, type: .default)
3875 reply(nil)
3876 }
3877 }
3878 }
3879
3880 //
3881 // Check for delta update in trust lists, that should lead to update of TLKShares
3882 //
3883 private func haveTrustMemberChanges(newDynamicInfo: TPPeerDynamicInfo, oldDynamicInfo: TPPeerDynamicInfo?) -> Bool {
3884 guard let oldDynamicInfo = oldDynamicInfo else {
3885 return true
3886 }
3887 if newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs {
3888 return true
3889 }
3890 if newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs {
3891 return true
3892 }
3893 if newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals {
3894 return true
3895 }
3896 return false
3897 }
3898
3899 // Fetch and persist changes from Cuttlefish. If this results
3900 // in us calculating a new dynamicInfo then give the new dynamicInfo
3901 // to Cuttlefish. If Cuttlefish returns more changes then persist
3902 // them locally, update dynamicInfo if needed, and keep doing that
3903 // until dynamicInfo is unchanged and there are no more changes to fetch.
3904 //
3905 // Must be holding the semaphore to call this, and it remains
3906 // the caller's responsibility to release it after it completes
3907 // (i.e. after reply is invoked).
3908 internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
3909 peerChanges: Bool = false,
3910 reply: @escaping (TrustedPeersHelperPeerState?, TPSyncingPolicy?, Error?) -> Void) {
3911 self.fetchAndPersistChanges { error in
3912 if let error = error {
3913 os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3914 reply(nil, nil, error)
3915 return
3916 }
3917
3918 self.updateTrustIfNeeded(stableChanges: stableChanges, peerChanges: peerChanges, reply: reply)
3919 }
3920 }
3921
3922 // If this results in us calculating a new dynamicInfo then,
3923 // upload the new dynamicInfo
3924 // to Cuttlefish. If Cuttlefish returns more changes then persist
3925 // them locally, update dynamicInfo if needed, and keep doing that
3926 // until dynamicInfo is unchanged and there are no more changes to fetch.
3927 //
3928 // Must be holding the semaphore to call this, and it remains
3929 // the caller's responsibility to release it after it completes
3930 // (i.e. after reply is invoked).
3931 private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
3932 peerChanges: Bool = false,
3933 reply: @escaping (TrustedPeersHelperPeerState?, TPSyncingPolicy?, Error?) -> Void) {
3934 self.moc.performAndWait {
3935 guard let egoPeerID = self.containerMO.egoPeerID else {
3936 // No identity, nothing to do
3937 os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
3938 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil),
3939 nil,
3940 nil)
3941 return
3942 }
3943 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3944 guard let signingKeyPair = signingKeyPair else {
3945 os_log("updateTrustIfNeeded: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3946 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil),
3947 nil,
3948 error)
3949 return
3950 }
3951 guard let currentSelfInModel = self.model.peer(withID: egoPeerID) else {
3952 // Not in circle, nothing to do
3953 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
3954 os_log("updateTrustIfNeeded: ego peer is not in model, is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
3955 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3956 isPreapproved: isPreapproved,
3957 status: .unknown,
3958 memberChanges: peerChanges,
3959 unknownMachineIDs: false,
3960 osVersion: nil),
3961 nil,
3962 nil)
3963 return
3964 }
3965
3966 // We need to try to have all policy versions that our peers claim to behave
3967 let allPolicyVersions = self.model.allPolicyVersions()
3968 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3969 if let error = policyFetchError {
3970 os_log("updateTrustIfNeeded: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3971 }
3972
3973 self.moc.performAndWait {
3974 let dynamicInfo: TPPeerDynamicInfo
3975 var stableInfo: TPPeerStableInfo?
3976 do {
3977 // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
3978 // and then only load the key if it has changed and we need to sign a new one. This would also
3979 // help make our detection of change immune from non-canonical serialization of dynamicInfo.
3980 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3981 addingPeerIDs: nil,
3982 removingPeerIDs: nil,
3983 preapprovedKeys: nil,
3984 signing: signingKeyPair,
3985 currentMachineIDs: self.onqueueCurrentMIDList())
3986
3987 stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
3988 permanentInfo: currentSelfInModel.permanentInfo,
3989 existingStableInfo: currentSelfInModel.stableInfo,
3990 dynamicInfo: dynamicInfo,
3991 signingKeyPair: signingKeyPair)
3992 } catch {
3993 os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3994 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3995 isPreapproved: false,
3996 status: self.model.statusOfPeer(withID: egoPeerID),
3997 memberChanges: peerChanges,
3998 unknownMachineIDs: false,
3999 osVersion: nil),
4000 nil,
4001 error)
4002 return
4003 }
4004
4005 os_log("updateTrustIfNeeded: produced a stableInfo: %{public}@", log: tplogDebug, type: .default, String(describing: stableInfo))
4006 os_log("updateTrustIfNeeded: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
4007
4008 let peer = self.model.peer(withID: egoPeerID)
4009 if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
4010 dynamicInfo == peer?.dynamicInfo {
4011 os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
4012 // No change to the dynamicInfo: update the MID list now that we've reached a steady state
4013 do {
4014 self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
4015 try self.moc.save()
4016 } catch {
4017 os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
4018 }
4019
4020 let syncingPolicy: TPSyncingPolicy?
4021 do {
4022 if let peer = self.model.peer(withID: egoPeerID), let stableInfo = peer.stableInfo {
4023 syncingPolicy = try self.syncingPolicyFor(modelID: peer.permanentInfo.modelID, stableInfo: stableInfo)
4024 } else {
4025 syncingPolicy = nil
4026 }
4027 } catch {
4028 os_log("updateTrustIfNeeded: unable to compute a new syncing policy: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
4029 syncingPolicy = nil
4030 }
4031
4032 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
4033 isPreapproved: false,
4034 status: self.model.statusOfPeer(withID: egoPeerID),
4035 memberChanges: peerChanges,
4036 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
4037 osVersion: peer?.stableInfo?.osVersion),
4038 syncingPolicy,
4039 nil)
4040 return
4041 }
4042 // Check if we change that should trigger a notification that should trigger TLKShare updates
4043 let havePeerChanges = peerChanges || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
4044
4045 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
4046 os_log("updateTrustIfNeeded: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
4047 var request = UpdateTrustRequest.with {
4048 $0.changeToken = self.containerMO.changeToken ?? ""
4049 $0.peerID = egoPeerID
4050 $0.dynamicInfoAndSig = signedDynamicInfo
4051 }
4052 if let stableInfo = stableInfo {
4053 request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
4054 }
4055
4056 self.perform(updateTrust: request, stableChanges: stableChanges, peerChanges: havePeerChanges, reply: reply)
4057 }
4058 }
4059 }
4060 }
4061 }
4062
4063 private func perform(updateTrust request: UpdateTrustRequest,
4064 stableChanges: StableChanges? = nil,
4065 peerChanges: Bool = false,
4066 reply: @escaping (TrustedPeersHelperPeerState?, TPSyncingPolicy?, Error?) -> Void) {
4067 self.cuttlefish.updateTrust(request) { response, error in
4068 guard let response = response, error == nil else {
4069 os_log("UpdateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
4070 reply(nil, nil, error ?? ContainerError.cloudkitResponseMissing)
4071 return
4072 }
4073
4074 do {
4075 try self.persist(changes: response.changes)
4076 } catch {
4077 os_log("UpdateTrust failed: %{public}@", log: tplogDebug, String(describing: error))
4078 reply(nil, nil, error)
4079 return
4080 }
4081
4082 if response.changes.more {
4083 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
4084 peerChanges: peerChanges,
4085 reply: reply)
4086 } else {
4087 self.updateTrustIfNeeded(stableChanges: stableChanges,
4088 peerChanges: peerChanges,
4089 reply: reply)
4090 }
4091 }
4092 }
4093
4094 private func persist(changes: Changes) throws {
4095 // This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
4096 // So, do it ourself
4097 var outsideBlockError: Error?
4098
4099 self.moc.performAndWait {
4100 os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
4101 changes.differences.count,
4102 changes.more)
4103 os_log("persist: New change token: %{public}@", log: tplogDebug, type: .default, changes.changeToken)
4104
4105 do {
4106 try self.onQueuePersist(changes: changes)
4107 } catch {
4108 outsideBlockError = error
4109 }
4110 }
4111
4112 if let outsideBlockError = outsideBlockError {
4113 throw outsideBlockError
4114 }
4115 }
4116
4117 // Must be on moc queue to call this.
4118 // Changes are registered in the model and stored in moc.
4119 private func onQueuePersist(changes: Changes) throws {
4120 self.containerMO.changeToken = changes.changeToken
4121 self.containerMO.moreChanges = changes.more
4122
4123 if !changes.differences.isEmpty {
4124 self.model.clearViableBottles()
4125 os_log("escrow cache and viable bottles are no longer valid", log: tplogDebug, type: .default)
4126 self.onQueueRemoveEscrowCache()
4127 }
4128
4129 try changes.differences.forEach { peerDifference in
4130 if let operation = peerDifference.operation {
4131 switch operation {
4132 case .add(let peer), .update(let peer):
4133 try self.addOrUpdate(peer: peer)
4134 // Update containerMO ego data if it has changed.
4135 if peer.peerID == self.containerMO.egoPeerID {
4136 guard let stableInfoAndSig: TPPeerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
4137 break
4138 }
4139 self.containerMO.egoPeerStableInfo = stableInfoAndSig.data
4140 self.containerMO.egoPeerStableInfoSig = stableInfoAndSig.sig
4141 }
4142
4143 case .remove(let peer):
4144 self.model.deletePeer(withID: peer.peerID)
4145 if let peerMO = try self.fetchPeerMO(peerID: peer.peerID) {
4146 self.moc.delete(peerMO)
4147 }
4148 }
4149 }
4150 }
4151
4152 let signingKey = changes.recoverySigningPubKey
4153 let encryptionKey = changes.recoveryEncryptionPubKey
4154
4155 if !signingKey.isEmpty && !encryptionKey.isEmpty {
4156 self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
4157 }
4158 try self.moc.save()
4159 }
4160
4161 // Must be on moc queue to call this
4162 // Deletes all things that should come back from CloudKit. Keeps the egoPeer data, though.
4163 private func deleteLocalCloudKitData() throws {
4164 os_log("Deleting all CloudKit data", log: tplogDebug, type: .default)
4165
4166 do {
4167 let peerRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
4168 peerRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
4169 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: peerRequest))
4170
4171 // If we have an ego peer ID, keep the bottle associated with it
4172 if let peerID = self.containerMO.egoPeerID, let bottles = self.containerMO.bottles {
4173 let nonPeerBottles = NSSet(array: bottles.filter {
4174 switch $0 {
4175 case let bottleMO as BottleMO:
4176 return bottleMO.peerID != peerID
4177 default:
4178 return false
4179 }
4180 })
4181 self.containerMO.removeFromBottles(nonPeerBottles as NSSet)
4182 } else {
4183 let bottleRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Bottle")
4184 bottleRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
4185 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: bottleRequest))
4186 self.containerMO.bottles = nil
4187 }
4188
4189 self.containerMO.peers = nil
4190 self.containerMO.changeToken = nil
4191 self.containerMO.moreChanges = false
4192
4193 self.model = Container.loadModel(from: self.containerMO)
4194 try self.moc.save()
4195 } catch {
4196 os_log("Local delete failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
4197 throw error
4198 }
4199
4200 os_log("Saved model with %d peers", log: tplogDebug, type: .default, self.model.allPeerIDs().count)
4201 }
4202
4203 // Must be on moc queue to call this.
4204 private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
4205 self.model.setRecoveryKeys(
4206 TPRecoveryKeyPair(signingKeyData: signingKey, encryptionKeyData: encryptionKey))
4207
4208 self.containerMO.recoveryKeySigningSPKI = signingKey
4209 self.containerMO.recoveryKeyEncryptionSPKI = encryptionKey
4210 }
4211
4212 // Must be on moc queue to call this.
4213 private func addOrUpdate(peer: Peer) throws {
4214 if !self.model.hasPeer(withID: peer.peerID) {
4215 // Add:
4216 guard let permanentInfo = peer.permanentInfoAndSig.toPermanentInfo(peerID: peer.peerID) else {
4217 // Ignoring bad peer
4218 return
4219 }
4220 let stableInfo = peer.stableInfoAndSig.toStableInfo()
4221 let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo()
4222 let vouchers = peer.vouchers.compactMap {
4223 TPVoucher(infoWith: $0.voucher, sig: $0.sig)
4224 }
4225 let isEgoPeer = peer.peerID == self.containerMO.egoPeerID
4226 try self.registerPeerMO(permanentInfo: permanentInfo,
4227 stableInfo: stableInfo,
4228 dynamicInfo: dynamicInfo,
4229 vouchers: vouchers,
4230 isEgoPeer: isEgoPeer)
4231 } else {
4232 // Update:
4233 // The assertion here is that every peer registered in model is also present in containerMO
4234 guard let peerMO = try self.fetchPeerMO(peerID: peer.peerID) else {
4235 throw ContainerError.peerRegisteredButNotStored(peer.peerID)
4236 }
4237
4238 if let stableInfo = peer.stableInfoAndSig.toStableInfo() {
4239 try self.model.update(stableInfo, forPeerWithID: peer.peerID)
4240 // Pull the stableInfo back out of the model, and persist that.
4241 // The model checks signatures and clocks to prevent replay attacks.
4242 let modelPeer = self.model.peer(withID: peer.peerID)
4243 peerMO.stableInfo = modelPeer?.stableInfo?.data
4244 peerMO.stableInfoSig = modelPeer?.stableInfo?.sig
4245 }
4246 if let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo() {
4247 try self.model.update(dynamicInfo, forPeerWithID: peer.peerID)
4248 // Pull the dynamicInfo back out of the model, and persist that.
4249 // The model checks signatures and clocks to prevent replay attacks.
4250 let modelPeer = self.model.peer(withID: peer.peerID)
4251 peerMO.dynamicInfo = modelPeer?.dynamicInfo?.data
4252 peerMO.dynamicInfoSig = modelPeer?.dynamicInfo?.sig
4253 }
4254 peer.vouchers.forEach {
4255 if let voucher = TPVoucher(infoWith: $0.voucher, sig: $0.sig) {
4256 self.model.register(voucher)
4257 if peer.vouchers.filter({ $0.voucher == voucher.data && $0.sig == voucher.sig }).isEmpty {
4258 let voucherMO = VoucherMO(context: self.moc)
4259 voucherMO.voucherInfo = voucher.data
4260 voucherMO.voucherInfoSig = voucher.sig
4261 peerMO.addToVouchers(voucherMO)
4262 }
4263 }
4264 }
4265 }
4266 }
4267
4268 // Must be on moc queue to call this.
4269 private func fetchPeerMO(peerID: String) throws -> PeerMO? {
4270 let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
4271 fetch.predicate = NSPredicate(format: "peerID == %@ && container == %@", peerID, self.containerMO)
4272 let peers = try self.moc.fetch(fetch)
4273 return peers.first as? PeerMO
4274 }
4275
4276 // Must be on moc queue to call this.
4277 private func getPolicyDoc(_ policyVersion: UInt64) throws -> TPPolicyDocument {
4278 guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
4279 throw ContainerError.unknownPolicyVersion(policyVersion)
4280 }
4281 assert(policyVersion == policyDoc.version.versionNumber)
4282 if policyVersion == prevailingPolicyVersion.versionNumber {
4283 assert(policyDoc.version.policyHash == prevailingPolicyVersion.policyHash)
4284 }
4285 return policyDoc
4286 }
4287
4288 // Must be on moc queue to call this.
4289 private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
4290 permanentInfo: TPPeerPermanentInfo,
4291 existingStableInfo: TPPeerStableInfo?,
4292 dynamicInfo: TPPeerDynamicInfo,
4293 signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
4294 func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
4295 return (nil == change) || change == existing
4296 }
4297
4298 let policyOfPeers = try? self.model.policy(forPeerIDs: dynamicInfo.includedPeerIDs,
4299 candidatePeerID: permanentInfo.peerID,
4300 candidateStableInfo: existingStableInfo)
4301
4302 // Pick the best version of:
4303 // 1. The policy version asked for by the client
4304 // 2. The policy override set on this object (tests only)
4305 // 3. The max of our existing policyVersion, the highest policy used by our trusted peers, and the compile-time prevailing policy version
4306 let optimalPolicyVersionNumber = stableChanges?.policyVersion ??
4307 self.policyVersionOverride?.versionNumber ??
4308 max(existingStableInfo?.bestPolicyVersion().versionNumber ?? prevailingPolicyVersion.versionNumber,
4309 policyOfPeers?.version.versionNumber ?? prevailingPolicyVersion.versionNumber,
4310 prevailingPolicyVersion.versionNumber)
4311
4312 // Determine which recovery key we'd like to be using, given our current idea of who to trust
4313 let optimalRecoveryKey = self.model.bestRecoveryKey(for: existingStableInfo, dynamicInfo: dynamicInfo)
4314
4315 let intendedSyncUserControllableViews = stableChanges?.setSyncUserControllableViews?.sanitizeForPlatform(permanentInfo: permanentInfo)
4316
4317 if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
4318 noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
4319 noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
4320 noChange(optimalPolicyVersionNumber, existingStableInfo?.bestPolicyVersion().versionNumber) &&
4321 noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
4322 noChange(optimalRecoveryKey?.signingKeyData, existingStableInfo?.recoverySigningPublicKey) &&
4323 noChange(optimalRecoveryKey?.encryptionKeyData, existingStableInfo?.recoveryEncryptionPublicKey) &&
4324 noChange(intendedSyncUserControllableViews, existingStableInfo?.syncUserControllableViews) {
4325 return nil
4326 }
4327
4328 // 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
4329 let optimalPolicyVersion = try self.getPolicyDoc(optimalPolicyVersionNumber).version
4330 let useFrozenPolicyVersion = optimalPolicyVersion.versionNumber >= frozenPolicyVersion.versionNumber
4331
4332 if let intendedSyncUserControllableViews = intendedSyncUserControllableViews {
4333 os_log("Intending to set user-controllable views to %{public}@", log: tplogTrace, type: .info, TPPBPeerStableInfo_UserControllableViewStatusAsString(intendedSyncUserControllableViews))
4334 }
4335
4336 return try self.model.createStableInfo(withFrozenPolicyVersion: useFrozenPolicyVersion ? frozenPolicyVersion : optimalPolicyVersion,
4337 flexiblePolicyVersion: useFrozenPolicyVersion ? optimalPolicyVersion : nil,
4338 policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
4339 syncUserControllableViews: intendedSyncUserControllableViews ?? existingStableInfo?.syncUserControllableViews ?? .UNKNOWN,
4340 deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
4341 serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
4342 osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
4343 signing: signingKeyPair,
4344 recoverySigningPubKey: optimalRecoveryKey?.signingKeyData,
4345 recoveryEncryptionPubKey: optimalRecoveryKey?.encryptionKeyData)
4346 }
4347
4348 private func assembleBottle(egoPeerID: String) throws -> Bottle {
4349 let bottleMoSet = containerMO.bottles as? Set<BottleMO>
4350
4351 var bottleMOs = bottleMoSet?.filter {
4352 $0.peerID == egoPeerID
4353 }
4354
4355 if let count = bottleMOs?.count {
4356 if count > 1 {
4357 throw ContainerError.tooManyBottlesForPeer
4358 // swiftlint:disable empty_count
4359 } else if count == 0 {
4360 // swiftlint:enable empty_count
4361 throw ContainerError.noBottleForPeer
4362 }
4363 } else {
4364 throw ContainerError.failedToAssembleBottle
4365 }
4366
4367 let BM: BottleMO? = (bottleMOs?.removeFirst())
4368
4369 let bottle = try Bottle.with {
4370 $0.peerID = egoPeerID
4371
4372 if let bottledPeerData = BM?.contents {
4373 $0.contents = bottledPeerData
4374 } else {
4375 throw ContainerError.failedToAssembleBottle
4376 }
4377
4378 if let bottledPeerEscrowSigningSPKI = BM?.escrowedSigningSPKI {
4379 $0.escrowedSigningSpki = bottledPeerEscrowSigningSPKI
4380 } else {
4381 throw ContainerError.failedToAssembleBottle
4382 }
4383
4384 if let bottledPeerSignatureUsingEscrowKey = BM?.signatureUsingEscrowKey {
4385 $0.signatureUsingEscrowKey = bottledPeerSignatureUsingEscrowKey
4386 } else {
4387 throw ContainerError.failedToAssembleBottle
4388 }
4389
4390 if let bottledPeerSignatureUsingPeerKey = BM?.signatureUsingPeerKey {
4391 $0.signatureUsingPeerKey = bottledPeerSignatureUsingPeerKey
4392 } else {
4393 throw ContainerError.failedToAssembleBottle
4394 }
4395
4396 if let bID = BM?.bottleID {
4397 $0.bottleID = bID
4398 } else {
4399 throw ContainerError.failedToAssembleBottle
4400 }
4401 }
4402
4403 return bottle
4404 }
4405
4406 func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
4407 self.semaphore.wait()
4408 let reply: (Error?) -> Void = {
4409 os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
4410 self.semaphore.signal()
4411 reply($0)
4412 }
4413
4414 var updatedRequest = request
4415
4416 if let egoPeerID = self.containerMO.egoPeerID {
4417 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, _ in
4418 updatedRequest.octagonEgoIdentity = (egoPeerKeys != nil)
4419 }
4420 }
4421
4422 self.moc.performAndWait {
4423 self.cuttlefish.reportHealth(updatedRequest) { _, error in
4424 guard error == nil else {
4425 reply(error)
4426 return
4427 }
4428 reply(nil)
4429 }
4430 }
4431 }
4432
4433 func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
4434 self.semaphore.wait()
4435 let reply: (Error?) -> Void = {
4436 os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
4437 self.semaphore.signal()
4438 reply($0)
4439 }
4440
4441 self.moc.performAndWait {
4442 self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { _, error in
4443 guard error == nil else {
4444 reply(error)
4445 return
4446 }
4447 reply(nil)
4448 }
4449 }
4450 }
4451 }