2 * Copyright (c) 2018 - 2020 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
29 import SecurityFoundation
31 let tplogDebug = OSLog(subsystem: "com.apple.security.trustedpeers", category: "debug")
32 let tplogTrace = OSLog(subsystem: "com.apple.security.trustedpeers", category: "trace")
34 let egoIdentitiesAccessGroup = "com.apple.security.egoIdentities"
36 extension ResetReason {
37 static func from(cuttlefishResetReason: CuttlefishResetReason) -> ResetReason {
38 switch cuttlefishResetReason {
40 return ResetReason.unknown
41 case .userInitiatedReset:
42 return ResetReason.userInitiatedReset
44 return ResetReason.healthCheck
45 case .noBottleDuringEscrowRecovery:
46 return ResetReason.noBottleDuringEscrowRecovery
47 case .legacyJoinCircle:
48 return ResetReason.legacyJoinCircle
50 return ResetReason.recoveryKey
52 return ResetReason.testGenerated
59 public enum ContainerError: Error {
60 case unableToCreateKeyPair
61 case noPreparedIdentity
62 case failedToStoreIdentity
63 case needsAuthentication
64 case missingStableInfo
65 case missingDynamicInfo
67 case invalidPermanentInfoOrSig
68 case invalidStableInfoOrSig
69 case invalidVoucherOrSig
70 case sponsorNotRegistered(String)
71 case unknownPolicyVersion(UInt64)
72 case preparedIdentityNotOnAllowedList(String)
73 case couldNotLoadAllowedList
74 case noPeersPreapprovePreparedIdentity
75 case policyDocumentDoesNotValidate
76 case tooManyBottlesForPeer
78 case restoreBottleFailed
79 case noBottlesForEscrowRecordID
80 case bottleDoesNotContainContents
81 case bottleDoesNotContainEscrowKeySignature
82 case bottleDoesNotContainerPeerKeySignature
83 case bottleDoesNotContainPeerID
84 case failedToCreateBottledPeer
85 case signatureVerificationFailed
86 case bottleDoesNotContainerEscrowKeySPKI
87 case failedToFetchEscrowContents
88 case failedToCreateRecoveryKey
89 case untrustedRecoveryKeys
91 case recoveryKeysNotEnrolled
92 case bottleCreatingPeerNotFound
93 case unknownCloudKitError
94 case cloudkitResponseMissing
95 case failedToLoadSecret(errorCode: Int)
96 case failedToLoadSecretDueToType
97 case failedToAssembleBottle
99 case failedToStoreSecret(errorCode: Int)
100 case unknownSecurityFoundationError
101 case failedToSerializeData
102 case unknownInternalError
105 extension ContainerError: LocalizedError {
106 public var errorDescription: String? {
108 case .unableToCreateKeyPair:
109 return "unable to create key pair"
110 case .noPreparedIdentity:
111 return "no prepared identity"
112 case .failedToStoreIdentity:
113 return "failed to stored identity"
114 case .needsAuthentication:
115 return "needs authentication"
116 case .missingStableInfo:
117 return "missing stable info"
118 case .missingDynamicInfo:
119 return "missing dynamic info"
122 case .invalidPermanentInfoOrSig:
123 return "invalid permanent info or signature"
124 case .invalidStableInfoOrSig:
125 return "invalid stable info or signature"
126 case .invalidVoucherOrSig:
127 return "invalid voucher or signature"
128 case .sponsorNotRegistered(let s):
129 return "sponsor not registered: \(s)"
130 case .unknownPolicyVersion(let v):
131 return "unknown policy version: \(v)"
132 case .preparedIdentityNotOnAllowedList(let id):
133 return "prepared identity (\(id)) not on allowed machineID list"
134 case .couldNotLoadAllowedList:
135 return "could not load allowed machineID list"
136 case .noPeersPreapprovePreparedIdentity:
137 return "no peers preapprove prepared identity"
138 case .policyDocumentDoesNotValidate:
139 return "policy document from server doesn't validate"
140 case .tooManyBottlesForPeer:
141 return "too many bottles exist for peer"
142 case .noBottleForPeer:
143 return "no bottle exists for peer"
144 case .restoreBottleFailed:
145 return "failed to restore bottle"
146 case .noBottlesForEscrowRecordID:
147 return "0 bottles exist for escrow record id"
148 case .bottleDoesNotContainContents:
149 return "bottle does not contain encrypted contents"
150 case .bottleDoesNotContainEscrowKeySignature:
151 return "bottle does not contain escrow signature"
152 case .bottleDoesNotContainerPeerKeySignature:
153 return "bottle does not contain peer signature"
154 case .bottleDoesNotContainPeerID:
155 return "bottle does not contain peer id"
156 case .failedToCreateBottledPeer:
157 return "failed to create a bottled peer"
158 case .signatureVerificationFailed:
159 return "failed to verify signature"
160 case .bottleDoesNotContainerEscrowKeySPKI:
161 return "bottle does not contain escrowed key spki"
162 case .failedToFetchEscrowContents:
163 return "failed to fetch escrow contents"
164 case .failedToCreateRecoveryKey:
165 return "failed to create recovery keys"
166 case .untrustedRecoveryKeys:
167 return "untrusted recovery keys"
168 case .noBottlesPresent:
169 return "no bottle present"
170 case .recoveryKeysNotEnrolled:
171 return "recovery key is not enrolled with octagon"
172 case .bottleCreatingPeerNotFound:
173 return "The peer that created the bottle was not found"
174 case .unknownCloudKitError:
175 return "unknown error from cloudkit"
176 case .cloudkitResponseMissing:
177 return "Response missing from CloudKit"
178 case .failedToLoadSecret(errorCode: let errorCode):
179 return "failed to load secret: \(errorCode)"
180 case .failedToLoadSecretDueToType:
181 return "Failed to load secret due to type mismatch (value was not dictionary)"
182 case .failedToAssembleBottle:
183 return "failed to assemble bottle for peer"
185 return "peerID is invalid"
186 case .failedToStoreSecret(errorCode: let errorCode):
187 return "failed to store the secret in the keychain \(errorCode)"
188 case .unknownSecurityFoundationError:
189 return "SecurityFoundation returned an unknown type"
190 case .failedToSerializeData:
191 return "Failed to encode protobuf data"
192 case .unknownInternalError:
193 return "Internal code failed, but didn't return error"
198 extension ContainerError: CustomNSError {
200 public static var errorDomain: String {
201 return "com.apple.security.trustedpeers.container"
204 public var errorCode: Int {
206 case .unableToCreateKeyPair:
208 case .noPreparedIdentity:
210 case .failedToStoreIdentity:
212 case .needsAuthentication:
214 case .missingStableInfo:
216 case .missingDynamicInfo:
220 case .invalidPermanentInfoOrSig:
222 case .invalidVoucherOrSig:
224 case .invalidStableInfoOrSig:
226 case .sponsorNotRegistered:
228 case .unknownPolicyVersion:
230 case .preparedIdentityNotOnAllowedList:
232 case .noPeersPreapprovePreparedIdentity:
234 case .policyDocumentDoesNotValidate:
236 // 16 was invalidPeerID and failedToAssembleBottle
237 case .tooManyBottlesForPeer:
239 case .noBottleForPeer:
241 case .restoreBottleFailed:
243 case .noBottlesForEscrowRecordID:
245 case .bottleDoesNotContainContents:
247 case .bottleDoesNotContainEscrowKeySignature:
249 case .bottleDoesNotContainerPeerKeySignature:
251 case .bottleDoesNotContainPeerID:
253 case .failedToCreateBottledPeer:
255 case .signatureVerificationFailed:
257 // 27 was failedToLoadSecret*
258 case .bottleDoesNotContainerEscrowKeySPKI:
260 case .failedToFetchEscrowContents:
262 case .couldNotLoadAllowedList:
264 case .failedToCreateRecoveryKey:
266 case .untrustedRecoveryKeys:
268 case .noBottlesPresent:
270 case .recoveryKeysNotEnrolled:
272 case .bottleCreatingPeerNotFound:
274 case .unknownCloudKitError:
276 case .cloudkitResponseMissing:
278 case .failedToLoadSecret:
280 case .failedToLoadSecretDueToType:
282 case .failedToStoreSecret:
284 case .failedToAssembleBottle:
288 case .unknownSecurityFoundationError:
290 case .failedToSerializeData:
292 case .unknownInternalError:
297 public var underlyingError: NSError? {
299 case .failedToLoadSecret(errorCode: let errorCode):
300 return NSError(domain: "securityd", code: errorCode)
301 case .failedToStoreSecret(errorCode: let errorCode):
302 return NSError(domain: "securityd", code: errorCode)
308 public var errorUserInfo: [String: Any] {
309 var ret = [String: Any]()
310 if let desc = self.errorDescription {
311 ret[NSLocalizedDescriptionKey] = desc
313 if let underlyingError = self.underlyingError {
314 ret[NSUnderlyingErrorKey] = underlyingError
320 internal func traceError(_ error: Error?) -> String {
321 if let error = error {
322 return "error: \(String(describing: error))"
328 func saveSecret(_ secret: Data, label: String) throws {
330 let query: [CFString: Any] = [
331 kSecClass: kSecClassInternetPassword,
332 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
333 kSecUseDataProtectionKeychain: true,
334 kSecAttrAccessGroup: "com.apple.security.octagon",
335 kSecAttrSynchronizable: false,
336 kSecAttrDescription: label,
338 kSecValueData: secret,
341 var results: CFTypeRef?
342 var status = SecItemAdd(query as CFDictionary, &results)
344 if status == errSecSuccess {
348 if status == errSecDuplicateItem {
349 // Add every primary key attribute to this find dictionary
350 var findQuery: [CFString: Any] = [:]
351 findQuery[kSecClass] = query[kSecClass]
352 findQuery[kSecAttrSynchronizable] = query[kSecAttrSynchronizable]
353 findQuery[kSecAttrAccessGroup] = query[kSecAttrAccessGroup]
354 findQuery[kSecAttrServer] = query[kSecAttrDescription]
355 findQuery[kSecAttrPath] = query[kSecAttrPath]
356 findQuery[kSecUseDataProtectionKeychain] = query[kSecUseDataProtectionKeychain]
358 var updateQuery: [CFString: Any] = query
359 updateQuery[kSecClass] = nil
361 status = SecItemUpdate(findQuery as CFDictionary, updateQuery as CFDictionary)
363 if status != errSecSuccess {
364 throw ContainerError.failedToStoreSecret(errorCode: Int(status))
367 throw ContainerError.failedToStoreSecret(errorCode: Int(status))
371 func loadSecret(label: String) throws -> (Data?) {
374 let query: [CFString: Any] = [
375 kSecClass: kSecClassInternetPassword,
376 kSecAttrAccessGroup: "com.apple.security.octagon",
377 kSecAttrDescription: label,
378 kSecReturnAttributes: true,
379 kSecReturnData: true,
380 kSecAttrSynchronizable: false,
381 kSecMatchLimit: kSecMatchLimitOne,
384 var result: CFTypeRef?
385 let status = SecItemCopyMatching(query as CFDictionary, &result)
387 if status != errSecSuccess || result == nil {
388 throw ContainerError.failedToLoadSecret(errorCode: Int(status))
392 if let dictionary = result as? [CFString: Any] {
393 secret = dictionary[kSecValueData] as? Data
395 throw ContainerError.failedToLoadSecretDueToType
401 func saveEgoKeyPair(_ keyPair: _SFECKeyPair, identifier: String, resultHandler: @escaping (Bool, Error?) -> Void) {
402 let keychainManager = _SFKeychainManager.default()
403 let signingIdentity = _SFIdentity(keyPair: keyPair)
404 let accessibility = SFAccessibilityMakeWithMode(SFAccessibilityMode.accessibleWhenUnlocked) // class A
405 let accessPolicy = _SFAccessPolicy(accessibility: accessibility,
406 sharingPolicy: SFSharingPolicy.thisDeviceOnly)
407 accessPolicy.accessGroup = egoIdentitiesAccessGroup
408 keychainManager.setIdentity(signingIdentity,
409 forIdentifier: identifier,
410 accessPolicy: accessPolicy,
411 resultHandler: resultHandler)
414 func loadEgoKeyPair(identifier: String, resultHandler: @escaping (_SFECKeyPair?, Error?) -> Void) {
415 let keychainManager = _SFKeychainManager.default()
417 // FIXME constrain to egoIdentitiesAccessGroup, <rdar://problem/39597940>
418 keychainManager.identity(forIdentifier: identifier) { result in
419 switch result.resultType {
420 case .valueAvailable:
421 resultHandler(result.value?.keyPair as? _SFECKeyPair, nil)
422 case .needsAuthentication:
423 resultHandler(nil, ContainerError.needsAuthentication)
425 resultHandler(nil, result.error)
427 resultHandler(nil, ContainerError.unknownSecurityFoundationError)
432 func loadEgoKeys(peerID: String, resultHandler: @escaping (OctagonSelfPeerKeys?, Error?) -> Void) {
433 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: peerID)) { signingKey, error in
434 guard let signingKey = signingKey else {
435 os_log("Unable to load signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
436 resultHandler(nil, error)
440 loadEgoKeyPair(identifier: encryptionKeyIdentifier(peerID: peerID)) { encryptionKey, error in
441 guard let encryptionKey = encryptionKey else {
442 os_log("Unable to load encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
443 resultHandler(nil, error)
448 resultHandler(try OctagonSelfPeerKeys(peerID: peerID, signingKey: signingKey, encryptionKey: encryptionKey), nil)
450 resultHandler(nil, error)
456 func removeEgoKeysSync(peerID: String) throws -> Bool {
457 var resultSema = DispatchSemaphore(value: 0)
459 let keychainManager = _SFKeychainManager.default()
461 var retresultForSigningDeletion: Bool = false
462 var reterrorForSigningDeletion: Error?
464 //remove signing keys
465 keychainManager.removeItem(withIdentifier: signingKeyIdentifier(peerID: peerID)) { result, error in
466 retresultForSigningDeletion = result
467 reterrorForSigningDeletion = error
473 if let error = reterrorForSigningDeletion {
477 if retresultForSigningDeletion == false {
478 return retresultForSigningDeletion
481 // now let's do the same thing with the encryption keys
482 resultSema = DispatchSemaphore(value: 0)
483 var retresultForEncryptionDeletion: Bool = false
484 var reterrorForEncryptionDeletion: Error?
486 keychainManager.removeItem(withIdentifier: encryptionKeyIdentifier(peerID: peerID)) { result, error in
487 retresultForEncryptionDeletion = result
488 reterrorForEncryptionDeletion = error
493 if let error = reterrorForEncryptionDeletion {
497 return retresultForEncryptionDeletion && retresultForSigningDeletion
500 func loadEgoKeysSync(peerID: String) throws -> OctagonSelfPeerKeys {
501 // Gotta promote to synchronous; 'antipattern' ahoy
502 let resultSema = DispatchSemaphore(value: 0)
504 var result: OctagonSelfPeerKeys?
507 loadEgoKeys(peerID: peerID) { keys, error in
514 if let error = reserror {
518 if let result = result {
525 func signingKeyIdentifier(peerID: String) -> String {
526 return "signing-key " + peerID
529 func encryptionKeyIdentifier(peerID: String) -> String {
530 return "encryption-key " + peerID
533 func makeTLKShares(ckksTLKs: [CKKSKeychainBackedKey]?, asPeer: CKKSSelfPeer, toPeer: CKKSPeer, epoch: Int) throws -> [TLKShare] {
534 return try (ckksTLKs ?? []).map { tlk in
535 os_log("Making TLKShare for %@ for key %@", log: tplogDebug, type: .default, toPeer.description, tlk)
536 // Not being able to convert a TLK to a TLKShare is a failure, but not having a TLK is only half bad
538 return TLKShare.convert(ckksTLKShare: try CKKSTLKShare(tlk, as: asPeer, to: toPeer, epoch: epoch, poisoned: 0))
540 let nserror = error as NSError
541 if nserror.domain == "securityd" && nserror.code == errSecItemNotFound {
542 os_log("No TLK contents for %@, no TLK share possible", log: tplogDebug, type: .default, tlk)
552 func extract(tlkShares: [CKKSTLKShare], peer: OctagonSelfPeerKeys, model: TPModel) -> (Int64, Int64) {
553 os_log("Attempting to recover %d TLK shares for peer %{public}@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
554 var tlksRecovered: Set<String> = Set()
555 var sharesRecovered: Int64 = 0
557 for share in tlkShares {
558 guard share.receiverPeerID == peer.peerID else {
559 os_log("Skipping %@ (wrong peerID)", log: tplogDebug, type: .default, share)
564 var trustedPeers: [AnyHashable] = [peer]
566 if let egoPeer = model.peer(withID: peer.peerID) {
567 egoPeer.trustedPeerIDs.forEach { trustedPeerID in
568 if let peer = model.peer(withID: trustedPeerID) {
569 let peerObj = CKKSActualPeer(peerID: trustedPeerID,
570 encryptionPublicKey: (peer.permanentInfo.encryptionPubKey as! _SFECPublicKey),
571 signing: (peer.permanentInfo.signingPubKey as! _SFECPublicKey),
574 trustedPeers.append(peerObj)
576 os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
580 os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
583 let key = try share.recoverTLK(peer,
584 trustedPeers: Set(trustedPeers),
587 try key.saveMaterialToKeychain()
588 tlksRecovered.insert(key.uuid)
590 os_log("Recovered %@ (from %@)", log: tplogDebug, type: .default, key, share)
592 os_log("Failed to recover share %@: %{public}@", log: tplogDebug, type: .default, share, error as CVarArg)
596 return (Int64(tlksRecovered.count), sharesRecovered)
599 struct ContainerState {
600 var egoPeerID: String?
601 var peers: [String: TPPeer] = [:]
602 var vouchers: [TPVoucher] = []
603 var bottles = Set<BottleMO>()
604 var recoverySigningKey: Data?
605 var recoveryEncryptionKey: Data?
608 internal struct StableChanges {
609 let deviceName: String?
610 let serialNumber: String?
611 let osVersion: String?
612 let policyVersion: UInt64?
613 let policySecrets: [String: Data]?
616 // CoreData doesn't handle creating an identical model from an identical URL. Help it out.
617 private var nsObjectModels: [URL: NSManagedObjectModel] = [:]
618 private let nsObjectModelsQueue = DispatchQueue(label: "com.apple.security.TrustedPeersHelper.nsObjectModels")
619 func getOrMakeModel(url: URL) -> NSManagedObjectModel {
620 return nsObjectModelsQueue.sync {
621 if let model = nsObjectModels[url] {
624 let newModel = NSManagedObjectModel(contentsOf: url)!
625 nsObjectModels[url] = newModel
630 struct ContainerName: Hashable, CustomStringConvertible {
631 let container: String
634 func asSingleString() -> String {
635 // Just to be nice, hide the fact that multiple contexts exist on most machines
636 if self.context == OTDefaultContext {
637 return self.container
639 return self.container + "-" + self.context
643 var description: String {
644 return "Container(\(self.container),\(self.context))"
648 extension ContainerMO {
649 func egoStableInfo() -> TPPeerStableInfo? {
650 guard let egoStableData = self.egoPeerStableInfo,
651 let egoStableSig = self.egoPeerStableInfoSig else {
655 return TPPeerStableInfo(data: egoStableData, sig: egoStableSig)
659 /// This maps to a Cuttlefish service backed by a CloudKit container,
660 /// and a corresponding local Core Data persistent container.
662 /// Methods may be invoked concurrently.
663 class Container: NSObject {
664 let name: ContainerName
666 private let cuttlefish: CuttlefishAPIAsync
668 // Only one request (from Client) is permitted to be in progress at a time.
669 // That includes while waiting for network, i.e. one request must complete
670 // before the next can begin. Otherwise two requests could in parallel be
671 // fetching updates from Cuttlefish, with ensuing changeToken overwrites etc.
672 // This applies for mutating requests -- requests that can only read the current
673 // state (on the moc queue) do not need to.
674 internal let semaphore = DispatchSemaphore(value: 1)
676 // All Core Data access happens through moc: NSManagedObjectContext. The
677 // moc insists on having its own queue, and all operations must happen on
679 internal let moc: NSManagedObjectContext
681 // Rather than Container having its own dispatch queue, we use moc's queue
682 // to synchronise access to our own state as well. So the following instance
683 // variables must only be accessed within blocks executed by calling
684 // moc.perform() or moc.performAndWait().
685 internal var containerMO: ContainerMO
686 internal var model: TPModel
688 Construct a Container.
690 - Parameter name: The name the CloudKit container to which requests will be routed.
692 The "real" container that drives CKKS etc should be named `"com.apple.security.keychain"`.
693 Use other names for test containers such as for rawfish (rawhide-style testing for cuttlefish)
695 - Parameter persistentStoreURL: The location the local Core Data database for this container will be stored.
697 - Parameter cuttlefish: Interface to cuttlefish.
699 init(name: ContainerName, persistentStoreDescription: NSPersistentStoreDescription, cuttlefish: CuttlefishAPIAsync) throws {
700 var initError: Error?
701 var containerMO: ContainerMO?
704 // Set up Core Data stack
705 let url = Bundle(for: type(of: self)).url(forResource: "TrustedPeersHelper", withExtension: "momd")!
706 let mom = getOrMakeModel(url: url)
707 let persistentContainer = NSPersistentContainer(name: "TrustedPeersHelper", managedObjectModel: mom)
708 persistentContainer.persistentStoreDescriptions = [persistentStoreDescription]
710 persistentContainer.loadPersistentStores { _, error in
713 if let initError = initError {
717 let moc = persistentContainer.newBackgroundContext()
718 moc.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
721 // Fetch an existing ContainerMO record if it exists, or create and save one
723 let containerFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Container")
724 containerFetch.predicate = NSPredicate(format: "name == %@", name.asSingleString())
725 let fetchedContainers = try moc.fetch(containerFetch)
726 if let container = fetchedContainers.first as? ContainerMO {
727 containerMO = container
729 containerMO = ContainerMO(context: moc)
730 containerMO!.name = name.asSingleString()
733 // Perform upgrades as needed
734 Container.onqueueUpgradeMachineIDSetToModel(container: containerMO!, moc: moc)
735 Container.onqueueUpgradeMachineIDSetToUseStatus(container: containerMO!, moc: moc)
737 model = Container.loadModel(from: containerMO!)
738 Container.ensureEgoConsistency(from: containerMO!, model: model!)
745 if let initError = initError {
751 self.containerMO = containerMO!
752 self.cuttlefish = cuttlefish
757 // Must be on containerMO's moc queue to call this
758 internal static func loadModel(from containerMO: ContainerMO) -> TPModel {
759 // Populate model from persistent store
760 let model = TPModel(decrypter: Decrypter())
761 let keyFactory = TPECPublicKeyFactory()
762 let peers = containerMO.peers as? Set<PeerMO>
763 peers?.forEach { peer in
764 guard let permanentInfo = TPPeerPermanentInfo(peerID: peer.peerID!,
765 data: peer.permanentInfo! as Data,
766 sig: peer.permanentInfoSig! as Data,
767 keyFactory: keyFactory) else {
770 model.registerPeer(with: permanentInfo)
771 if let data = peer.stableInfo, let sig = peer.stableInfoSig {
772 if let stableInfo = TPPeerStableInfo(data: data as Data, sig: sig as Data) {
774 try model.update(stableInfo, forPeerWithID: permanentInfo.peerID)
776 os_log("loadModel unable to update stable info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
779 os_log("loadModel: peer %{public}@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
782 os_log("loadModel: peer %{public}@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
784 if let data = peer.dynamicInfo, let sig = peer.dynamicInfoSig {
785 if let dynamicInfo = TPPeerDynamicInfo(data: data as Data, sig: sig as Data) {
787 try model.update(dynamicInfo, forPeerWithID: permanentInfo.peerID)
789 os_log("loadModel unable to update dynamic info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
792 os_log("loadModel: peer %{public}@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
795 os_log("loadModel: peer %{public}@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
797 peer.vouchers?.forEach {
798 let v = $0 as! VoucherMO
799 if let data = v.voucherInfo, let sig = v.voucherInfoSig {
800 if let voucher = TPVoucher(infoWith: data, sig: sig) {
801 model.register(voucher)
807 if let recoveryKeySigningSPKI = containerMO.recoveryKeySigningSPKI,
808 let recoveryKeyEncyryptionSPKI = containerMO.recoveryKeyEncryptionSPKI {
809 model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: recoveryKeySigningSPKI, encryptionSPKI: recoveryKeyEncyryptionSPKI))
811 // If the ego peer has an RK set, tell the model to use that one
812 // This is a hack to work around TPH databases which don't have the RK set on the container due to previously running old software
813 if let egoStableInfo = containerMO.egoStableInfo(),
814 egoStableInfo.recoverySigningPublicKey.count > 0,
815 egoStableInfo.recoveryEncryptionPublicKey.count > 0 {
816 os_log("loadModel: recovery key not set in model, but is set on ego peer", log: tplogDebug, type: .default)
817 model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: egoStableInfo.recoverySigningPublicKey, encryptionSPKI: egoStableInfo.recoveryEncryptionPublicKey))
821 // Register persisted policies (cached from cuttlefish)
822 let policies = containerMO.policies as? Set<PolicyMO>
823 policies?.forEach { policyMO in
824 if let policyHash = policyMO.policyHash,
825 let policyData = policyMO.policyData {
826 if let policyDoc = TPPolicyDocument.policyDoc(withHash: policyHash, data: policyData) {
827 model.register(policyDoc)
832 // Register built-in policies
833 builtInPolicyDocuments().forEach { policyDoc in
834 model.register(policyDoc)
837 let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
838 let allowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.allowed.rawValue }.compactMap { $0.machineID })
839 let disallowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.disallowed.rawValue }.compactMap { $0.machineID })
841 os_log("loadModel: allowedMachineIDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
842 os_log("loadModel: disallowedMachineIDs: %{public}@", log: tplogDebug, type: .default, disallowedMachineIDs)
844 if allowedMachineIDs.isEmpty {
845 os_log("loadModel: no allowedMachineIDs?", log: tplogDebug, type: .default)
851 // Must be on containerMO's moc queue to call this
852 internal static func ensureEgoConsistency(from containerMO: ContainerMO, model: TPModel) {
853 guard let egoPeerID = containerMO.egoPeerID,
854 let egoStableData = containerMO.egoPeerStableInfo,
855 let egoStableSig = containerMO.egoPeerStableInfoSig
857 os_log("ensureEgoConsistency failed to find ego peer information", log: tplogDebug, type: .error)
861 guard let containerEgoStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
862 os_log("ensureEgoConsistency failed to create TPPeerStableInfo from container", log: tplogDebug, type: .error)
866 guard let modelStableInfo = model.getStableInfoForPeer(withID: egoPeerID) else {
867 os_log("ensureEgoConsistency failed to create TPPeerStableInfo from model", log: tplogDebug, type: .error)
871 if modelStableInfo.clock > containerEgoStableInfo.clock {
872 containerMO.egoPeerStableInfo = modelStableInfo.data
873 containerMO.egoPeerStableInfoSig = modelStableInfo.sig
878 static func dictionaryRepresentation(bottle: BottleMO) -> [String: Any] {
879 var dict: [String: String] = [:]
881 dict["bottleID"] = bottle.bottleID
882 dict["peerID"] = bottle.peerID
883 dict["signingSPKI"] = bottle.escrowedSigningSPKI?.base64EncodedString()
884 dict["signatureUsingPeerKey"] = bottle.signatureUsingPeerKey?.base64EncodedString()
885 dict["signatureUsingSPKI"] = bottle.signatureUsingEscrowKey?.base64EncodedString()
886 // ignore the bottle contents; they're mostly unreadable
891 static func peerdictionaryRepresentation(peer: TPPeer) -> [String: Any] {
892 var peerDict: [String: Any] = [
893 "permanentInfo": peer.permanentInfo.dictionaryRepresentation(),
894 "peerID": peer.peerID,
896 if let stableInfo = peer.stableInfo {
897 peerDict["stableInfo"] = stableInfo.dictionaryRepresentation()
899 if let dynamicInfo = peer.dynamicInfo {
900 peerDict["dynamicInfo"] = dynamicInfo.dictionaryRepresentation()
906 func onQueueDetermineLocalTrustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
907 let viablePeerCountsByModelID = self.model.viablePeerCountsByModelID()
908 let peerCountsByMachineID = self.model.peerCountsByMachineID()
909 if let egoPeerID = self.containerMO.egoPeerID {
910 var status = self.model.statusOfPeer(withID: egoPeerID)
911 var isExcluded: Bool = (status == .excluded)
913 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, loadError in
914 var returnError = loadError
916 guard returnError == nil else {
918 if let error = (loadError as NSError?) {
919 os_log("trust status: Unable to load ego keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
920 if error.code == errSecItemNotFound && error.domain == NSOSStatusErrorDomain {
921 os_log("trust status: Lost the ego key pair, returning 'excluded' in hopes of fixing up the identity", log: tplogDebug, type: .debug)
924 } else if error.code == errSecInteractionNotAllowed && error.domain == NSOSStatusErrorDomain {
925 // we have an item, but can't read it, suppressing error
931 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
933 viablePeerCountsByModelID: viablePeerCountsByModelID,
934 peerCountsByMachineID: peerCountsByMachineID,
935 isExcluded: isExcluded,
937 reply(egoStatus, returnError)
941 //ensure egoPeerKeys are populated
942 guard egoPeerKeys != nil else {
943 os_log("trust status: No error but Ego Peer Keys are nil", log: tplogDebug, type: .default)
944 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
946 viablePeerCountsByModelID: viablePeerCountsByModelID,
947 peerCountsByMachineID: peerCountsByMachineID,
951 reply(egoStatus, loadError)
955 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
957 viablePeerCountsByModelID: viablePeerCountsByModelID,
958 peerCountsByMachineID: peerCountsByMachineID,
959 isExcluded: isExcluded,
961 reply(egoStatus, nil)
966 // With no ego peer ID, either return 'excluded' if there are extant peers, or 'unknown' to signal no peers at all
967 if self.model.allPeerIDs().isEmpty {
968 os_log("No existing peers in account", log: tplogDebug, type: .debug)
969 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
971 viablePeerCountsByModelID: viablePeerCountsByModelID,
972 peerCountsByMachineID: peerCountsByMachineID,
975 reply(egoStatus, nil)
978 os_log("Existing peers in account, but we don't have a peer ID. We are excluded.", log: tplogDebug, type: .debug)
979 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
981 viablePeerCountsByModelID: viablePeerCountsByModelID,
982 peerCountsByMachineID: peerCountsByMachineID,
985 reply(egoStatus, nil)
991 func trustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
992 self.semaphore.wait()
993 let reply: (TrustedPeersHelperEgoPeerStatus, Error?) -> Void = {
994 // Suppress logging of successful replies here; it's not that useful
995 let logType: OSLogType = $1 == nil ? .debug : .info
996 os_log("trustStatus complete: %{public}@ %{public}@",
997 log: tplogTrace, type: logType, TPPeerStatusToString($0.egoStatus), traceError($1))
999 self.semaphore.signal()
1002 self.moc.performAndWait {
1003 // Knowledge of your peer status only exists if you know about other peers. If you haven't fetched, fetch.
1004 if self.containerMO.changeToken == nil {
1005 self.fetchAndPersistChanges { fetchError in
1006 guard fetchError == nil else {
1007 if let error = fetchError {
1008 os_log("Unable to fetch changes, trust status is unknown: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1011 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
1013 viablePeerCountsByModelID: [:],
1014 peerCountsByMachineID: [:],
1017 reply(egoStatus, fetchError)
1021 self.moc.performAndWait {
1022 self.onQueueDetermineLocalTrustStatus(reply: reply)
1026 self.onQueueDetermineLocalTrustStatus(reply: reply)
1031 func fetchTrustState(reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
1032 let reply: (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void = {
1033 os_log("fetch trust state complete: %{public}@ %{public}@",
1034 log: tplogTrace, type: .info, String(reflecting: $0), traceError($2))
1038 self.moc.performAndWait {
1039 if let egoPeerID = self.containerMO.egoPeerID,
1040 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1041 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig {
1043 let keyFactory = TPECPublicKeyFactory()
1044 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1045 os_log("fetchTrustState failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
1046 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
1050 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(permanentInfo.signingPubKey.spki())
1051 os_log("fetchTrustState: ego peer is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
1053 let egoStableInfo = self.model.getStableInfoForPeer(withID: egoPeerID)
1055 let egoPeerStatus = TrustedPeersHelperPeerState(peerID: egoPeerID,
1056 isPreapproved: isPreapproved,
1057 status: self.model.statusOfPeer(withID: egoPeerID),
1058 memberChanges: false,
1059 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
1060 osVersion: egoStableInfo?.osVersion)
1062 var tphPeers: [TrustedPeersHelperPeer] = []
1064 if let egoPeer = self.model.peer(withID: egoPeerID) {
1065 egoPeer.trustedPeerIDs.forEach { trustedPeerID in
1066 if let peer = self.model.peer(withID: trustedPeerID) {
1067 let peerViews = try? self.model.getViewsForPeer(peer.permanentInfo,
1068 stableInfo: peer.stableInfo)
1070 tphPeers.append(TrustedPeersHelperPeer(peerID: trustedPeerID,
1071 signingSPKI: peer.permanentInfo.signingPubKey.spki(),
1072 encryptionSPKI: peer.permanentInfo.encryptionPubKey.spki(),
1073 viewList: peerViews ?? Set()))
1075 os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
1079 os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
1082 os_log("Returning trust state: %{public}@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
1083 reply(egoPeerStatus, tphPeers, nil)
1085 // With no ego peer ID, there are no trusted peers
1086 os_log("No peer ID => no trusted peers", log: tplogDebug, type: .debug)
1087 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: false, unknownMachineIDs: false, osVersion: nil), [], nil)
1092 func dump(reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
1093 let reply: ([AnyHashable: Any]?, Error?) -> Void = {
1094 os_log("dump complete: %{public}@",
1095 log: tplogTrace, type: .info, traceError($1))
1098 self.moc.performAndWait {
1099 var d: [AnyHashable: Any] = [:]
1101 if let egoPeerID = self.containerMO.egoPeerID {
1102 if let peer = self.model.peer(withID: egoPeerID) {
1103 d["self"] = Container.peerdictionaryRepresentation(peer: peer)
1105 d["self"] = ["peerID": egoPeerID]
1111 d["peers"] = self.model.allPeers().filter { $0.peerID != self.containerMO.egoPeerID }.map { peer in
1112 Container.peerdictionaryRepresentation(peer: peer)
1115 d["vouchers"] = self.model.allVouchers().map { $0.dictionaryRepresentation() }
1117 if let bottles = self.containerMO.bottles as? Set<BottleMO> {
1118 d["bottles"] = bottles.map { Container.dictionaryRepresentation(bottle: $0) }
1123 let midList = self.onqueueCurrentMIDList()
1124 d["machineIDsAllowed"] = midList.machineIDs(in: .allowed).sorted()
1125 d["machineIDsDisallowed"] = midList.machineIDs(in: .disallowed).sorted()
1126 d["modelRecoverySigningPublicKey"] = self.model.recoverySigningPublicKey()
1127 d["modelRecoveryEncryptionPublicKey"] = self.model.recoveryEncryptionPublicKey()
1133 func dumpEgoPeer(reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
1134 let reply: (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void = {
1135 os_log("dumpEgoPeer complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
1136 reply($0, $1, $2, $3, $4)
1138 self.moc.performAndWait {
1139 guard let egoPeerID = self.containerMO.egoPeerID else {
1140 reply(nil, nil, nil, nil, ContainerError.noPreparedIdentity)
1144 guard let peer = self.model.peer(withID: egoPeerID) else {
1145 reply(egoPeerID, nil, nil, nil, nil)
1149 reply(egoPeerID, peer.permanentInfo, peer.stableInfo, peer.dynamicInfo, nil)
1153 func validatePeers(request: ValidatePeersRequest, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
1154 self.semaphore.wait()
1155 let reply: ([AnyHashable: Any]?, Error?) -> Void = {
1156 os_log("validatePeers complete %{public}@", log: tplogTrace, type: .info, traceError($1))
1157 self.semaphore.signal()
1161 self.cuttlefish.validatePeers(request) { response, error in
1162 os_log("ValidatePeers(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1163 guard let response = response, error == nil else {
1164 os_log("validatePeers failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1165 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
1169 var info: [AnyHashable: Any] = [:]
1170 info["health"] = response.validatorsHealth as AnyObject
1171 info["results"] = try? JSONSerialization.jsonObject(with: response.jsonUTF8Data())
1177 func reset(resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
1178 self.semaphore.wait()
1179 let reply: (Error?) -> Void = {
1180 os_log("reset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
1181 self.semaphore.signal()
1185 self.moc.performAndWait {
1186 let resetReason = ResetReason.from(cuttlefishResetReason: resetReason)
1187 let request = ResetRequest.with {
1188 $0.resetReason = resetReason
1190 self.cuttlefish.reset(request) { response, error in
1191 os_log("Reset(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1192 guard let response = response, error == nil else {
1193 os_log("reset failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1194 reply(error ?? ContainerError.cloudkitResponseMissing)
1198 // Erase container's persisted state
1199 self.moc.performAndWait {
1200 self.moc.delete(self.containerMO)
1201 self.containerMO = ContainerMO(context: self.moc)
1202 self.containerMO.name = self.name.asSingleString()
1203 self.model = Container.loadModel(from: self.containerMO)
1205 try self.onQueuePersist(changes: response.changes)
1206 os_log("reset succeded", log: tplogDebug, type: .default)
1209 os_log("reset persist failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1217 func localReset(reply: @escaping (Error?) -> Void) {
1218 self.semaphore.wait()
1219 let reply: (Error?) -> Void = {
1220 os_log("localReset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
1221 self.semaphore.signal()
1225 self.moc.performAndWait {
1227 // Erase container's persisted state
1228 self.moc.delete(self.containerMO)
1229 self.containerMO = ContainerMO(context: self.moc)
1230 self.containerMO.name = self.name.asSingleString()
1231 self.model = Container.loadModel(from: self.containerMO)
1241 func loadOrCreateKeyPair(privateKeyPersistentRef: Data?) throws -> _SFECKeyPair {
1242 if let privateKeyPersistentRef = privateKeyPersistentRef {
1243 return try TPHObjectiveC.fetchKeyPair(withPrivateKeyPersistentRef: privateKeyPersistentRef)
1245 let keySpecifier = _SFECKeySpecifier(curve: SFEllipticCurve.nistp384)
1246 guard let keyPair = _SFECKeyPair(randomKeyPairWith: keySpecifier) else {
1247 throw ContainerError.unableToCreateKeyPair
1253 // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion.versionNumber
1254 func prepare(epoch: UInt64,
1259 deviceName: String?,
1260 serialNumber: String,
1262 policyVersion: TPPolicyVersion?,
1263 policySecrets: [String: Data]?,
1264 signingPrivateKeyPersistentRef: Data?,
1265 encryptionPrivateKeyPersistentRef: Data?,
1266 reply: @escaping (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void) {
1267 self.semaphore.wait()
1268 let reply: (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void = {
1269 os_log("prepare complete peerID: %{public}@ %{public}@",
1270 log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($7))
1271 self.semaphore.signal()
1272 reply($0, $1, $2, $3, $4, $5, $6, $7)
1275 // Create a new peer identity with random keys, and store the keys in keychain
1276 let permanentInfo: TPPeerPermanentInfo
1277 let signingKeyPair: _SFECKeyPair
1278 let encryptionKeyPair: _SFECKeyPair
1280 signingKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: signingPrivateKeyPersistentRef)
1281 encryptionKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: encryptionPrivateKeyPersistentRef)
1283 // <rdar://problem/56270219> Octagon: use epoch transmitted across pairing channel
1284 permanentInfo = try TPPeerPermanentInfo(machineID: machineID,
1287 signing: signingKeyPair,
1288 encryptionKeyPair: encryptionKeyPair,
1289 peerIDHashAlgo: TPHashAlgo.SHA256)
1292 reply(nil, nil, nil, nil, nil, nil, nil, error)
1296 let peerID = permanentInfo.peerID
1298 let bottle: BottledPeer
1300 bottle = try BottledPeer(peerID: peerID,
1302 peerSigningKey: signingKeyPair,
1303 peerEncryptionKey: encryptionKeyPair,
1304 bottleSalt: bottleSalt)
1306 _ = try saveSecret(bottle.secret, label: peerID)
1308 os_log("bottle creation failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1309 reply(nil, nil, nil, nil, nil, nil, nil, error)
1313 saveEgoKeyPair(signingKeyPair, identifier: signingKeyIdentifier(peerID: peerID)) { success, error in
1314 guard success else {
1315 os_log("Unable to save signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1316 reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
1319 saveEgoKeyPair(encryptionKeyPair, identifier: encryptionKeyIdentifier(peerID: peerID)) { success, error in
1320 guard success else {
1321 os_log("Unable to save encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1322 reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
1326 let policyVersion = policyVersion ?? prevailingPolicyVersion
1327 self.fetchPolicyDocumentWithSemaphore(version: policyVersion) { policyDoc, policyFetchError in
1328 guard let policyDoc = policyDoc, policyFetchError == nil else {
1329 os_log("Unable to fetch policy: %{public}@", log: tplogDebug, type: .default, (policyFetchError as CVarArg?) ?? "error missing")
1330 reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.unknownInternalError)
1334 // Save the prepared identity as containerMO.egoPeer* and its bottle
1335 self.moc.performAndWait {
1338 let stableInfo = TPPeerStableInfo(clock: 1,
1339 frozenPolicyVersion: frozenPolicyVersion,
1340 flexiblePolicyVersion: policyDoc.version,
1341 policySecrets: policySecrets,
1342 deviceName: deviceName,
1343 serialNumber: serialNumber,
1344 osVersion: osVersion,
1345 signing: signingKeyPair,
1346 recoverySigningPubKey: nil,
1347 recoveryEncryptionPubKey: nil,
1350 self.containerMO.egoPeerID = permanentInfo.peerID
1351 self.containerMO.egoPeerPermanentInfo = permanentInfo.data
1352 self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
1353 self.containerMO.egoPeerStableInfo = stableInfo.data
1354 self.containerMO.egoPeerStableInfoSig = stableInfo.sig
1356 let bottleMO = BottleMO(context: self.moc)
1357 bottleMO.peerID = bottle.peerID
1358 bottleMO.bottleID = bottle.bottleID
1359 bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
1360 bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
1361 bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
1362 bottleMO.contents = bottle.contents
1364 self.containerMO.addToBottles(bottleMO)
1366 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
1370 reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, syncingViews, policy, nil)
1372 os_log("Unable to save identity: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1373 reply(nil, nil, nil, nil, nil, nil, nil, error)
1380 func getEgoEpoch(reply: @escaping (UInt64, Error?) -> Void) {
1381 let reply: (UInt64, Error?) -> Void = {
1382 os_log("getEgoEpoch complete: %d %{public}@", log: tplogTrace, type: .info, $0, traceError($1))
1386 self.moc.performAndWait {
1387 guard let egoPeerID = self.containerMO.egoPeerID else {
1388 reply(0, ContainerError.noPreparedIdentity)
1391 guard let egoPeer = self.model.peer(withID: egoPeerID) else {
1392 reply(0, ContainerError.noPreparedIdentity)
1396 reply(egoPeer.permanentInfo.epoch, nil)
1399 func establish(ckksKeys: [CKKSKeychainBackedKeySet],
1400 tlkShares: [CKKSTLKShare],
1401 preapprovedKeys: [Data]?,
1402 reply: @escaping (String?, [CKRecord], Error?) -> Void) {
1403 self.semaphore.wait()
1404 let reply: (String?, [CKRecord], Error?) -> Void = {
1405 os_log("establish complete peer: %{public}@ %{public}@",
1406 log: tplogTrace, type: .default, ($0 ?? "NULL") as CVarArg, traceError($2))
1407 self.semaphore.signal()
1411 self.moc.performAndWait {
1412 self.onqueueEstablish(ckksKeys: ckksKeys,
1413 tlkShares: tlkShares,
1414 preapprovedKeys: preapprovedKeys,
1415 reply: { peerID, ckrecords, _, _, error in
1416 reply(peerID, ckrecords, error)
1421 func onqueueTTRUntrusted() {
1422 let ttr = SecTapToRadar(tapToRadar: "Device not IDMS trusted",
1423 description: "Device not IDMS trusted",
1428 func fetchAfterEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
1429 tlkShares: [CKKSTLKShare],
1430 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
1431 self.moc.performAndWait {
1433 try self.deleteLocalCloudKitData()
1435 os_log("fetchAfterEstablish failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1436 reply(nil, [], nil, nil, error)
1439 self.onqueueFetchAndPersistChanges { error in
1440 guard error == nil else {
1441 os_log("fetchAfterEstablish failed to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1442 reply(nil, [], nil, nil, error)
1446 self.moc.performAndWait {
1447 guard let egoPeerID = self.containerMO.egoPeerID,
1448 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1449 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
1450 let egoStableData = self.containerMO.egoPeerStableInfo,
1451 let egoStableSig = self.containerMO.egoPeerStableInfoSig
1453 os_log("fetchAfterEstablish: failed to fetch egoPeerID", log: tplogDebug, type: .default)
1454 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
1457 guard self.model.hasPeer(withID: egoPeerID) else {
1458 os_log("fetchAfterEstablish: did not find peer %{public}@ in model", log: tplogDebug, type: .default, egoPeerID)
1459 reply(nil, [], nil, nil, ContainerError.invalidPeerID)
1462 let keyFactory = TPECPublicKeyFactory()
1463 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1464 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
1467 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
1468 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1469 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
1472 self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares) { ckrecords, error in
1473 guard error == nil else {
1474 os_log("fetchAfterEstablish failed to update TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1475 reply(nil, [], nil, nil, error)
1480 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
1481 stableInfo: selfStableInfo)
1482 os_log("fetchAfterEstablish succeeded", log: tplogDebug, type: .default)
1483 reply(egoPeerID, ckrecords ?? [], syncingViews, policy, nil)
1485 os_log("fetchAfterEstablish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1486 reply(nil, [], nil, nil, error)
1494 func onqueueEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
1495 tlkShares: [CKKSTLKShare],
1496 preapprovedKeys: [Data]?,
1497 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
1498 // Fetch ego peer identity from local storage.
1499 guard let egoPeerID = self.containerMO.egoPeerID,
1500 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1501 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
1502 let egoStableData = self.containerMO.egoPeerStableInfo,
1503 let egoStableSig = self.containerMO.egoPeerStableInfoSig
1505 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
1509 let keyFactory = TPECPublicKeyFactory()
1510 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1511 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
1514 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
1515 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1516 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
1519 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
1520 os_log("establish: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
1521 self.onqueueTTRUntrusted()
1522 reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
1526 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
1527 guard let egoPeerKeys = egoPeerKeys else {
1528 os_log("Don't have my own peer keys; can't establish: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1529 reply(nil, [], nil, nil, error)
1532 self.moc.performAndWait {
1533 let viewKeys: [ViewKeys] = ckksKeys.map(ViewKeys.convert)
1534 let allTLKShares: [TLKShare]
1536 let octagonShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk }, asPeer: egoPeerKeys, toPeer: egoPeerKeys, epoch: Int(selfPermanentInfo.epoch))
1537 let sosShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
1539 allTLKShares = octagonShares + sosShares
1541 os_log("Unable to make TLKShares for self: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1542 reply(nil, [], nil, nil, error)
1546 let dynamicInfo: TPPeerDynamicInfo
1548 dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
1549 peerPermanentInfo: selfPermanentInfo,
1550 peerStableInfo: selfStableInfo,
1552 preapprovedKeys: preapprovedKeys,
1553 signing: egoPeerKeys.signingKey,
1554 currentMachineIDs: self.onqueueCurrentMIDList())
1556 os_log("dynamic info: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
1558 reply(nil, [], nil, nil, error)
1562 let peer = Peer.with {
1563 $0.peerID = egoPeerID
1564 $0.permanentInfoAndSig.peerPermanentInfo = egoPermData
1565 $0.permanentInfoAndSig.sig = egoPermSig
1566 $0.stableInfoAndSig.peerStableInfo = egoStableData
1567 $0.stableInfoAndSig.sig = egoStableSig
1568 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
1573 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
1575 reply(nil, [], nil, nil, error)
1578 os_log("Beginning establish for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
1579 os_log("Establish permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
1580 os_log("Establish permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
1581 os_log("Establish stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
1582 os_log("Establish stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
1583 os_log("Establish dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
1584 os_log("Establish dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
1586 os_log("Establish introducing %d key sets, %d tlk shares", log: tplogDebug, type: .default, viewKeys.count, allTLKShares.count)
1589 os_log("Establish bottle: %{public}@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
1590 os_log("Establish peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
1592 os_log("Establish unable to encode bottle/peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
1595 let request = EstablishRequest.with {
1598 $0.viewKeys = viewKeys
1599 $0.tlkShares = allTLKShares
1601 self.cuttlefish.establish(request) { response, error in
1602 os_log("Establish(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1603 os_log("Establish: viewKeys: %{public}@", String(describing: viewKeys))
1604 guard let response = response, error == nil else {
1606 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.establishFailed):
1607 os_log("establish returned failed, trying fetch", log: tplogDebug, type: .default)
1608 self.fetchAfterEstablish(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
1611 os_log("establish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1612 reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
1618 os_log("Establish returned changes: %{public}@", log: tplogDebug, type: .default, try response.changes.jsonString())
1620 os_log("Establish returned changes, but they can't be serialized", log: tplogDebug, type: .default)
1623 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
1626 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
1627 stableInfo: selfStableInfo)
1629 try self.persist(changes: response.changes)
1631 guard response.changes.more == false else {
1632 os_log("establish succeeded, but more changes need fetching...", log: tplogDebug, type: .default)
1634 self.fetchAndPersistChanges { fetchError in
1635 guard fetchError == nil else {
1636 // This is an odd error condition: we might be able to fetch again and be in a good state...
1637 os_log("fetch-after-establish failed: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
1638 reply(nil, keyHierarchyRecords, nil, nil, fetchError)
1642 os_log("fetch-after-establish succeeded", log: tplogDebug, type: .default)
1643 reply(egoPeerID, keyHierarchyRecords, nil, nil, nil)
1648 os_log("establish succeeded", log: tplogDebug, type: .default)
1649 reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
1651 os_log("establish handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1652 reply(nil, keyHierarchyRecords, nil, nil, error)
1659 func setRecoveryKey(recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
1660 self.semaphore.wait()
1661 let reply: (Error?) -> Void = {
1662 os_log("setRecoveryKey complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
1663 self.semaphore.signal()
1667 os_log("beginning a setRecoveryKey", log: tplogDebug, type: .default)
1669 self.moc.performAndWait {
1670 guard let egoPeerID = self.containerMO.egoPeerID else {
1671 os_log("no prepared identity, cannot set recovery key", log: tplogDebug, type: .default)
1672 reply(ContainerError.noPreparedIdentity)
1676 var recoveryKeys: RecoveryKey
1678 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
1680 os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1681 reply(ContainerError.failedToCreateRecoveryKey)
1685 let signingPublicKey: Data = recoveryKeys.peerKeys.signingVerificationKey.keyData
1686 let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionVerificationKey.keyData
1688 os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
1689 os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
1691 guard let stableInfoData = self.containerMO.egoPeerStableInfo else {
1692 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
1693 reply(ContainerError.nonMember)
1696 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
1697 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
1698 reply(ContainerError.nonMember)
1701 guard let permInfoData = self.containerMO.egoPeerPermanentInfo else {
1702 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
1703 reply(ContainerError.nonMember)
1706 guard let permInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
1707 os_log("permInfoSig does not exist", log: tplogDebug, type: .default)
1708 reply(ContainerError.nonMember)
1711 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
1712 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1713 reply(ContainerError.invalidStableInfoOrSig)
1716 let keyFactory = TPECPublicKeyFactory()
1717 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permInfoData, sig: permInfoSig, keyFactory: keyFactory) else {
1718 os_log("cannot create TPPeerPermanentInfo", log: tplogDebug, type: .default)
1719 reply(ContainerError.invalidStableInfoOrSig)
1723 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
1724 guard let signingKeyPair = signingKeyPair else {
1725 os_log("handle: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1729 self.moc.performAndWait {
1731 let tlkShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
1732 asPeer: recoveryKeys.peerKeys,
1733 toPeer: recoveryKeys.peerKeys,
1734 epoch: Int(permanentInfo.epoch))
1736 let policyVersion = stableInfo.bestPolicyVersion()
1737 let policyDoc = try self.getPolicyDoc(policyVersion.versionNumber)
1739 let updatedStableInfo = TPPeerStableInfo(clock: stableInfo.clock + 1,
1740 frozenPolicyVersion: frozenPolicyVersion,
1741 flexiblePolicyVersion: policyDoc.version,
1742 policySecrets: stableInfo.policySecrets,
1743 deviceName: stableInfo.deviceName,
1744 serialNumber: stableInfo.serialNumber,
1745 osVersion: stableInfo.osVersion,
1746 signing: signingKeyPair,
1747 recoverySigningPubKey: signingPublicKey,
1748 recoveryEncryptionPubKey: encryptionPublicKey,
1750 let signedStableInfo = SignedPeerStableInfo(updatedStableInfo)
1752 let request = SetRecoveryKeyRequest.with {
1753 $0.peerID = egoPeerID
1754 $0.recoverySigningPubKey = signingPublicKey
1755 $0.recoveryEncryptionPubKey = encryptionPublicKey
1756 $0.stableInfoAndSig = signedStableInfo
1757 $0.tlkShares = tlkShares
1758 $0.changeToken = self.containerMO.changeToken ?? ""
1761 self.cuttlefish.setRecoveryKey(request) { response, error in
1762 os_log("SetRecoveryKey(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1763 guard let response = response, error == nil else {
1764 os_log("setRecoveryKey failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1765 reply(error ?? ContainerError.cloudkitResponseMissing)
1769 self.moc.performAndWait {
1771 self.containerMO.egoPeerStableInfo = updatedStableInfo.data
1772 self.containerMO.egoPeerStableInfoSig = updatedStableInfo.sig
1773 try self.onQueuePersist(changes: response.changes)
1775 os_log("setRecoveryKey succeeded", log: tplogDebug, type: .default)
1778 os_log("setRecoveryKey handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
1791 func currentSetContainerBottleID(bottleMOs: Set<BottleMO>, bottleID: String) -> (Bool) {
1792 let bmos = bottleMOs.filter {
1793 $0.bottleID == bottleID
1795 return !bmos.isEmpty
1798 func onqueueFindBottle(bottleID: String, reply: @escaping (BottleMO?, Error?) -> Void) {
1801 var bottles: Set<BottleMO> = []
1802 var shouldPerformFetch = false
1804 if let containerBottles = self.containerMO.bottles as? Set<BottleMO> {
1805 if self.currentSetContainerBottleID(bottleMOs: containerBottles, bottleID: bottleID) == false {
1806 shouldPerformFetch = true
1808 bottles = containerBottles
1811 shouldPerformFetch = true
1814 if shouldPerformFetch == true {
1815 self.fetchViableBottlesWithSemaphore { _, _, error in
1816 guard error == nil else {
1817 os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1822 guard let newBottles = self.containerMO.bottles as? Set<BottleMO> else {
1823 os_log("no bottles on container: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1824 reply(nil, ContainerError.noBottlesPresent)
1828 guard self.currentSetContainerBottleID(bottleMOs: newBottles, bottleID: bottleID) == true else {
1829 reply(nil, ContainerError.noBottlesForEscrowRecordID)
1833 os_log("onqueueFindBottle found bottle: %{public}@", log: tplogDebug, type: .default, newBottles)
1835 bottles = newBottles.filter {
1836 $0.bottleID == bottleID
1838 if bottles.count > 1 {
1839 reply(nil, ContainerError.tooManyBottlesForPeer)
1842 bmo = bottles.removeFirst()
1846 var filteredBottles = bottles.filter {
1847 $0.bottleID == bottleID
1849 if filteredBottles.count > 1 {
1850 reply(nil, ContainerError.tooManyBottlesForPeer)
1853 bmo = filteredBottles.removeFirst()
1858 func onqueueRecoverBottle(managedBottle: BottleMO, entropy: Data, bottleSalt: String) throws -> BottledPeer {
1859 guard let bottledContents = managedBottle.contents else {
1860 throw ContainerError.bottleDoesNotContainContents
1862 guard let signatureUsingEscrowKey = managedBottle.signatureUsingEscrowKey else {
1863 throw ContainerError.bottleDoesNotContainEscrowKeySignature
1866 guard let signatureUsingPeerKey = managedBottle.signatureUsingPeerKey else {
1867 throw ContainerError.bottleDoesNotContainerPeerKeySignature
1869 guard let sponsorPeerID = managedBottle.peerID else {
1870 throw ContainerError.bottleDoesNotContainPeerID
1873 //verify bottle signature using peer
1875 guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
1876 os_log("recover bottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
1877 throw ContainerError.bottleCreatingPeerNotFound
1879 guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
1880 os_log("recover bottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
1881 throw ContainerError.signatureVerificationFailed
1884 _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
1886 os_log("Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1887 throw ContainerError.failedToCreateBottledPeer
1891 return try BottledPeer(contents: bottledContents,
1893 bottleSalt: bottleSalt,
1894 signatureUsingEscrow: signatureUsingEscrowKey,
1895 signatureUsingPeerKey: signatureUsingPeerKey)
1897 os_log("Creation of Bottled Peer failed with bottle salt: %@,\nAttempting with empty bottle salt", bottleSalt)
1900 return try BottledPeer(contents: bottledContents,
1903 signatureUsingEscrow: signatureUsingEscrowKey,
1904 signatureUsingPeerKey: signatureUsingPeerKey)
1906 os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1907 throw ContainerError.failedToCreateBottledPeer
1912 func vouchWithBottle(bottleID: String,
1915 tlkShares: [CKKSTLKShare],
1916 reply: @escaping (Data?, Data?, Int64, Int64, Error?) -> Void) {
1917 self.semaphore.wait()
1918 let reply: (Data?, Data?, Int64, Int64, Error?) -> Void = {
1919 os_log("vouchWithBottle complete: %{public}@",
1920 log: tplogTrace, type: .info, traceError($4))
1921 self.semaphore.signal()
1922 reply($0, $1, $2, $3, $4)
1925 self.fetchAndPersistChangesIfNeeded { error in
1926 guard error == nil else {
1927 os_log("vouchWithBottle unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1928 reply(nil, nil, 0, 0, error)
1932 self.onqueueFindBottle(bottleID: bottleID) { returnedBMO, error in
1933 self.moc.performAndWait {
1934 guard error == nil else {
1935 os_log("vouchWithBottle unable to find bottle for escrow record id: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1936 reply(nil, nil, 0, 0, error)
1940 guard let bmo: BottleMO = returnedBMO else {
1941 os_log("vouchWithBottle bottle is nil: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1942 reply(nil, nil, 0, 0, error)
1946 guard let bottledContents = bmo.contents else {
1947 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainContents)
1950 guard let signatureUsingEscrowKey = bmo.signatureUsingEscrowKey else {
1951 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainEscrowKeySignature)
1955 guard let signatureUsingPeerKey = bmo.signatureUsingPeerKey else {
1956 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainerPeerKeySignature)
1959 guard let sponsorPeerID = bmo.peerID else {
1960 reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainPeerID)
1964 //verify bottle signature using peer
1966 guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
1967 os_log("vouchWithBottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
1968 reply(nil, nil, 0, 0, ContainerError.bottleCreatingPeerNotFound)
1971 guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
1972 os_log("vouchWithBottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
1973 reply(nil, nil, 0, 0, ContainerError.signatureVerificationFailed)
1977 _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
1979 os_log("vouchWithBottle: Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
1980 reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
1984 //create bottled peer
1985 let bottledPeer: BottledPeer
1987 bottledPeer = try BottledPeer(contents: bottledContents,
1989 bottleSalt: bottleSalt,
1990 signatureUsingEscrow: signatureUsingEscrowKey,
1991 signatureUsingPeerKey: signatureUsingPeerKey)
1993 os_log("Creation of Bottled Peer failed with bottle salt: %@,\nAttempting with empty bottle salt", bottleSalt)
1996 bottledPeer = try BottledPeer(contents: bottledContents,
1999 signatureUsingEscrow: signatureUsingEscrowKey,
2000 signatureUsingPeerKey: signatureUsingPeerKey)
2003 os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2004 reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
2009 os_log("Have a bottle for peer %{public}@", log: tplogDebug, type: .default, bottledPeer.peerID)
2011 // Extract any TLKs we have been given
2012 let (uniqueTLKsRecovered, totalSharesRecovered) = extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys, model: self.model)
2014 self.moc.performAndWait {
2015 // I must have an ego identity in order to vouch using bottle
2016 guard let egoPeerID = self.containerMO.egoPeerID else {
2017 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2018 reply(nil, nil, 0, 0, ContainerError.nonMember)
2021 guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
2022 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
2023 reply(nil, nil, 0, 0, ContainerError.nonMember)
2026 guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
2027 os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
2028 reply(nil, nil, 0, 0, ContainerError.nonMember)
2031 guard let stableInfo = self.containerMO.egoPeerStableInfo else {
2032 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
2033 reply(nil, nil, 0, 0, ContainerError.nonMember)
2036 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2037 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
2038 reply(nil, nil, 0, 0, ContainerError.nonMember)
2041 let keyFactory = TPECPublicKeyFactory()
2042 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2043 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2044 reply(nil, nil, 0, 0, ContainerError.invalidPermanentInfoOrSig)
2047 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2048 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2049 reply(nil, nil, 0, 0, ContainerError.invalidStableInfoOrSig)
2054 let voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2055 stableInfo: beneficiaryStableInfo,
2056 withSponsorID: sponsorPeerID,
2057 reason: TPVoucherReason.restore,
2058 signing: bottledPeer.peerKeys.signingKey)
2059 reply(voucher.data, voucher.sig, uniqueTLKsRecovered, totalSharesRecovered, nil)
2062 os_log("Error creating voucher with bottle: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2063 reply(nil, nil, 0, 0, error)
2072 func vouchWithRecoveryKey(recoveryKey: String,
2074 tlkShares: [CKKSTLKShare],
2075 reply: @escaping (Data?, Data?, Error?) -> Void) {
2076 self.semaphore.wait()
2077 let reply: (Data?, Data?, Error?) -> Void = {
2078 os_log("vouchWithRecoveryKey complete: %{public}@",
2079 log: tplogTrace, type: .info, traceError($2))
2080 self.semaphore.signal()
2084 self.moc.performAndWait {
2085 os_log("beginning a vouchWithRecoveryKey", log: tplogDebug, type: .default)
2087 // I must have an ego identity in order to vouch using bottle
2088 guard let egoPeerID = self.containerMO.egoPeerID else {
2089 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2090 reply(nil, nil, ContainerError.nonMember)
2093 guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
2094 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
2095 reply(nil, nil, ContainerError.nonMember)
2098 guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
2099 os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
2100 reply(nil, nil, ContainerError.nonMember)
2103 guard let stableInfo = self.containerMO.egoPeerStableInfo else {
2104 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
2105 reply(nil, nil, ContainerError.nonMember)
2108 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2109 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
2110 reply(nil, nil, ContainerError.nonMember)
2113 let keyFactory = TPECPublicKeyFactory()
2114 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2115 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2116 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2119 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2120 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2121 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2125 //create recovery key set
2126 var recoveryKeys: RecoveryKey
2128 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
2130 os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2131 reply(nil, nil, ContainerError.failedToCreateRecoveryKey)
2135 extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys, model: self.model)
2137 let signingPublicKey: Data = recoveryKeys.peerKeys.signingKey.publicKey.keyData
2138 let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionKey.publicKey.keyData
2140 os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
2141 os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
2143 guard self.model.isRecoveryKeyEnrolled() else {
2144 os_log("Recovery Key is not enrolled", log: tplogDebug, type: .default)
2145 reply(nil, nil, ContainerError.recoveryKeysNotEnrolled)
2149 //find matching peer containing recovery keys
2150 guard let sponsorPeerID = self.model.peerIDThatTrustsRecoveryKeys(TPRecoveryKeyPair(signingSPKI: signingPublicKey, encryptionSPKI: encryptionPublicKey)) else {
2151 os_log("Untrusted recovery key set", log: tplogDebug, type: .default)
2152 reply(nil, nil, ContainerError.untrustedRecoveryKeys)
2157 let voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2158 stableInfo: beneficiaryStableInfo,
2159 withSponsorID: sponsorPeerID,
2160 reason: TPVoucherReason.recoveryKey,
2161 signing: recoveryKeys.peerKeys.signingKey)
2162 reply(voucher.data, voucher.sig, nil)
2165 os_log("Error creating voucher using recovery key set: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2166 reply(nil, nil, error)
2172 func vouch(peerID: String,
2173 permanentInfo: Data,
2174 permanentInfoSig: Data,
2176 stableInfoSig: Data,
2177 ckksKeys: [CKKSKeychainBackedKeySet],
2178 reply: @escaping (Data?, Data?, Error?) -> Void) {
2179 self.semaphore.wait()
2180 let reply: (Data?, Data?, Error?) -> Void = {
2181 os_log("vouch complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2182 self.semaphore.signal()
2186 self.moc.performAndWait {
2187 // I must have an ego identity in order to vouch for someone else.
2188 guard let egoPeerID = self.containerMO.egoPeerID,
2189 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2190 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
2191 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2192 reply(nil, nil, ContainerError.nonMember)
2196 let keyFactory = TPECPublicKeyFactory()
2198 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2199 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2203 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: peerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2204 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2205 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2209 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2210 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2211 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2215 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
2216 guard let egoPeerKeys = egoPeerKeys else {
2217 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")
2218 reply(nil, nil, error)
2222 self.fetchPolicyDocumentsWithSemaphore(versions: Set([beneficiaryStableInfo.bestPolicyVersion()])) { _, policyFetchError in
2223 guard policyFetchError == nil else {
2224 os_log("Unknown policy for beneficiary: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2225 reply(nil, nil, policyFetchError)
2229 self.moc.performAndWait {
2230 let voucher: TPVoucher
2232 voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2233 stableInfo: beneficiaryStableInfo,
2234 withSponsorID: egoPeerID,
2235 reason: TPVoucherReason.secureChannel,
2236 signing: egoPeerKeys.signingKey)
2238 os_log("Error creating voucher: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2239 reply(nil, nil, error)
2243 // And generate and upload any tlkShares
2244 let tlkShares: [TLKShare]
2246 // Note that this might not be the whole list, so filter some of them out
2247 let peerViews = try? self.model.getViewsForPeer(beneficiaryPermanentInfo,
2248 stableInfo: beneficiaryStableInfo)
2250 // Note: we only want to send up TLKs for uploaded ckks zones
2251 let ckksTLKs = ckksKeys
2252 .filter { !$0.newUpload }
2253 .filter { peerViews?.contains($0.tlk.zoneID.zoneName) ?? false }
2256 tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
2257 asPeer: egoPeerKeys,
2258 toPeer: beneficiaryPermanentInfo,
2259 epoch: Int(selfPermanentInfo.epoch))
2261 os_log("Unable to make TLKShares for beneficiary %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, error as CVarArg)
2262 reply(nil, nil, error)
2266 guard !tlkShares.isEmpty else {
2267 os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
2268 reply(voucher.data, voucher.sig, nil)
2272 self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
2274 stableInfoAndSig: nil,
2275 dynamicInfoAndSig: nil,
2276 tlkShares: tlkShares,
2277 viewKeys: []) { response, error in
2278 guard let response = response, error == nil else {
2279 os_log("Unable to upload new tlkshares: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
2280 reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
2284 let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
2285 os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
2286 // We don't need to save these; CKKS will refetch them as needed
2288 reply(voucher.data, voucher.sig, nil)
2296 func departByDistrustingSelf(reply: @escaping (Error?) -> Void) {
2297 self.semaphore.wait()
2298 let reply: (Error?) -> Void = {
2299 os_log("departByDistrustingSelf complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
2300 self.semaphore.signal()
2304 self.moc.performAndWait {
2305 guard let egoPeerID = self.containerMO.egoPeerID else {
2306 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2307 reply(ContainerError.noPreparedIdentity)
2311 self.onqueueDistrust(peerIDs: [egoPeerID], reply: reply)
2315 func distrust(peerIDs: Set<String>,
2316 reply: @escaping (Error?) -> Void) {
2317 self.semaphore.wait()
2318 let reply: (Error?) -> Void = {
2319 os_log("distrust complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
2320 self.semaphore.signal()
2324 self.moc.performAndWait {
2325 guard let egoPeerID = self.containerMO.egoPeerID else {
2326 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2327 reply(ContainerError.noPreparedIdentity)
2331 guard !peerIDs.contains(egoPeerID) else {
2332 os_log("Self-distrust via peerID not allowed", log: tplogDebug, type: .default)
2333 reply(ContainerError.invalidPeerID)
2337 self.onqueueDistrust(peerIDs: peerIDs, reply: reply)
2341 func onqueueDistrust(peerIDs: Set<String>,
2342 reply: @escaping (Error?) -> Void) {
2344 guard let egoPeerID = self.containerMO.egoPeerID else {
2345 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2346 reply(ContainerError.noPreparedIdentity)
2350 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
2351 guard let signingKeyPair = signingKeyPair else {
2352 os_log("No longer have signing key pair; can't sign distrust: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
2357 self.moc.performAndWait {
2358 let dynamicInfo: TPPeerDynamicInfo
2360 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
2362 removingPeerIDs: Array(peerIDs),
2363 preapprovedKeys: nil,
2364 signing: signingKeyPair,
2365 currentMachineIDs: self.onqueueCurrentMIDList())
2368 os_log("Error preparing dynamic info: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
2373 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
2374 os_log("attempting distrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
2376 let request = UpdateTrustRequest.with {
2377 $0.changeToken = self.containerMO.changeToken ?? ""
2378 $0.peerID = egoPeerID
2379 $0.dynamicInfoAndSig = signedDynamicInfo
2381 self.cuttlefish.updateTrust(request) { response, error in
2382 os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2383 guard let response = response, error == nil else {
2384 os_log("updateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2385 reply(error ?? ContainerError.cloudkitResponseMissing)
2390 try self.persist(changes: response.changes)
2391 os_log("distrust succeeded", log: tplogDebug, type: .default)
2394 os_log("distrust handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
2402 func fetchEscrowContents(reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
2403 self.semaphore.wait()
2404 let reply: (Data?, String?, Data?, Error?) -> Void = {
2405 os_log("fetchEscrowContents complete: %{public}@", log: tplogTrace, type: .info, traceError($3))
2406 self.semaphore.signal()
2407 reply($0, $1, $2, $3)
2409 os_log("beginning a fetchEscrowContents", log: tplogDebug, type: .default)
2411 self.moc.performAndWait {
2412 guard let egoPeerID = self.containerMO.egoPeerID else {
2413 os_log("fetchEscrowContents failed", log: tplogDebug, type: .default)
2414 reply(nil, nil, nil, ContainerError.noPreparedIdentity)
2418 guard let bottles = self.containerMO.bottles as? Set<BottleMO> else {
2419 os_log("fetchEscrowContents failed", log: tplogDebug, type: .default)
2420 reply(nil, nil, nil, ContainerError.noBottleForPeer)
2424 var bmoSet = bottles.filter { $0.peerID == egoPeerID }
2425 let bmo = bmoSet.removeFirst()
2426 let bottleID = bmo.bottleID
2430 guard let loaded = try loadSecret(label: egoPeerID) else {
2431 os_log("fetchEscrowContents failed to load entropy", log: tplogDebug, type: .default)
2432 reply(nil, nil, nil, ContainerError.failedToFetchEscrowContents)
2437 os_log("fetchEscrowContents failed to load entropy: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2438 reply(nil, nil, nil, error)
2442 guard let signingPublicKey = bmo.escrowedSigningSPKI else {
2443 os_log("fetchEscrowContents no escrow signing spki", log: tplogDebug, type: .default)
2444 reply(nil, nil, nil, ContainerError.bottleDoesNotContainerEscrowKeySPKI)
2447 reply(entropy, bottleID, signingPublicKey, nil)
2451 func fetchViableBottles(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
2452 self.semaphore.wait()
2453 let reply: ([String]?, [String]?, Error?) -> Void = {
2454 os_log("fetchViableBottles complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2455 self.semaphore.signal()
2459 self.fetchViableBottlesWithSemaphore(reply: reply)
2462 func onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: TPCachedViableBottles) -> Bool {
2463 guard let egoPeerID = self.containerMO.egoPeerID else {
2464 os_log("bottleForEgoPeer: No identity.", log: tplogDebug, type: .default)
2467 guard let bottles: Set<BottleMO> = self.containerMO.bottles as? Set<BottleMO> else {
2468 os_log("bottleForEgoPeer: No Bottles.", log: tplogDebug, type: .default)
2471 var matchesCached: Bool = false
2472 for bottle in bottles {
2473 guard let bottleID: String = bottle.bottleID else {
2476 if bottle.peerID == egoPeerID && (cachedBottles.viableBottles.contains(bottleID) || cachedBottles.partialBottles.contains(bottleID)) {
2477 matchesCached = true
2481 return matchesCached
2484 func fetchViableBottlesWithSemaphore(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
2485 os_log("beginning a fetchViableBottles", log: tplogDebug, type: .default)
2487 let cachedBottles: TPCachedViableBottles = self.model.currentCachedViableBottlesSet()
2488 self.moc.performAndWait {
2489 if self.onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: cachedBottles)
2490 && (!cachedBottles.viableBottles.isEmpty || !cachedBottles.partialBottles.isEmpty) {
2491 os_log("returning from fetchViableBottles, using cached bottles", log: tplogDebug, type: .default)
2492 reply(cachedBottles.viableBottles, cachedBottles.partialBottles, nil)
2496 self.cuttlefish.fetchViableBottles { response, error in
2497 guard error == nil else {
2498 os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2499 reply(nil, nil, error)
2503 self.moc.performAndWait {
2505 guard let escrowPairs = response?.viableBottles else {
2506 os_log("fetchViableBottles returned no viable bottles", log: tplogDebug, type: .default)
2511 var partialPairs: [EscrowPair] = []
2512 if let partial = response?.partialBottles {
2513 partialPairs = partial
2515 os_log("fetchViableBottles returned no partially viable bottles, but that's ok", log: tplogDebug, type: .default)
2518 let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
2519 os_log("fetchViableBottles returned viable bottles: %{public}@", log: tplogDebug, type: .default, viableBottleIDs)
2521 let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
2522 os_log("fetchViableBottles returned partial bottles: %{public}@", log: tplogDebug, type: .default, partialBottleIDs)
2524 escrowPairs.forEach { pair in
2525 let bottle = pair.bottle
2527 // Save this bottle only if we don't already have it
2528 if let existingBottles = self.containerMO.bottles as? Set<BottleMO> {
2529 let matchingBottles: Set<BottleMO> = existingBottles.filter { existing in
2530 existing.peerID == bottle.peerID &&
2531 existing.bottleID == bottle.bottleID &&
2532 existing.escrowedSigningSPKI == bottle.escrowedSigningSpki &&
2533 existing.signatureUsingEscrowKey == bottle.signatureUsingEscrowKey &&
2534 existing.signatureUsingPeerKey == bottle.signatureUsingPeerKey &&
2535 existing.contents == bottle.contents
2537 if !matchingBottles.isEmpty {
2538 os_log("fetchViableBottles already knows about bottle", log: tplogDebug, type: .default, bottle.bottleID)
2543 let bmo = BottleMO(context: self.moc)
2544 bmo.peerID = bottle.peerID
2545 bmo.bottleID = bottle.bottleID
2546 bmo.escrowedSigningSPKI = bottle.escrowedSigningSpki
2547 bmo.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
2548 bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
2549 bmo.contents = bottle.contents
2551 os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
2552 self.containerMO.addToBottles(bmo)
2555 partialPairs.forEach { pair in
2556 let bottle = pair.bottle
2558 // Save this bottle only if we don't already have it
2559 if let existingBottles = self.containerMO.bottles as? Set<BottleMO> {
2560 let matchingBottles: Set<BottleMO> = existingBottles.filter { existing in
2561 existing.peerID == bottle.peerID &&
2562 existing.bottleID == bottle.bottleID &&
2563 existing.escrowedSigningSPKI == bottle.escrowedSigningSpki &&
2564 existing.signatureUsingEscrowKey == bottle.signatureUsingEscrowKey &&
2565 existing.signatureUsingPeerKey == bottle.signatureUsingPeerKey &&
2566 existing.contents == bottle.contents
2568 if !matchingBottles.isEmpty {
2569 os_log("fetchViableBottles already knows about bottle", log: tplogDebug, type: .default, bottle.bottleID)
2574 let bmo = BottleMO(context: self.moc)
2575 bmo.peerID = bottle.peerID
2576 bmo.bottleID = bottle.bottleID
2577 bmo.escrowedSigningSPKI = bottle.escrowedSigningSpki
2578 bmo.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
2579 bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
2580 bmo.contents = bottle.contents
2582 os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
2583 self.containerMO.addToBottles(bmo)
2588 os_log("fetchViableBottles saved bottles", log: tplogDebug, type: .default)
2589 let cached = TPCachedViableBottles(viableBottles: viableBottleIDs, partialBottles: partialBottleIDs)
2590 self.model.setViableBottles(cached)
2591 reply(viableBottleIDs, partialBottleIDs, nil)
2593 os_log("fetchViableBottles unable to save bottles: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2594 reply(nil, nil, error)
2602 func fetchCurrentPolicy(reply: @escaping (Set<String>?, TPPolicy?, Error?) -> Void) {
2603 self.semaphore.wait()
2604 let reply: (Set<String>?, TPPolicy?, Error?) -> Void = {
2605 os_log("fetchCurrentPolicy complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
2606 self.semaphore.signal()
2610 self.moc.performAndWait {
2611 guard let egoPeerID = self.containerMO.egoPeerID,
2612 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2613 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
2614 let stableInfoData = self.containerMO.egoPeerStableInfo,
2615 let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2616 os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
2617 reply(nil, nil, ContainerError.noPreparedIdentity)
2621 let keyFactory = TPECPublicKeyFactory()
2622 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2623 os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
2624 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2627 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
2628 os_log("fetchCurrentPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
2629 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2634 let (views, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
2635 reply(views, policy, nil)
2638 os_log("TPPolicyDocument failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2639 reply(nil, nil, error)
2645 func policyAndViewsFor(permanentInfo: TPPeerPermanentInfo, stableInfo: TPPeerStableInfo) throws -> (Set<String>, TPPolicy) {
2646 let policyVersion = stableInfo.bestPolicyVersion()
2647 guard let policyDocument = self.model.policy(withVersion: policyVersion.versionNumber) else {
2648 os_log("current policy is missing?", log: tplogDebug, type: .default)
2649 throw ContainerError.unknownPolicyVersion(policyVersion.versionNumber)
2652 let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
2653 let views = try policy.views(forModel: permanentInfo.modelID)
2655 return (views, policy)
2658 // All-or-nothing: return an error in case full list cannot be returned.
2659 // Completion handler data format: [version : [hash, data]]
2660 func fetchPolicyDocuments(versions: Set<TPPolicyVersion>,
2661 reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
2662 self.semaphore.wait()
2663 let reply: ([TPPolicyVersion: Data]?, Error?) -> Void = {
2664 os_log("fetchPolicyDocuments complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
2665 self.semaphore.signal()
2669 self.fetchPolicyDocumentsWithSemaphore(versions: versions) { policyDocuments, fetchError in
2670 reply(policyDocuments.flatMap { $0.mapValues { policyDoc in policyDoc.protobuf } }, fetchError)
2674 func fetchPolicyDocumentWithSemaphore(version: TPPolicyVersion,
2675 reply: @escaping (TPPolicyDocument?, Error?) -> Void) {
2676 self.fetchPolicyDocumentsWithSemaphore(versions: Set([version])) { versions, fetchError in
2677 guard fetchError == nil else {
2678 reply(nil, fetchError)
2682 guard let doc = versions?[version] else {
2683 os_log("fetchPolicyDocument: didn't return policy of version: %{public}@", log: tplogDebug, versions ?? "no versions")
2684 reply(nil, ContainerError.unknownPolicyVersion(version.versionNumber))
2692 func fetchPolicyDocumentsWithSemaphore(versions: Set<TPPolicyVersion>,
2693 reply: @escaping ([TPPolicyVersion: TPPolicyDocument]?, Error?) -> Void) {
2694 var remaining = versions
2695 var docs: [TPPolicyVersion: TPPolicyDocument] = [:]
2697 self.moc.performAndWait {
2698 for version in remaining {
2699 if let policydoc = try? self.getPolicyDoc(version.versionNumber), policydoc.version.policyHash == version.policyHash {
2700 docs[policydoc.version] = policydoc
2701 remaining.remove(version)
2706 guard !remaining.isEmpty else {
2711 let request = FetchPolicyDocumentsRequest.with {
2712 $0.keys = remaining.map { version in
2713 PolicyDocumentKey.with { $0.version = version.versionNumber; $0.hash = version.policyHash }}
2716 self.cuttlefish.fetchPolicyDocuments(request) { response, error in
2717 os_log("FetchPolicyDocuments(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2718 guard let response = response, error == nil else {
2719 os_log("FetchPolicyDocuments failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2720 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
2724 self.moc.performAndWait {
2725 for mapEntry in response.entries {
2726 // TODO: validate the policy's signature
2728 guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
2729 os_log("Can't make policy document with hash %{public}@ and data %{public}@",
2730 log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
2731 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2735 guard let expectedVersion = (remaining.filter { $0.versionNumber == doc.version.versionNumber }.first) else {
2736 os_log("Received a policy version we didn't request: %d", log: tplogDebug, type: .default, doc.version.versionNumber)
2737 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2741 guard expectedVersion.policyHash == doc.version.policyHash else {
2742 os_log("Requested hash %{public}@ does not match fetched hash %{public}@", log: tplogDebug, type: .default,
2743 expectedVersion.policyHash, doc.version.policyHash)
2744 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2748 remaining.remove(expectedVersion) // Server responses should be unique, let's enforce
2750 docs[doc.version] = doc
2751 self.model.register(doc)
2755 try self.moc.save() // if this fails callers might make bad data assumptions
2761 // Determine if there's anything left to fetch
2762 guard let unfetchedVersion = remaining.first else {
2763 // Nothing remaining? Success!
2768 reply(nil, ContainerError.unknownPolicyVersion(unfetchedVersion.versionNumber))
2773 // Must be on moc queue to call this.
2774 // Caller is responsible for saving the moc afterwards.
2776 private func registerPeerMO(permanentInfo: TPPeerPermanentInfo,
2777 stableInfo: TPPeerStableInfo? = nil,
2778 dynamicInfo: TPPeerDynamicInfo? = nil,
2779 vouchers: [TPVoucher]? = nil,
2780 isEgoPeer: Bool = false) throws -> PeerMO {
2781 let peerID = permanentInfo.peerID
2783 let peer = PeerMO(context: self.moc)
2784 peer.peerID = peerID
2785 peer.permanentInfo = permanentInfo.data
2786 peer.permanentInfoSig = permanentInfo.sig
2787 peer.stableInfo = stableInfo?.data
2788 peer.stableInfoSig = stableInfo?.sig
2789 peer.dynamicInfo = dynamicInfo?.data
2790 peer.dynamicInfoSig = dynamicInfo?.sig
2791 peer.isEgoPeer = isEgoPeer
2792 self.containerMO.addToPeers(peer)
2794 self.model.registerPeer(with: permanentInfo)
2795 if let stableInfo = stableInfo {
2796 try self.model.update(stableInfo, forPeerWithID: peerID)
2798 if let dynamicInfo = dynamicInfo {
2799 try self.model.update(dynamicInfo, forPeerWithID: peerID)
2801 vouchers?.forEach { voucher in
2802 self.model.register(voucher)
2803 let voucherMO = VoucherMO(context: self.moc)
2804 voucherMO.voucherInfo = voucher.data
2805 voucherMO.voucherInfoSig = voucher.sig
2806 peer.addToVouchers(voucherMO)
2811 /* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
2812 func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
2813 tlkShares: [CKKSTLKShare],
2814 egoPeerKeys: OctagonSelfPeerKeys,
2815 egoPeerDynamicInfo: TPPeerDynamicInfo,
2816 epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
2817 let newCKKSKeys = ckksKeys.filter { $0.newUpload }
2818 let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
2820 let octagonSelfShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
2821 asPeer: egoPeerKeys,
2822 toPeer: egoPeerKeys,
2824 let extraShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
2826 var peerShares: [TLKShare] = []
2828 for keyset in newCKKSKeys {
2830 let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
2831 toAccessView: keyset.tlk.zoneID.zoneName)
2832 os_log("Planning to share %@ with peers %{public}@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
2834 let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
2835 let viewPeerShares = try peers.map { receivingPeer in
2836 TLKShare.convert(ckksTLKShare: try CKKSTLKShare(keyset.tlk,
2838 to: receivingPeer.permanentInfo,
2843 peerShares += viewPeerShares
2846 os_log("Unable to create TLKShares for keyset %@: %{public}@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
2850 return (newViewKeys, octagonSelfShares + peerShares + extraShares)
2853 func onqueuePreparePeerForJoining(egoPeerID: String,
2854 peerPermanentInfo: TPPeerPermanentInfo,
2855 stableInfo: TPPeerStableInfo,
2857 preapprovedKeys: [Data]?,
2858 vouchers: [SignedVoucher],
2859 egoPeerKeys: OctagonSelfPeerKeys) throws -> (Peer, TPPeerDynamicInfo) {
2860 let dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
2861 peerPermanentInfo: peerPermanentInfo,
2862 peerStableInfo: stableInfo,
2863 sponsorID: sponsorID,
2864 preapprovedKeys: preapprovedKeys,
2865 signing: egoPeerKeys.signingKey,
2866 currentMachineIDs: self.onqueueCurrentMIDList())
2868 let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: nil,
2869 egoPeerID: egoPeerID,
2870 existingStableInfo: stableInfo,
2871 dynamicInfo: dynamicInfo,
2872 signingKeyPair: egoPeerKeys.signingKey)
2874 let peer = Peer.with {
2875 $0.peerID = egoPeerID
2876 $0.permanentInfoAndSig = SignedPeerPermanentInfo(peerPermanentInfo)
2877 $0.stableInfoAndSig = SignedPeerStableInfo(newStableInfo ?? stableInfo)
2878 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
2879 $0.vouchers = vouchers
2882 return (peer, dynamicInfo)
2885 func join(voucherData: Data,
2887 ckksKeys: [CKKSKeychainBackedKeySet],
2888 tlkShares: [CKKSTLKShare],
2889 preapprovedKeys: [Data]?,
2890 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
2891 self.semaphore.wait()
2892 let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
2893 os_log("join complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
2894 self.semaphore.signal()
2895 reply($0, $1, $2, $3, $4)
2898 self.fetchAndPersistChanges { error in
2899 guard error == nil else {
2900 reply(nil, [], nil, nil, error)
2904 // To join, you must know all policies that exist
2905 let allPolicyVersions = self.model.allPolicyVersions()
2906 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
2907 if let error = policyFetchError {
2908 os_log("join: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2911 self.moc.performAndWait {
2912 guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
2913 reply(nil, [], nil, nil, ContainerError.invalidVoucherOrSig)
2916 guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
2917 reply(nil, [], nil, nil, ContainerError.sponsorNotRegistered(voucher.sponsorID))
2921 // Fetch ego peer identity from local storage.
2922 guard let egoPeerID = self.containerMO.egoPeerID,
2923 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2924 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
2925 let egoStableData = self.containerMO.egoPeerStableInfo,
2926 let egoStableSig = self.containerMO.egoPeerStableInfoSig
2928 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
2932 let keyFactory = TPECPublicKeyFactory()
2933 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2934 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
2937 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
2938 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
2941 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
2942 os_log("join: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
2943 self.onqueueTTRUntrusted()
2944 reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
2948 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
2949 guard let egoPeerKeys = egoPeerKeys else {
2950 os_log("Don't have my own peer keys; can't join: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
2951 reply(nil, [], nil, nil, error)
2954 self.moc.performAndWait {
2956 let newDynamicInfo: TPPeerDynamicInfo
2958 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
2959 peerPermanentInfo: selfPermanentInfo,
2960 stableInfo: selfStableInfo,
2961 sponsorID: sponsor.peerID,
2962 preapprovedKeys: preapprovedKeys,
2963 vouchers: [SignedVoucher.with {
2964 $0.voucher = voucher.data
2965 $0.sig = voucher.sig
2967 egoPeerKeys: egoPeerKeys)
2969 os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2970 reply(nil, [], nil, nil, error)
2974 guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
2975 os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
2976 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
2980 let allTLKShares: [TLKShare]
2981 let viewKeys: [ViewKeys]
2983 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
2984 tlkShares: tlkShares,
2985 egoPeerKeys: egoPeerKeys,
2986 egoPeerDynamicInfo: newDynamicInfo,
2987 epoch: Int(selfPermanentInfo.epoch))
2989 os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
2990 reply(nil, [], nil, nil, error)
2995 try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
2996 stableInfo: peer.stableInfoAndSig.toStableInfo(),
2997 withSponsorID: sponsor.peerID)
2999 os_log("Error checking introduction: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3000 reply(nil, [], nil, nil, error)
3006 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3008 reply(nil, [], nil, nil, error)
3012 os_log("Beginning join for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
3013 os_log("Join permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3014 os_log("Join permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3015 os_log("Join stableInfo: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
3016 os_log("Join stableInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
3017 os_log("Join dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3018 os_log("Join dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3020 os_log("Join vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3021 os_log("Join voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3023 os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3026 os_log("Join peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3028 os_log("Join unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
3031 let changeToken = self.containerMO.changeToken ?? ""
3032 let request = JoinWithVoucherRequest.with {
3033 $0.changeToken = changeToken
3036 $0.tlkShares = allTLKShares
3037 $0.viewKeys = viewKeys
3039 self.cuttlefish.joinWithVoucher(request) { response, error in
3040 os_log("JoinWithVoucher(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3041 guard let response = response, error == nil else {
3042 os_log("joinWithVoucher failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3043 reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
3047 self.moc.performAndWait {
3049 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3050 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3052 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
3053 stableInfo: peerStableInfo)
3055 try self.onQueuePersist(changes: response.changes)
3056 os_log("JoinWithVoucher succeeded", log: tplogDebug)
3058 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3059 reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
3061 os_log("JoinWithVoucher failed: %{public}@", log: tplogDebug, String(describing: error))
3062 reply(nil, [], nil, nil, error)
3073 func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
3074 self.semaphore.wait()
3075 let reply: (Bool, Bool, Bool, Bool, Error?) -> Void = {
3076 os_log("health check complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
3077 self.semaphore.signal()
3078 reply($0, $1, $2, $3, $4)
3081 os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
3083 self.moc.performAndWait {
3084 guard let egoPeerID = self.containerMO.egoPeerID else {
3085 // No identity, nothing to do
3086 os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
3087 reply(false, false, false, false, ContainerError.noPreparedIdentity)
3090 let request = GetRepairActionRequest.with {
3091 $0.peerID = egoPeerID
3092 $0.requiresEscrowCheck = requiresEscrowCheck
3095 self.cuttlefish.getRepairAction(request) { response, error in
3096 guard error == nil else {
3097 reply(false, false, false, false, error)
3100 guard let action = response?.repairAction else {
3101 os_log("repair response is empty, returning false", log: tplogDebug, type: .default)
3102 reply(false, false, false, false, nil)
3105 var postRepairAccount: Bool = false
3106 var postRepairEscrow: Bool = false
3107 var resetOctagon: Bool = false
3108 var leaveTrust: Bool = false
3113 case .postRepairAccount:
3114 postRepairAccount = true
3115 case .postRepairEscrow:
3116 postRepairEscrow = true
3124 reply(postRepairAccount, postRepairEscrow, resetOctagon, leaveTrust, nil)
3129 func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
3130 self.semaphore.wait()
3131 let reply: (Data?, Error?) -> Void = {
3132 os_log("getSupportAppInfo complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3133 self.semaphore.signal()
3137 self.cuttlefish.getSupportAppInfo { response, error in
3138 os_log("getSupportAppInfo(): %{public}@, error: %{public}@", log: tplogDebug,
3139 "(\(String(describing: response))", "\(String(describing: error))")
3140 guard let response = response, error == nil else {
3141 os_log("getSupportAppInfo failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3142 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3146 guard let data = try? response.serializedData() else {
3147 reply(nil, ContainerError.failedToSerializeData)
3156 func preflightPreapprovedJoin(reply: @escaping (Bool, Error?) -> Void) {
3157 self.semaphore.wait()
3158 let reply: (Bool, Error?) -> Void = {
3159 os_log("preflightPreapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3160 self.semaphore.signal()
3164 self.fetchAndPersistChanges { error in
3165 guard error == nil else {
3166 os_log("preflightPreapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3171 // We need to try to have all policy versions that our peers claim to behave
3172 let allPolicyVersions = self.model.allPolicyVersions()
3173 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3174 if let error = policyFetchError {
3175 os_log("preflightPreapprovedJoin: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3178 // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
3180 guard !self.model.allPeerIDs().isEmpty else {
3181 // If, after fetch and handle changes, there's no peers, then we can likely establish.
3186 guard let egoPeerID = self.containerMO.egoPeerID,
3187 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3188 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3190 os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3191 reply(false, ContainerError.noPreparedIdentity)
3195 let keyFactory = TPECPublicKeyFactory()
3196 guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3197 os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
3198 reply(false, ContainerError.invalidPermanentInfoOrSig)
3202 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
3203 os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3204 reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
3213 func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
3214 tlkShares: [CKKSTLKShare],
3215 preapprovedKeys: [Data]?,
3216 reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
3217 self.semaphore.wait()
3218 let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
3219 os_log("preapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
3220 self.semaphore.signal()
3221 reply($0, $1, $2, $3, $4)
3224 self.fetchAndPersistChangesIfNeeded { error in
3225 guard error == nil else {
3226 os_log("preapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3227 reply(nil, [], nil, nil, error)
3230 self.moc.performAndWait {
3231 // If, after fetch and handle changes, there's no peers, then fire off an establish
3232 // Note that if the establish fails, retrying this call might work.
3233 // That's up to the caller.
3234 if self.model.allPeerIDs().isEmpty {
3235 os_log("preapprovedJoin but no existing peers, attempting establish", log: tplogDebug, type: .debug)
3236 self.onqueueEstablish(ckksKeys: ckksKeys,
3237 tlkShares: tlkShares,
3238 preapprovedKeys: preapprovedKeys,
3243 // Fetch ego peer identity from local storage.
3244 guard let egoPeerID = self.containerMO.egoPeerID,
3245 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3246 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
3247 let egoStableData = self.containerMO.egoPeerStableInfo,
3248 let egoStableSig = self.containerMO.egoPeerStableInfoSig
3250 os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3251 reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
3255 let keyFactory = TPECPublicKeyFactory()
3256 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3257 reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
3260 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
3261 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
3265 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
3266 os_log("preapprovedJoin: self machineID %{public}@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
3267 self.onqueueTTRUntrusted()
3268 reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
3271 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3272 guard let egoPeerKeys = egoPeerKeys else {
3273 os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3274 reply(nil, [], nil, nil, error)
3278 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
3279 os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3280 reply(nil, [], nil, nil, ContainerError.noPeersPreapprovePreparedIdentity)
3284 self.moc.performAndWait {
3287 let newDynamicInfo: TPPeerDynamicInfo
3289 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
3290 peerPermanentInfo: selfPermanentInfo,
3291 stableInfo: selfStableInfo,
3293 preapprovedKeys: preapprovedKeys,
3295 egoPeerKeys: egoPeerKeys)
3297 os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3298 reply(nil, [], nil, nil, error)
3302 guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3303 os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
3304 reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
3308 let allTLKShares: [TLKShare]
3309 let viewKeys: [ViewKeys]
3311 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3312 tlkShares: tlkShares,
3313 egoPeerKeys: egoPeerKeys,
3314 egoPeerDynamicInfo: newDynamicInfo,
3315 epoch: Int(selfPermanentInfo.epoch))
3317 os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3318 reply(nil, [], nil, nil, error)
3324 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3326 reply(nil, [], nil, nil, error)
3330 os_log("Beginning preapprovedJoin for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
3331 os_log("preapprovedJoin permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3332 os_log("preapprovedJoin permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3333 os_log("preapprovedJoin stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
3334 os_log("preapprovedJoin stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
3335 os_log("preapprovedJoin dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3336 os_log("preapprovedJoin dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3338 os_log("preapprovedJoin vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3339 os_log("preapprovedJoin voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3341 os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3344 os_log("preapprovedJoin peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3346 os_log("preapprovedJoin unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
3349 let changeToken = self.containerMO.changeToken ?? ""
3350 let request = JoinWithVoucherRequest.with {
3351 $0.changeToken = changeToken
3354 $0.tlkShares = allTLKShares
3355 $0.viewKeys = viewKeys
3357 self.cuttlefish.joinWithVoucher(request) { response, error in
3358 os_log("preapprovedJoin(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3359 guard let response = response, error == nil else {
3360 os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3361 reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
3365 self.moc.performAndWait {
3367 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3368 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3370 let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
3371 stableInfo: peerStableInfo)
3373 try self.onQueuePersist(changes: response.changes)
3374 os_log("preapprovedJoin succeeded", log: tplogDebug)
3376 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3377 reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
3379 os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, String(describing: error))
3380 reply(nil, [], nil, nil, error)
3390 func update(deviceName: String?,
3391 serialNumber: String?,
3393 policyVersion: UInt64?,
3394 policySecrets: [String: Data]?,
3395 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3396 self.semaphore.wait()
3397 let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
3398 os_log("update complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3399 self.semaphore.signal()
3403 // Get (and save) the latest from cuttlefish
3404 let stableChanges = StableChanges(deviceName: deviceName,
3405 serialNumber: serialNumber,
3406 osVersion: osVersion,
3407 policyVersion: policyVersion,
3408 policySecrets: policySecrets)
3409 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
3412 func set(preapprovedKeys: [Data],
3413 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3414 self.semaphore.wait()
3415 let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
3416 os_log("setPreapprovedKeys complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3417 self.semaphore.signal()
3421 self.moc.performAndWait {
3422 os_log("setPreapprovedKeys: %@", log: tplogDebug, type: .default, preapprovedKeys)
3424 guard let egoPeerID = self.containerMO.egoPeerID else {
3425 // No identity, nothing to do
3426 os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
3427 reply(nil, ContainerError.noPreparedIdentity)
3430 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3431 guard let signingKeyPair = signingKeyPair else {
3432 os_log("setPreapprovedKeys: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3433 reply(nil, error ?? ContainerError.unableToCreateKeyPair)
3437 self.moc.performAndWait {
3438 let dynamicInfo: TPPeerDynamicInfo
3440 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3442 removingPeerIDs: nil,
3443 preapprovedKeys: preapprovedKeys,
3444 signing: signingKeyPair,
3445 currentMachineIDs: self.onqueueCurrentMIDList())
3447 os_log("setPreapprovedKeys: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3452 os_log("setPreapprovedKeys: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
3454 if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
3455 os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
3457 // Calling this will fill in the peer status
3458 self.updateTrustIfNeeded(reply: reply)
3462 os_log("setPreapprovedKeys: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3463 let request = UpdateTrustRequest.with {
3464 $0.changeToken = self.containerMO.changeToken ?? ""
3465 $0.peerID = egoPeerID
3466 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
3469 self.perform(updateTrust: request) { state, error in
3470 guard error == nil else {
3471 os_log("setPreapprovedKeys: failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
3476 os_log("setPreapprovedKeys: updateTrust succeeded", log: tplogDebug, type: .default)
3484 func updateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3485 tlkShares: [CKKSTLKShare],
3486 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3487 self.semaphore.wait()
3488 let reply: ([CKRecord]?, Error?) -> Void = {
3489 os_log("updateTLKs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
3490 self.semaphore.signal()
3494 os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
3496 self.moc.performAndWait {
3497 self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
3501 func onqueueUpdateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3502 tlkShares: [CKKSTLKShare],
3503 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3504 guard let egoPeerID = self.containerMO.egoPeerID,
3505 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3506 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3508 os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
3509 reply(nil, ContainerError.noPreparedIdentity)
3513 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
3514 os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
3515 reply(nil, ContainerError.invalidPermanentInfoOrSig)
3519 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3520 guard let egoPeerKeys = egoPeerKeys else {
3521 os_log("Don't have my own peer keys; can't upload new TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
3525 self.moc.performAndWait {
3526 guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
3527 os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
3528 reply(nil, ContainerError.missingDynamicInfo)
3532 let allTLKShares: [TLKShare]
3533 let viewKeys: [ViewKeys]
3535 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3536 tlkShares: tlkShares,
3537 egoPeerKeys: egoPeerKeys,
3538 egoPeerDynamicInfo: egoPeerDynamicInfo,
3539 epoch: Int(selfPermanentInfo.epoch))
3541 os_log("Unable to process keys before uploading: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3546 let request = UpdateTrustRequest.with {
3547 $0.changeToken = self.containerMO.changeToken ?? ""
3548 $0.peerID = egoPeerID
3549 $0.tlkShares = allTLKShares
3550 $0.viewKeys = viewKeys
3553 self.cuttlefish.updateTrust(request) { response, error in
3554 os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3556 guard error == nil else {
3561 let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
3562 os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
3563 reply(keyHierarchyRecords, nil)
3569 func getState(reply: @escaping (ContainerState) -> Void) {
3570 self.semaphore.wait()
3571 let reply: (ContainerState) -> Void = {
3572 os_log("getState complete: %{public}@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
3573 self.semaphore.signal()
3577 self.moc.performAndWait {
3578 var state = ContainerState()
3579 state.egoPeerID = self.containerMO.egoPeerID
3581 if self.containerMO.bottles != nil {
3582 self.containerMO.bottles!.forEach { bottle in
3583 state.bottles.insert(bottle as! BottleMO)
3587 self.model.allPeers().forEach { peer in
3588 state.peers[peer.peerID] = peer
3590 state.vouchers = Array(self.model.allVouchers())
3595 // This will only fetch changes if no changes have ever been fetched before
3596 func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
3597 self.moc.performAndWait {
3598 if self.containerMO.changeToken == nil {
3599 self.onqueueFetchAndPersistChanges(reply: reply)
3606 private func fetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3607 self.moc.performAndWait {
3608 self.onqueueFetchAndPersistChanges(reply: reply)
3612 private func onqueueFetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3613 let request = FetchChangesRequest.with {
3614 $0.changeToken = self.containerMO.changeToken ?? ""
3616 os_log("Fetching with change token: %{public}@", log: tplogDebug, type: .default, !request.changeToken.isEmpty ? request.changeToken : "empty")
3618 self.cuttlefish.fetchChanges(request) { response, error in
3619 os_log("FetchChanges(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3620 guard let response = response, error == nil else {
3622 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
3623 os_log("change token is expired; resetting local CK storage", log: tplogDebug, type: .default)
3625 self.moc.performAndWait {
3627 try self.deleteLocalCloudKitData()
3629 os_log("Failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3634 self.fetchAndPersistChanges(reply: reply)
3639 os_log("Fetch error is an unknown error: %{public}@", log: tplogDebug, type: .default, String(describing: error))
3642 os_log("Could not fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3648 try self.persist(changes: response.changes)
3650 os_log("Could not persist changes: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3655 if response.changes.more {
3656 os_log("persist: More changes indicated. Fetching...", log: tplogDebug, type: .default)
3657 self.fetchAndPersistChanges(reply: reply)
3660 os_log("persist: no more changes!", log: tplogDebug, type: .default)
3667 // Check for delta update in trust lists, that should lead to update of TLKShares
3669 private func haveTrustMemberChanges(newDynamicInfo: TPPeerDynamicInfo, oldDynamicInfo: TPPeerDynamicInfo?) -> Bool {
3670 guard let oldDynamicInfo = oldDynamicInfo else {
3673 if newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs {
3676 if newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs {
3679 if newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals {
3685 // Fetch and persist changes from Cuttlefish. If this results
3686 // in us calculating a new dynamicInfo then give the new dynamicInfo
3687 // to Cuttlefish. If Cuttlefish returns more changes then persist
3688 // them locally, update dynamicInfo if needed, and keep doing that
3689 // until dynamicInfo is unchanged and there are no more changes to fetch.
3691 // Must be holding the semaphore to call this, and it remains
3692 // the caller's responsibility to release it after it completes
3693 // (i.e. after reply is invoked).
3694 internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
3695 peerChanges: Bool = false,
3696 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3697 self.fetchAndPersistChanges { error in
3698 if let error = error {
3699 os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3704 self.updateTrustIfNeeded(stableChanges: stableChanges, peerChanges: peerChanges, reply: reply)
3708 // If this results in us calculating a new dynamicInfo then,
3709 // upload the new dynamicInfo
3710 // to Cuttlefish. If Cuttlefish returns more changes then persist
3711 // them locally, update dynamicInfo if needed, and keep doing that
3712 // until dynamicInfo is unchanged and there are no more changes to fetch.
3714 // Must be holding the semaphore to call this, and it remains
3715 // the caller's responsibility to release it after it completes
3716 // (i.e. after reply is invoked).
3717 private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
3718 peerChanges: Bool = false,
3719 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3720 self.moc.performAndWait {
3721 guard let egoPeerID = self.containerMO.egoPeerID else {
3722 // No identity, nothing to do
3723 os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
3724 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), nil)
3727 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3728 guard let signingKeyPair = signingKeyPair else {
3729 os_log("updateTrustIfNeeded: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3730 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), error)
3733 guard let currentSelfInModel = self.model.peer(withID: egoPeerID) else {
3734 // Not in circle, nothing to do
3735 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
3736 os_log("updateTrustIfNeeded: ego peer is not in model, is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
3737 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3738 isPreapproved: isPreapproved,
3740 memberChanges: peerChanges,
3741 unknownMachineIDs: false,
3747 // We need to try to have all policy versions that our peers claim to behave
3748 let allPolicyVersions = self.model.allPolicyVersions()
3749 self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
3750 if let error = policyFetchError {
3751 os_log("updateTrustIfNeeded: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3754 self.moc.performAndWait {
3755 let dynamicInfo: TPPeerDynamicInfo
3756 var stableInfo: TPPeerStableInfo?
3759 // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
3760 // and then only load the key if it has changed and we need to sign a new one. This would also
3761 // help make our detection of change immune from non-canonical serialization of dynamicInfo.
3762 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3764 removingPeerIDs: nil,
3765 preapprovedKeys: nil,
3766 signing: signingKeyPair,
3767 currentMachineIDs: self.onqueueCurrentMIDList())
3769 stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
3770 egoPeerID: egoPeerID,
3771 existingStableInfo: currentSelfInModel.stableInfo,
3772 dynamicInfo: dynamicInfo,
3773 signingKeyPair: signingKeyPair)
3775 os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3776 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3777 isPreapproved: false,
3778 status: self.model.statusOfPeer(withID: egoPeerID),
3779 memberChanges: peerChanges,
3780 unknownMachineIDs: false,
3786 os_log("updateTrustIfNeeded: produced a stableInfo: %{public}@", log: tplogDebug, type: .default, String(describing: stableInfo))
3787 os_log("updateTrustIfNeeded: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
3789 let peer = self.model.peer(withID: egoPeerID)
3790 if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
3791 dynamicInfo == peer?.dynamicInfo {
3792 os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
3793 // No change to the dynamicInfo: update the MID list now that we've reached a steady state
3795 self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
3798 os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3801 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3802 isPreapproved: false,
3803 status: self.model.statusOfPeer(withID: egoPeerID),
3804 memberChanges: peerChanges,
3805 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
3806 osVersion: peer?.stableInfo?.osVersion),
3810 // Check if we change that should trigger a notification that should trigger TLKShare updates
3811 let havePeerChanges = peerChanges || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
3813 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
3814 os_log("updateTrustIfNeeded: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3815 var request = UpdateTrustRequest.with {
3816 $0.changeToken = self.containerMO.changeToken ?? ""
3817 $0.peerID = egoPeerID
3818 $0.dynamicInfoAndSig = signedDynamicInfo
3820 if let stableInfo = stableInfo {
3821 request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
3824 self.perform(updateTrust: request, stableChanges: stableChanges, peerChanges: havePeerChanges, reply: reply)
3831 private func perform(updateTrust request: UpdateTrustRequest,
3832 stableChanges: StableChanges? = nil,
3833 peerChanges: Bool = false,
3834 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3836 self.cuttlefish.updateTrust(request) { response, error in
3837 os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3838 guard let response = response, error == nil else {
3839 os_log("UpdateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3840 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3845 try self.persist(changes: response.changes)
3847 os_log("UpdateTrust failed: %{public}@", log: tplogDebug, String(describing: error))
3852 if response.changes.more {
3853 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
3854 peerChanges: peerChanges,
3857 self.updateTrustIfNeeded(stableChanges: stableChanges,
3858 peerChanges: peerChanges,
3864 private func persist(changes: Changes) throws {
3865 // This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
3866 // So, do it ourself
3867 var outsideBlockError: Error?
3869 self.moc.performAndWait {
3870 os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
3871 changes.differences.count,
3873 os_log("persist: New change token: %{public}@", log: tplogDebug, type: .default, changes.changeToken)
3876 try self.onQueuePersist(changes: changes)
3878 outsideBlockError = error
3882 if let outsideBlockError = outsideBlockError {
3883 throw outsideBlockError
3887 // Must be on moc queue to call this.
3888 // Changes are registered in the model and stored in moc.
3889 private func onQueuePersist(changes: Changes) throws {
3890 self.containerMO.changeToken = changes.changeToken
3891 self.containerMO.moreChanges = changes.more
3893 if !changes.differences.isEmpty {
3894 self.model.clearViableBottles()
3897 try changes.differences.forEach { peerDifference in
3898 if let operation = peerDifference.operation {
3900 case .add(let peer):
3901 try self.addOrUpdate(peer: peer)
3903 case .update(let peer):
3904 try self.addOrUpdate(peer: peer)
3905 // Update containerMO ego data if it has changed.
3906 if peer.peerID == self.containerMO.egoPeerID {
3907 guard let stableInfoAndSig: TPPeerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3910 self.containerMO.egoPeerStableInfo = stableInfoAndSig.data
3911 self.containerMO.egoPeerStableInfoSig = stableInfoAndSig.sig
3914 case .remove(let peer):
3915 self.model.deletePeer(withID: peer.peerID)
3916 if let peerMO = try self.fetchPeerMO(peerID: peer.peerID) {
3917 self.moc.delete(peerMO)
3923 let signingKey = changes.recoverySigningPubKey
3924 let encryptionKey = changes.recoveryEncryptionPubKey
3926 if !signingKey.isEmpty && !encryptionKey.isEmpty {
3927 self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
3932 // Must be on moc queue to call this
3933 // Deletes all things that should come back from CloudKit. Keeps the egoPeer data, though.
3934 private func deleteLocalCloudKitData() throws {
3935 os_log("Deleting all CloudKit data", log: tplogDebug, type: .default)
3938 let peerRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
3939 peerRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
3940 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: peerRequest))
3942 let bottleRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Bottle")
3943 bottleRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
3944 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: bottleRequest))
3946 self.containerMO.peers = nil
3947 self.containerMO.bottles = nil
3948 self.containerMO.changeToken = nil
3949 self.containerMO.moreChanges = false
3951 self.model = Container.loadModel(from: self.containerMO)
3954 os_log("Local delete failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
3958 os_log("Saved model with %d peers", log: tplogDebug, type: .default, self.model.allPeerIDs().count)
3961 // Must be on moc queue to call this.
3962 private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
3963 self.model.setRecoveryKeys(
3964 TPRecoveryKeyPair(signingSPKI: signingKey, encryptionSPKI: encryptionKey))
3966 self.containerMO.recoveryKeySigningSPKI = signingKey
3967 self.containerMO.recoveryKeyEncryptionSPKI = encryptionKey
3970 // Must be on moc queue to call this.
3971 private func addOrUpdate(peer: Peer) throws {
3972 if !self.model.hasPeer(withID: peer.peerID) {
3974 guard let permanentInfo = peer.permanentInfoAndSig.toPermanentInfo(peerID: peer.peerID) else {
3975 // Ignoring bad peer
3978 let stableInfo = peer.stableInfoAndSig.toStableInfo()
3979 let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo()
3980 let vouchers = peer.vouchers.compactMap {
3981 TPVoucher(infoWith: $0.voucher, sig: $0.sig)
3983 let isEgoPeer = peer.peerID == self.containerMO.egoPeerID
3984 try self.registerPeerMO(permanentInfo: permanentInfo,
3985 stableInfo: stableInfo,
3986 dynamicInfo: dynamicInfo,
3988 isEgoPeer: isEgoPeer)
3991 // The assertion here is that every peer registered in model is also present in containerMO
3992 let peerMO = try self.fetchPeerMO(peerID: peer.peerID)!
3994 if let stableInfo = peer.stableInfoAndSig.toStableInfo() {
3995 try self.model.update(stableInfo, forPeerWithID: peer.peerID)
3996 // Pull the stableInfo back out of the model, and persist that.
3997 // The model checks signatures and clocks to prevent replay attacks.
3998 let modelPeer = self.model.peer(withID: peer.peerID)
3999 peerMO.stableInfo = modelPeer?.stableInfo?.data
4000 peerMO.stableInfoSig = modelPeer?.stableInfo?.sig
4002 if let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo() {
4003 try self.model.update(dynamicInfo, forPeerWithID: peer.peerID)
4004 // Pull the dynamicInfo back out of the model, and persist that.
4005 // The model checks signatures and clocks to prevent replay attacks.
4006 let modelPeer = self.model.peer(withID: peer.peerID)
4007 peerMO.dynamicInfo = modelPeer?.dynamicInfo?.data
4008 peerMO.dynamicInfoSig = modelPeer?.dynamicInfo?.sig
4010 peer.vouchers.forEach {
4011 if let voucher = TPVoucher(infoWith: $0.voucher, sig: $0.sig) {
4012 self.model.register(voucher)
4013 let voucherMO = VoucherMO(context: self.moc)
4014 voucherMO.voucherInfo = voucher.data
4015 voucherMO.voucherInfoSig = voucher.sig
4016 peerMO.addToVouchers(voucherMO)
4022 // Must be on moc queue to call this.
4023 private func fetchPeerMO(peerID: String) throws -> PeerMO? {
4024 let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
4025 fetch.predicate = NSPredicate(format: "peerID == %@ && container == %@", peerID, self.containerMO)
4026 let peers = try self.moc.fetch(fetch)
4027 return peers.first as? PeerMO
4030 // Must be on moc queue to call this.
4031 private func getPolicyDoc(_ policyVersion: UInt64) throws -> TPPolicyDocument {
4032 guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
4033 throw ContainerError.unknownPolicyVersion(policyVersion)
4035 assert(policyVersion == policyDoc.version.versionNumber)
4036 if policyVersion == prevailingPolicyVersion.versionNumber {
4037 assert(policyDoc.version.policyHash == prevailingPolicyVersion.policyHash)
4042 // Must be on moc queue to call this.
4043 private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
4045 existingStableInfo: TPPeerStableInfo?,
4046 dynamicInfo: TPPeerDynamicInfo,
4047 signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
4048 func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
4049 return (nil == change) || change == existing
4052 let policyOfPeers = try? self.model.policy(forPeerIDs: dynamicInfo.includedPeerIDs,
4053 candidatePeerID: egoPeerID,
4054 candidateStableInfo: existingStableInfo)
4056 // Pick the best version of:
4057 // 1. The policy version asked for by the client
4058 // 2. The max of our existing policyVersion, the highest policy used by our trusted peers, and the compile-time prevailing policy version
4059 let optimalPolicyVersionNumber = stableChanges?.policyVersion ??
4060 max(existingStableInfo?.bestPolicyVersion().versionNumber ?? prevailingPolicyVersion.versionNumber,
4061 policyOfPeers?.version.versionNumber ?? prevailingPolicyVersion.versionNumber,
4062 prevailingPolicyVersion.versionNumber)
4064 // Determine which recovery key we'd like to be using, given our current idea of who to trust
4065 let optimalRecoveryKey = self.model.bestRecoveryKey(for: existingStableInfo, dynamicInfo: dynamicInfo)
4067 if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
4068 noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
4069 noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
4070 noChange(optimalPolicyVersionNumber, existingStableInfo?.bestPolicyVersion().versionNumber) &&
4071 noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
4072 noChange(optimalRecoveryKey?.signingSPKI, existingStableInfo?.recoverySigningPublicKey) &&
4073 noChange(optimalRecoveryKey?.encryptionSPKI, existingStableInfo?.recoveryEncryptionPublicKey) {
4077 let optimalPolicyVersion = try self.getPolicyDoc(optimalPolicyVersionNumber).version
4079 return try self.model.createStableInfo(withFrozenPolicyVersion: frozenPolicyVersion,
4080 flexiblePolicyVersion: optimalPolicyVersion,
4081 policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
4082 deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
4083 serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
4084 osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
4085 signing: signingKeyPair,
4086 recoverySigningPubKey: optimalRecoveryKey?.signingSPKI,
4087 recoveryEncryptionPubKey: optimalRecoveryKey?.encryptionSPKI)
4090 private func assembleBottle(egoPeerID: String) throws -> Bottle {
4091 let bottleMoSet = containerMO.bottles as? Set<BottleMO>
4093 var bottleMOs = bottleMoSet?.filter {
4094 $0.peerID == egoPeerID
4097 if let count = bottleMOs?.count {
4099 throw ContainerError.tooManyBottlesForPeer
4100 } else if count == 0 {
4101 throw ContainerError.noBottleForPeer
4104 throw ContainerError.failedToAssembleBottle
4107 let BM: BottleMO? = (bottleMOs?.removeFirst())
4109 let bottle = try Bottle.with {
4110 $0.peerID = egoPeerID
4112 if let bottledPeerData = BM?.contents {
4113 $0.contents = bottledPeerData
4115 throw ContainerError.failedToAssembleBottle
4118 if let bottledPeerEscrowSigningSPKI = BM?.escrowedSigningSPKI {
4119 $0.escrowedSigningSpki = bottledPeerEscrowSigningSPKI
4121 throw ContainerError.failedToAssembleBottle
4124 if let bottledPeerSignatureUsingEscrowKey = BM?.signatureUsingEscrowKey {
4125 $0.signatureUsingEscrowKey = bottledPeerSignatureUsingEscrowKey
4127 throw ContainerError.failedToAssembleBottle
4130 if let bottledPeerSignatureUsingPeerKey = BM?.signatureUsingPeerKey {
4131 $0.signatureUsingPeerKey = bottledPeerSignatureUsingPeerKey
4133 throw ContainerError.failedToAssembleBottle
4136 if let bID = BM?.bottleID {
4139 throw ContainerError.failedToAssembleBottle
4146 func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
4147 self.semaphore.wait()
4148 let reply: (Error?) -> Void = {
4149 os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
4150 self.semaphore.signal()
4154 var updatedRequest = request
4156 if let egoPeerID = self.containerMO.egoPeerID {
4157 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, _ in
4158 updatedRequest.octagonEgoIdentity = (egoPeerKeys != nil)
4162 self.moc.performAndWait {
4163 self.cuttlefish.reportHealth(updatedRequest) { response, error in
4164 os_log("reportHealth(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
4165 guard error == nil else {
4174 func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
4175 self.semaphore.wait()
4176 let reply: (Error?) -> Void = {
4177 os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
4178 self.semaphore.signal()
4182 self.moc.performAndWait {
4183 self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { response, error in
4184 os_log("pushHealthInquiry(): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
4185 guard error == nil else {