2 * Copyright (c) 2018 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
104 extension ContainerError: LocalizedError {
105 public var errorDescription: String? {
107 case .unableToCreateKeyPair:
108 return "unable to create key pair"
109 case .noPreparedIdentity:
110 return "no prepared identity"
111 case .failedToStoreIdentity:
112 return "failed to stored identity"
113 case .needsAuthentication:
114 return "needs authentication"
115 case .missingStableInfo:
116 return "missing stable info"
117 case .missingDynamicInfo:
118 return "missing dynamic info"
121 case .invalidPermanentInfoOrSig:
122 return "invalid permanent info or signature"
123 case .invalidStableInfoOrSig:
124 return "invalid stable info or signature"
125 case .invalidVoucherOrSig:
126 return "invalid voucher or signature"
127 case .sponsorNotRegistered(let s):
128 return "sponsor not registered: \(s)"
129 case .unknownPolicyVersion(let v):
130 return "unknown policy version: \(v)"
131 case .preparedIdentityNotOnAllowedList(let id):
132 return "prepared identity (\(id)) not on allowed machineID list"
133 case .couldNotLoadAllowedList:
134 return "could not load allowed machineID list"
135 case .noPeersPreapprovePreparedIdentity:
136 return "no peers preapprove prepared identity"
137 case .policyDocumentDoesNotValidate:
138 return "policy document from server doesn't validate"
139 case .tooManyBottlesForPeer:
140 return "too many bottles exist for peer"
141 case .noBottleForPeer:
142 return "no bottle exists for peer"
143 case .restoreBottleFailed:
144 return "failed to restore bottle"
145 case .noBottlesForEscrowRecordID:
146 return "0 bottles exist for escrow record id"
147 case .bottleDoesNotContainContents:
148 return "bottle does not contain encrypted contents"
149 case .bottleDoesNotContainEscrowKeySignature:
150 return "bottle does not contain escrow signature"
151 case .bottleDoesNotContainerPeerKeySignature:
152 return "bottle does not contain peer signature"
153 case .bottleDoesNotContainPeerID:
154 return "bottle does not contain peer id"
155 case .failedToCreateBottledPeer:
156 return "failed to create a bottled peer"
157 case .signatureVerificationFailed:
158 return "failed to verify signature"
159 case .bottleDoesNotContainerEscrowKeySPKI:
160 return "bottle does not contain escrowed key spki"
161 case .failedToFetchEscrowContents:
162 return "failed to fetch escrow contents"
163 case .failedToCreateRecoveryKey:
164 return "failed to create recovery keys"
165 case .untrustedRecoveryKeys:
166 return "untrusted recovery keys"
167 case .noBottlesPresent:
168 return "no bottle present"
169 case .recoveryKeysNotEnrolled:
170 return "recovery key is not enrolled with octagon"
171 case .bottleCreatingPeerNotFound:
172 return "The peer that created the bottle was not found"
173 case .unknownCloudKitError:
174 return "unknown error from cloudkit"
175 case .cloudkitResponseMissing:
176 return "Response missing from CloudKit"
177 case .failedToLoadSecret(errorCode: let errorCode):
178 return "failed to load secret: \(errorCode)"
179 case .failedToLoadSecretDueToType:
180 return "Failed to load secret due to type mismatch (value was not dictionary)"
181 case .failedToAssembleBottle:
182 return "failed to assemble bottle for peer"
184 return "peerID is invalid"
185 case .failedToStoreSecret(errorCode: let errorCode):
186 return "failed to store the secret in the keychain \(errorCode)"
187 case .unknownSecurityFoundationError:
188 return "SecurityFoundation returned an unknown type"
189 case .failedToSerializeData:
190 return "Failed to encode protobuf data"
195 extension ContainerError: CustomNSError {
197 public static var errorDomain: String {
198 return "com.apple.security.trustedpeers.container"
201 public var errorCode: Int {
203 case .unableToCreateKeyPair:
205 case .noPreparedIdentity:
207 case .failedToStoreIdentity:
209 case .needsAuthentication:
211 case .missingStableInfo:
213 case .missingDynamicInfo:
217 case .invalidPermanentInfoOrSig:
219 case .invalidVoucherOrSig:
221 case .invalidStableInfoOrSig:
223 case .sponsorNotRegistered:
225 case .unknownPolicyVersion:
227 case .preparedIdentityNotOnAllowedList:
229 case .noPeersPreapprovePreparedIdentity:
231 case .policyDocumentDoesNotValidate:
233 // 16 was invalidPeerID and failedToAssembleBottle
234 case .tooManyBottlesForPeer:
236 case .noBottleForPeer:
238 case .restoreBottleFailed:
240 case .noBottlesForEscrowRecordID:
242 case .bottleDoesNotContainContents:
244 case .bottleDoesNotContainEscrowKeySignature:
246 case .bottleDoesNotContainerPeerKeySignature:
248 case .bottleDoesNotContainPeerID:
250 case .failedToCreateBottledPeer:
252 case .signatureVerificationFailed:
254 // 27 was failedToLoadSecret*
255 case .bottleDoesNotContainerEscrowKeySPKI:
257 case .failedToFetchEscrowContents:
259 case .couldNotLoadAllowedList:
261 case .failedToCreateRecoveryKey:
263 case .untrustedRecoveryKeys:
265 case .noBottlesPresent:
267 case .recoveryKeysNotEnrolled:
269 case .bottleCreatingPeerNotFound:
271 case .unknownCloudKitError:
273 case .cloudkitResponseMissing:
275 case .failedToLoadSecret:
277 case .failedToLoadSecretDueToType:
279 case .failedToStoreSecret:
281 case .failedToAssembleBottle:
285 case .unknownSecurityFoundationError:
287 case .failedToSerializeData:
292 public var underlyingError: NSError? {
294 case .failedToLoadSecret(errorCode: let errorCode):
295 return NSError(domain: "securityd", code: errorCode)
296 case .failedToStoreSecret(errorCode: let errorCode):
297 return NSError(domain: "securityd", code: errorCode)
303 public var errorUserInfo: [String: Any] {
304 var ret = [String: Any]()
305 if let desc = self.errorDescription {
306 ret[NSLocalizedDescriptionKey] = desc
308 if let underlyingError = self.underlyingError {
309 ret[NSUnderlyingErrorKey] = underlyingError
315 internal func traceError(_ error: Error?) -> String {
316 if let error = error {
317 return "error: \(String(describing: error))"
323 func saveSecret(_ secret: Data, label: String) throws {
325 let query: [CFString: Any] = [
326 kSecClass: kSecClassInternetPassword,
327 kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
328 kSecUseDataProtectionKeychain: true,
329 kSecAttrAccessGroup: "com.apple.security.octagon",
330 kSecAttrSynchronizable: false,
331 kSecAttrDescription: label,
333 kSecValueData: secret,
336 var results: CFTypeRef?
337 var status = SecItemAdd(query as CFDictionary, &results)
339 if status == errSecSuccess {
343 if status == errSecDuplicateItem {
344 // Add every primary key attribute to this find dictionary
345 var findQuery: [CFString: Any] = [:]
346 findQuery[kSecClass] = query[kSecClass]
347 findQuery[kSecAttrSynchronizable] = query[kSecAttrSynchronizable]
348 findQuery[kSecAttrAccessGroup] = query[kSecAttrAccessGroup]
349 findQuery[kSecAttrServer] = query[kSecAttrDescription]
350 findQuery[kSecAttrPath] = query[kSecAttrPath]
351 findQuery[kSecUseDataProtectionKeychain] = query[kSecUseDataProtectionKeychain]
353 var updateQuery: [CFString: Any] = query
354 updateQuery[kSecClass] = nil
356 status = SecItemUpdate(findQuery as CFDictionary, updateQuery as CFDictionary)
358 if status != errSecSuccess {
359 throw ContainerError.failedToStoreSecret(errorCode: Int(status))
362 throw ContainerError.failedToStoreSecret(errorCode: Int(status))
366 func loadSecret(label: String) throws -> (Data?) {
369 let query: [CFString: Any] = [
370 kSecClass: kSecClassInternetPassword,
371 kSecAttrAccessGroup: "com.apple.security.octagon",
372 kSecAttrDescription: label,
373 kSecReturnAttributes: true,
374 kSecReturnData: true,
375 kSecAttrSynchronizable: false,
376 kSecMatchLimit: kSecMatchLimitOne,
379 var result: CFTypeRef?
380 let status = SecItemCopyMatching(query as CFDictionary, &result)
382 if status != errSecSuccess || result == nil {
383 throw ContainerError.failedToLoadSecret(errorCode: Int(status))
387 if let dictionary = result as? [CFString: Any] {
388 secret = dictionary[kSecValueData] as? Data
390 throw ContainerError.failedToLoadSecretDueToType
396 func saveEgoKeyPair(_ keyPair: _SFECKeyPair, identifier: String, resultHandler: @escaping (Bool, Error?) -> Void) {
397 let keychainManager = _SFKeychainManager.default()
398 let signingIdentity = _SFIdentity(keyPair: keyPair)
399 let accessibility = SFAccessibilityMakeWithMode(SFAccessibilityMode.accessibleWhenUnlocked) // class A
400 let accessPolicy = _SFAccessPolicy(accessibility: accessibility,
401 sharingPolicy: SFSharingPolicy.thisDeviceOnly)
402 accessPolicy.accessGroup = egoIdentitiesAccessGroup
403 keychainManager.setIdentity(signingIdentity,
404 forIdentifier: identifier,
405 accessPolicy: accessPolicy,
406 resultHandler: resultHandler)
409 func loadEgoKeyPair(identifier: String, resultHandler: @escaping (_SFECKeyPair?, Error?) -> Void) {
410 let keychainManager = _SFKeychainManager.default()
412 // FIXME constrain to egoIdentitiesAccessGroup, <rdar://problem/39597940>
413 keychainManager.identity(forIdentifier: identifier) { result in
414 switch result.resultType {
415 case .valueAvailable:
416 resultHandler(result.value?.keyPair as? _SFECKeyPair, nil)
417 case .needsAuthentication:
418 resultHandler(nil, ContainerError.needsAuthentication)
420 resultHandler(nil, result.error)
422 resultHandler(nil, ContainerError.unknownSecurityFoundationError)
427 func loadEgoKeys(peerID: String, resultHandler: @escaping (OctagonSelfPeerKeys?, Error?) -> Void) {
428 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: peerID)) { signingKey, error in
429 guard let signingKey = signingKey else {
430 os_log("Unable to load signing key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
431 resultHandler(nil, error)
435 loadEgoKeyPair(identifier: encryptionKeyIdentifier(peerID: peerID)) { encryptionKey, error in
436 guard let encryptionKey = encryptionKey else {
437 os_log("Unable to load encryption key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
438 resultHandler(nil, error)
443 resultHandler(try OctagonSelfPeerKeys(peerID: peerID, signingKey: signingKey, encryptionKey: encryptionKey), nil)
445 resultHandler(nil, error)
451 func removeEgoKeysSync(peerID: String) throws -> Bool {
452 var resultSema = DispatchSemaphore(value: 0)
454 let keychainManager = _SFKeychainManager.default()
456 var retresultForSigningDeletion: Bool = false
457 var reterrorForSigningDeletion: Error?
459 //remove signing keys
460 keychainManager.removeItem(withIdentifier: signingKeyIdentifier(peerID: peerID)) { result, error in
461 retresultForSigningDeletion = result
462 reterrorForSigningDeletion = error
468 if let error = reterrorForSigningDeletion {
472 if retresultForSigningDeletion == false {
473 return retresultForSigningDeletion
476 // now let's do the same thing with the encryption keys
477 resultSema = DispatchSemaphore(value: 0)
478 var retresultForEncryptionDeletion: Bool = false
479 var reterrorForEncryptionDeletion: Error?
481 keychainManager.removeItem(withIdentifier: encryptionKeyIdentifier(peerID: peerID)) { result, error in
482 retresultForEncryptionDeletion = result
483 reterrorForEncryptionDeletion = error
488 if let error = reterrorForEncryptionDeletion {
492 return retresultForEncryptionDeletion && retresultForSigningDeletion
495 func loadEgoKeysSync(peerID: String) throws -> OctagonSelfPeerKeys {
496 // Gotta promote to synchronous; 'antipattern' ahoy
497 let resultSema = DispatchSemaphore(value: 0)
499 var result: OctagonSelfPeerKeys?
502 loadEgoKeys(peerID: peerID) { keys, error in
509 if let error = reserror {
513 if let result = result {
520 func signingKeyIdentifier(peerID: String) -> String {
521 return "signing-key " + peerID
524 func encryptionKeyIdentifier(peerID: String) -> String {
525 return "encryption-key " + peerID
528 func makeTLKShares(ckksTLKs: [CKKSKeychainBackedKey]?, asPeer: CKKSSelfPeer, toPeer: CKKSPeer, epoch: Int) throws -> [TLKShare] {
529 return try (ckksTLKs ?? []).map { tlk in
530 os_log("Making TLKShare for %@ for key %@", log: tplogDebug, type: .default, toPeer.description, tlk)
531 // Not being able to convert a TLK to a TLKShare is a failure, but not having a TLK is only half bad
533 return TLKShare.convert(ckksTLKShare: try CKKSTLKShare(tlk, as: asPeer, to: toPeer, epoch: epoch, poisoned: 0))
535 let nserror = error as NSError
536 if nserror.domain == "securityd" && nserror.code == errSecItemNotFound {
537 os_log("No TLK contents for %@, no TLK share possible", log: tplogDebug, type: .default, tlk)
546 func extract(tlkShares: [CKKSTLKShare], peer: CKKSSelfPeer) {
547 os_log("Attempting to recover %d TLK shares for peer %@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
548 for share in tlkShares {
549 guard share.receiverPeerID == peer.peerID else {
550 os_log("Skipping %@ (wrong peerID)", log: tplogDebug, type: .default, share)
555 // TODO: how should we handle peer sets here?
556 let key = try share.recoverTLK(peer,
557 trustedPeers: [peer as! AnyHashable],
560 try key.saveMaterialToKeychain()
561 os_log("Recovered %@ (from %@)", log: tplogDebug, type: .default, key, share)
563 os_log("Failed to recover share %@: %@", log: tplogDebug, type: .default, share, error as CVarArg)
569 struct ContainerState {
570 var egoPeerID: String?
571 var peers: [String: TPPeer] = [:]
572 var vouchers: [TPVoucher] = []
573 var bottles = Set<BottleMO>()
574 var recoverySigningKey: Data?
575 var recoveryEncryptionKey: Data?
578 internal struct StableChanges {
579 let deviceName: String?
580 let serialNumber: String?
581 let osVersion: String?
582 let policyVersion: UInt64?
583 let policySecrets: [String: Data]?
584 let recoverySigningPubKey: Data?
585 var recoveryEncryptionPubKey: Data?
588 // CoreData doesn't handle creating an identical model from an identical URL. Help it out.
589 private var nsObjectModels: [URL: NSManagedObjectModel] = [:]
590 private let nsObjectModelsQueue = DispatchQueue(label: "com.apple.security.TrustedPeersHelper.nsObjectModels")
591 func getOrMakeModel(url: URL) -> NSManagedObjectModel {
592 return nsObjectModelsQueue.sync {
593 if let model = nsObjectModels[url] {
596 let newModel = NSManagedObjectModel(contentsOf: url)!
597 nsObjectModels[url] = newModel
602 struct ContainerName: Hashable, CustomStringConvertible {
603 let container: String
606 func asSingleString() -> String {
607 // Just to be nice, hide the fact that multiple contexts exist on most machines
608 if self.context == OTDefaultContext {
609 return self.container
611 return self.container + "-" + self.context
615 var description: String {
616 return "Container(\(self.container),\(self.context))"
620 /// This maps to a Cuttlefish service backed by a CloudKit container,
621 /// and a corresponding local Core Data persistent container.
623 /// Methods may be invoked concurrently.
624 class Container: NSObject {
625 let name: ContainerName
627 private let cuttlefish: CuttlefishAPIAsync
629 // Only one request (from Client) is permitted to be in progress at a time.
630 // That includes while waiting for network, i.e. one request must complete
631 // before the next can begin. Otherwise two requests could in parallel be
632 // fetching updates from Cuttlefish, with ensuing changeToken overwrites etc.
633 // This applies for mutating requests -- requests that can only read the current
634 // state (on the moc queue) do not need to.
635 internal let semaphore = DispatchSemaphore(value: 1)
637 // All Core Data access happens through moc: NSManagedObjectContext. The
638 // moc insists on having its own queue, and all operations must happen on
640 internal let moc: NSManagedObjectContext
642 // Rather than Container having its own dispatch queue, we use moc's queue
643 // to synchronise access to our own state as well. So the following instance
644 // variables must only be accessed within blocks executed by calling
645 // moc.perform() or moc.performAndWait().
646 internal var containerMO: ContainerMO
647 internal var model: TPModel
650 Construct a Container.
652 - Parameter name: The name the CloudKit container to which requests will be routed.
654 The "real" container that drives CKKS etc should be named `"com.apple.security.keychain"`.
655 Use other names for test containers such as for rawfish (rawhide-style testing for cuttlefish)
657 - Parameter persistentStoreURL: The location the local Core Data database for this container will be stored.
659 - Parameter cuttlefish: Interface to cuttlefish.
661 init(name: ContainerName, persistentStoreDescription: NSPersistentStoreDescription, cuttlefish: CuttlefishAPIAsync) throws {
662 var initError: Error?
663 var containerMO: ContainerMO?
666 // Set up Core Data stack
667 let url = Bundle(for: type(of: self)).url(forResource: "TrustedPeersHelper", withExtension: "momd")!
668 let mom = getOrMakeModel(url: url)
669 let persistentContainer = NSPersistentContainer(name: "TrustedPeersHelper", managedObjectModel: mom)
670 persistentContainer.persistentStoreDescriptions = [persistentStoreDescription]
672 persistentContainer.loadPersistentStores { _, error in
675 if let initError = initError {
679 let moc = persistentContainer.newBackgroundContext()
680 moc.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
683 // Fetch an existing ContainerMO record if it exists, or create and save one
685 let containerFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Container")
686 containerFetch.predicate = NSPredicate(format: "name == %@", name.asSingleString())
687 let fetchedContainers = try moc.fetch(containerFetch)
688 if let container = fetchedContainers.first as? ContainerMO {
689 containerMO = container
691 containerMO = ContainerMO(context: moc)
692 containerMO!.name = name.asSingleString()
695 // Perform upgrades as needed
696 Container.onqueueUpgradeMachineIDSetToModel(container: containerMO!, moc: moc)
697 Container.onqueueUpgradeMachineIDSetToUseStatus(container: containerMO!, moc: moc)
699 model = Container.loadModel(from: containerMO!)
700 Container.ensureEgoConsistency(from: containerMO!, model: model!)
707 if let initError = initError {
713 self.containerMO = containerMO!
714 self.cuttlefish = cuttlefish
720 // Must be on containerMO's moc queue to call this
721 internal static func loadModel(from containerMO: ContainerMO) -> TPModel {
722 // Populate model from persistent store
723 let model = TPModel(decrypter: Decrypter())
724 let keyFactory = TPECPublicKeyFactory()
725 let peers = containerMO.peers as? Set<PeerMO>
726 peers?.forEach { peer in
727 guard let permanentInfo = TPPeerPermanentInfo(peerID: peer.peerID!,
728 data: peer.permanentInfo! as Data,
729 sig: peer.permanentInfoSig! as Data,
730 keyFactory: keyFactory) else {
733 model.registerPeer(with: permanentInfo)
734 if let data = peer.stableInfo, let sig = peer.stableInfoSig {
735 if let stableInfo = TPPeerStableInfo(data: data as Data, sig: sig as Data) {
737 try model.update(stableInfo, forPeerWithID: permanentInfo.peerID)
739 os_log("loadModel unable to update stable info for peer(%@): %@", log: tplogDebug, type: .default, peer, error as CVarArg)
742 os_log("loadModel: peer %@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
745 os_log("loadModel: peer %@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
747 if let data = peer.dynamicInfo, let sig = peer.dynamicInfoSig {
748 if let dynamicInfo = TPPeerDynamicInfo(data: data as Data, sig: sig as Data) {
750 try model.update(dynamicInfo, forPeerWithID: permanentInfo.peerID)
752 os_log("loadModel unable to update dynamic info for peer(%@): %@", log: tplogDebug, type: .default, peer, error as CVarArg)
755 os_log("loadModel: peer %@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
758 os_log("loadModel: peer %@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
760 peer.vouchers?.forEach {
761 let v = $0 as! VoucherMO
762 if let data = v.voucherInfo, let sig = v.voucherInfoSig {
763 if let voucher = TPVoucher(infoWith: data, sig: sig) {
764 model.register(voucher)
770 // Register persisted policies (cached from cuttlefish)
771 let policies = containerMO.policies as? Set<PolicyMO>
772 policies?.forEach { policyMO in
773 if let policyHash = policyMO.policyHash,
774 let policyData = policyMO.policyData {
775 if let policyDoc = TPPolicyDocument.policyDoc(withHash: policyHash, data: policyData) {
776 model.register(policyDoc)
781 // Register built-in policies
782 builtInPolicyDocuments().forEach { policyDoc in
783 model.register(policyDoc)
786 let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
787 let allowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.allowed.rawValue }.compactMap { $0.machineID })
788 let disallowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.disallowed.rawValue }.compactMap { $0.machineID })
790 os_log("loadModel: allowedMachineIDs: %@", log: tplogDebug, type: .default, allowedMachineIDs)
791 os_log("loadModel: disallowedMachineIDs: %@", log: tplogDebug, type: .default, disallowedMachineIDs)
793 if allowedMachineIDs.count == 0 {
794 os_log("loadModel: no allowedMachineIDs?", log: tplogDebug, type: .default)
800 // Must be on containerMO's moc queue to call this
801 internal static func ensureEgoConsistency(from containerMO: ContainerMO, model: TPModel) {
802 guard let egoPeerID = containerMO.egoPeerID,
803 let egoStableData = containerMO.egoPeerStableInfo,
804 let egoStableSig = containerMO.egoPeerStableInfoSig
806 os_log("ensureEgoConsistency failed to find ego peer information", log: tplogDebug, type: .error)
810 guard let containerEgoStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
811 os_log("ensureEgoConsistency failed to create TPPeerStableInfo from container", log: tplogDebug, type: .error)
815 guard let modelStableInfo = model.getStableInfoForPeer(withID: egoPeerID) else {
816 os_log("ensureEgoConsistency failed to create TPPeerStableInfo from model", log: tplogDebug, type: .error)
820 if modelStableInfo.clock > containerEgoStableInfo.clock {
821 containerMO.egoPeerStableInfo = modelStableInfo.data
822 containerMO.egoPeerStableInfoSig = modelStableInfo.sig
827 static func dictionaryRepresentation(bottle: BottleMO) -> [String: Any] {
828 var dict: [String: String] = [:]
830 dict["bottleID"] = bottle.bottleID
831 dict["peerID"] = bottle.peerID
832 dict["signingSPKI"] = bottle.escrowedSigningSPKI?.base64EncodedString()
833 dict["signatureUsingPeerKey"] = bottle.signatureUsingPeerKey?.base64EncodedString()
834 dict["signatureUsingSPKI"] = bottle.signatureUsingEscrowKey?.base64EncodedString()
835 // ignore the bottle contents; they're mostly unreadable
840 static func peerdictionaryRepresentation(peer: TPPeer) -> [String: Any] {
841 var peerDict: [String: Any] = [
842 "permanentInfo": peer.permanentInfo.dictionaryRepresentation(),
843 "peerID": peer.peerID,
845 if let stableInfo = peer.stableInfo {
846 peerDict["stableInfo"] = stableInfo.dictionaryRepresentation()
848 if let dynamicInfo = peer.dynamicInfo {
849 peerDict["dynamicInfo"] = dynamicInfo.dictionaryRepresentation()
855 func onQueueDetermineLocalTrustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
856 let viablePeerCountsByModelID = self.model.viablePeerCountsByModelID()
857 let peerCountsByMachineID = self.model.peerCountsByMachineID()
859 if let egoPeerID = self.containerMO.egoPeerID {
860 var status = self.model.statusOfPeer(withID: egoPeerID)
861 var isExcluded: Bool = (status == .excluded)
863 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, loadError in
864 var returnError = loadError
866 guard returnError == nil else {
868 if let error = (loadError as NSError?) {
869 os_log("trust status: Unable to load ego keys: %@", log: tplogDebug, type: .default, error as CVarArg)
870 if error.code == errSecItemNotFound && error.domain == NSOSStatusErrorDomain {
871 os_log("trust status: Lost the ego key pair, returning 'excluded' in hopes of fixing up the identity", log: tplogDebug, type: .debug)
874 } else if error.code == errSecInteractionNotAllowed && error.domain == NSOSStatusErrorDomain {
875 // we have an item, but can't read it, suppressing error
881 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
883 viablePeerCountsByModelID: viablePeerCountsByModelID,
884 peerCountsByMachineID: peerCountsByMachineID,
885 isExcluded: isExcluded,
887 reply(egoStatus, returnError)
891 //ensure egoPeerKeys are populated
892 guard egoPeerKeys != nil else {
893 os_log("trust status: No error but Ego Peer Keys are nil", log: tplogDebug, type: .default)
894 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
896 viablePeerCountsByModelID: viablePeerCountsByModelID,
897 peerCountsByMachineID: peerCountsByMachineID,
901 reply(egoStatus, loadError)
905 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: egoPeerID,
907 viablePeerCountsByModelID: viablePeerCountsByModelID,
908 peerCountsByMachineID: peerCountsByMachineID,
909 isExcluded: isExcluded,
911 reply(egoStatus, nil)
916 // With no ego peer ID, either return 'excluded' if there are extant peers, or 'unknown' to signal no peers at all
917 if self.model.allPeerIDs().isEmpty {
918 os_log("No existing peers in account", log: tplogDebug, type: .debug)
919 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
921 viablePeerCountsByModelID: viablePeerCountsByModelID,
922 peerCountsByMachineID: peerCountsByMachineID,
925 reply(egoStatus, nil)
928 os_log("Existing peers in account, but we don't have a peer ID. We are excluded.", log: tplogDebug, type: .debug)
929 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
931 viablePeerCountsByModelID: viablePeerCountsByModelID,
932 peerCountsByMachineID: peerCountsByMachineID,
935 reply(egoStatus, nil)
941 func trustStatus(reply: @escaping (TrustedPeersHelperEgoPeerStatus, Error?) -> Void) {
942 self.semaphore.wait()
943 let reply: (TrustedPeersHelperEgoPeerStatus, Error?) -> Void = {
944 // Suppress logging of successful replies here; it's not that useful
945 let logType: OSLogType = $1 == nil ? .debug : .info
946 os_log("trustStatus complete: %@ %@",
947 log: tplogTrace, type: logType, TPPeerStatusToString($0.egoStatus), traceError($1))
949 self.semaphore.signal()
952 self.moc.performAndWait {
953 // Knowledge of your peer status only exists if you know about other peers. If you haven't fetched, fetch.
954 if self.containerMO.changeToken == nil {
955 self.fetchAndPersistChanges { fetchError in
956 guard fetchError == nil else {
957 if let error = fetchError {
958 os_log("Unable to fetch changes, trust status is unknown: %@", log: tplogDebug, type: .default, error as CVarArg)
961 let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
963 viablePeerCountsByModelID: [:],
964 peerCountsByMachineID: [:],
967 reply(egoStatus, fetchError)
971 self.moc.performAndWait {
972 self.onQueueDetermineLocalTrustStatus(reply: reply)
976 self.onQueueDetermineLocalTrustStatus(reply: reply)
981 func fetchTrustState(reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
982 let reply: (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void = {
983 os_log("fetch trust state complete: %@ %@",
984 log: tplogTrace, type: .info, String(reflecting: $0), traceError($2))
988 self.moc.performAndWait {
989 if let egoPeerID = self.containerMO.egoPeerID,
990 let egoPermData = self.containerMO.egoPeerPermanentInfo,
991 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig {
993 let keyFactory = TPECPublicKeyFactory()
994 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
995 os_log("fetchTrustState failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
996 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
1000 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(permanentInfo.signingPubKey.spki())
1001 os_log("fetchTrustState: ego peer is %@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
1003 let egoStableInfo = self.model.getStableInfoForPeer(withID: egoPeerID)
1005 let egoPeerStatus = TrustedPeersHelperPeerState(peerID: egoPeerID,
1006 isPreapproved: isPreapproved,
1007 status: self.model.statusOfPeer(withID: egoPeerID),
1008 memberChanges: false,
1009 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
1010 osVersion: egoStableInfo?.osVersion)
1012 var tphPeers: [TrustedPeersHelperPeer] = []
1014 if let egoPeer = self.model.peer(withID: egoPeerID) {
1015 egoPeer.trustedPeerIDs.forEach { trustedPeerID in
1016 if let peer = self.model.peer(withID: trustedPeerID) {
1017 let peerViews = try? self.model.getViewsForPeer(peer.permanentInfo,
1018 stableInfo: peer.stableInfo,
1021 tphPeers.append(TrustedPeersHelperPeer(peerID: trustedPeerID,
1022 signingSPKI: peer.permanentInfo.signingPubKey.spki(),
1023 encryptionSPKI: peer.permanentInfo.encryptionPubKey.spki(),
1024 viewList: peerViews ?? Set()))
1026 os_log("No peer for trusted ID %@", log: tplogDebug, type: .default, trustedPeerID)
1030 os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
1033 os_log("Returning trust state: %@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
1034 reply(egoPeerStatus, tphPeers, nil)
1036 // With no ego peer ID, there are no trusted peers
1037 os_log("No peer ID => no trusted peers", log: tplogDebug, type: .debug)
1038 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: false, unknownMachineIDs: false, osVersion: nil), [], nil)
1043 func dump(reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
1044 let reply: ([AnyHashable: Any]?, Error?) -> Void = {
1045 os_log("dump complete: %@",
1046 log: tplogTrace, type: .info, traceError($1))
1049 self.moc.performAndWait {
1050 var d: [AnyHashable: Any] = [:]
1052 if let egoPeerID = self.containerMO.egoPeerID {
1053 if let peer = self.model.peer(withID: egoPeerID) {
1054 d["self"] = Container.peerdictionaryRepresentation(peer: peer)
1056 d["self"] = ["peerID": egoPeerID]
1062 d["peers"] = self.model.allPeers().filter { $0.peerID != self.containerMO.egoPeerID }.map { peer in
1063 Container.peerdictionaryRepresentation(peer: peer)
1066 d["vouchers"] = self.model.allVouchers().map { $0.dictionaryRepresentation() }
1068 if let bottles = self.containerMO.bottles as? Set<BottleMO> {
1069 d["bottles"] = bottles.map { Container.dictionaryRepresentation(bottle: $0) }
1074 let midList = self.onqueueCurrentMIDList()
1075 d["machineIDsAllowed"] = midList.machineIDs(in: .allowed).sorted()
1076 d["machineIDsDisallowed"] = midList.machineIDs(in: .disallowed).sorted()
1077 d["modelRecoverySigningPublicKey"] = self.model.recoverySigningPublicKey()
1078 d["modelRecoveryEncryptionPublicKey"] = self.model.recoveryEncryptionPublicKey()
1084 func dumpEgoPeer(reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
1085 let reply: (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void = {
1086 os_log("dumpEgoPeer complete: %@", log: tplogTrace, type: .info, traceError($4))
1087 reply($0, $1, $2, $3, $4)
1089 self.moc.performAndWait {
1090 guard let egoPeerID = self.containerMO.egoPeerID else {
1091 reply(nil, nil, nil, nil, ContainerError.noPreparedIdentity)
1095 guard let peer = self.model.peer(withID: egoPeerID) else {
1096 reply(egoPeerID, nil, nil, nil, nil)
1100 reply(egoPeerID, peer.permanentInfo, peer.stableInfo, peer.dynamicInfo, nil)
1104 func validatePeers(request: ValidatePeersRequest, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
1105 self.semaphore.wait()
1106 let reply: ([AnyHashable: Any]?, Error?) -> Void = {
1107 os_log("validatePeers complete %@", log: tplogTrace, type: .info, traceError($1))
1108 self.semaphore.signal()
1112 self.cuttlefish.validatePeers(request) { response, error in
1113 os_log("ValidatePeers(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1114 guard let response = response, error == nil else {
1115 os_log("validatePeers failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1116 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
1120 var info: [AnyHashable: Any] = [:]
1121 info["health"] = response.validatorsHealth as AnyObject
1122 info["results"] = try? JSONSerialization.jsonObject(with: response.jsonUTF8Data())
1128 func getViews(inViews: [String], reply: @escaping ([String]?, Error?) -> Void) {
1129 let reply: ([String]?, Error?) -> Void = {
1130 os_log("getViews complete %@", log: tplogTrace, type: .info, traceError($1))
1133 self.moc.performAndWait {
1134 guard let egoPeerID = self.containerMO.egoPeerID,
1135 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1136 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
1137 let egoStableData = self.containerMO.egoPeerStableInfo,
1138 let egoStableSig = self.containerMO.egoPeerStableInfoSig
1140 os_log("getViews failed to find ego peer information", log: tplogDebug, type: .error)
1141 reply(nil, ContainerError.noPreparedIdentity)
1144 guard let stableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
1145 os_log("getViews failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
1146 reply(nil, ContainerError.invalidStableInfoOrSig)
1150 let keyFactory = TPECPublicKeyFactory()
1151 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1152 os_log("getViews failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
1153 reply(nil, ContainerError.invalidPermanentInfoOrSig)
1158 let views = try self.model.getViewsForPeer(permanentInfo, stableInfo: stableInfo, inViews: Set(inViews))
1159 reply(Array(views), nil)
1167 func reset(resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
1168 self.semaphore.wait()
1169 let reply: (Error?) -> Void = {
1170 os_log("reset complete %@", log: tplogTrace, type: .info, traceError($0))
1171 self.semaphore.signal()
1175 self.moc.performAndWait {
1176 let resetReason = ResetReason.from(cuttlefishResetReason: resetReason)
1177 let request = ResetRequest.with {
1178 $0.resetReason = resetReason
1180 self.cuttlefish.reset(request) { response, error in
1181 os_log("Reset(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1182 guard let response = response, error == nil else {
1183 os_log("reset failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1184 reply(error ?? ContainerError.cloudkitResponseMissing)
1188 // Erase container's persisted state
1189 self.moc.performAndWait {
1190 self.moc.delete(self.containerMO)
1191 self.containerMO = ContainerMO(context: self.moc)
1192 self.containerMO.name = self.name.asSingleString()
1193 self.model = Container.loadModel(from: self.containerMO)
1195 try self.onQueuePersist(changes: response.changes)
1196 os_log("reset succeded", log: tplogDebug, type: .default)
1199 os_log("reset persist failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
1207 func localReset(reply: @escaping (Error?) -> Void) {
1208 self.semaphore.wait()
1209 let reply: (Error?) -> Void = {
1210 os_log("localReset complete %@", log: tplogTrace, type: .info, traceError($0))
1211 self.semaphore.signal()
1215 self.moc.performAndWait {
1217 // Erase container's persisted state
1218 self.moc.delete(self.containerMO)
1219 self.containerMO = ContainerMO(context: self.moc)
1220 self.containerMO.name = self.name.asSingleString()
1221 self.model = Container.loadModel(from: self.containerMO)
1231 func loadOrCreateKeyPair(privateKeyPersistentRef: Data?) throws -> _SFECKeyPair {
1232 if let privateKeyPersistentRef = privateKeyPersistentRef {
1233 return try TPHObjectiveC.fetchKeyPair(withPrivateKeyPersistentRef: privateKeyPersistentRef)
1235 let keySpecifier = _SFECKeySpecifier(curve: SFEllipticCurve.nistp384)
1236 guard let keyPair = _SFECKeyPair(randomKeyPairWith: keySpecifier) else {
1237 throw ContainerError.unableToCreateKeyPair
1243 // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion
1244 func prepare(epoch: UInt64,
1249 deviceName: String?,
1250 serialNumber: String,
1252 policyVersion: UInt64?,
1253 policySecrets: [String: Data]?,
1254 signingPrivateKeyPersistentRef: Data?,
1255 encryptionPrivateKeyPersistentRef: Data?,
1256 reply: @escaping (String?, Data?, Data?, Data?, Data?, Error?) -> Void) {
1257 self.semaphore.wait()
1258 let reply: (String?, Data?, Data?, Data?, Data?, Error?) -> Void = {
1259 os_log("prepare complete peerID: %@ %@",
1260 log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($5))
1261 self.semaphore.signal()
1262 reply($0, $1, $2, $3, $4, $5)
1265 // Create a new peer identity with random keys, and store the keys in keychain
1266 let permanentInfo: TPPeerPermanentInfo
1267 let signingKeyPair: _SFECKeyPair
1268 let encryptionKeyPair: _SFECKeyPair
1270 signingKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: signingPrivateKeyPersistentRef)
1271 encryptionKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: encryptionPrivateKeyPersistentRef)
1273 permanentInfo = try TPPeerPermanentInfo(machineID: machineID,
1276 signing: signingKeyPair,
1277 encryptionKeyPair: encryptionKeyPair,
1278 peerIDHashAlgo: TPHashAlgo.SHA256)
1281 reply(nil, nil, nil, nil, nil, error)
1285 let peerID = permanentInfo.peerID
1287 let bottle: BottledPeer
1289 bottle = try BottledPeer(peerID: peerID,
1291 peerSigningKey: signingKeyPair,
1292 peerEncryptionKey: encryptionKeyPair,
1293 bottleSalt: bottleSalt)
1295 _ = try saveSecret(bottle.secret, label: peerID)
1297 os_log("bottle creation failed: %@", log: tplogDebug, type: .default, error as CVarArg)
1298 reply(nil, nil, nil, nil, nil, error)
1302 saveEgoKeyPair(signingKeyPair, identifier: signingKeyIdentifier(peerID: peerID)) { success, error in
1303 guard success else {
1304 os_log("Unable to save signing key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1305 reply(nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
1308 saveEgoKeyPair(encryptionKeyPair, identifier: encryptionKeyIdentifier(peerID: peerID)) { success, error in
1309 guard success else {
1310 os_log("Unable to save encryption key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1311 reply(nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
1315 // Save the prepared identity as containerMO.egoPeer* and its bottle
1316 self.moc.performAndWait {
1318 let policyVersion = policyVersion ?? prevailingPolicyVersion
1319 let policyDoc = try self.getPolicyDoc(policyVersion)
1321 let stableInfo = TPPeerStableInfo(clock: 1,
1322 policyVersion: policyDoc.policyVersion,
1323 policyHash: policyDoc.policyHash,
1324 policySecrets: policySecrets,
1325 deviceName: deviceName,
1326 serialNumber: serialNumber,
1327 osVersion: osVersion,
1328 signing: signingKeyPair,
1329 recoverySigningPubKey: nil,
1330 recoveryEncryptionPubKey: nil,
1333 self.containerMO.egoPeerID = permanentInfo.peerID
1334 self.containerMO.egoPeerPermanentInfo = permanentInfo.data
1335 self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
1336 self.containerMO.egoPeerStableInfo = stableInfo.data
1337 self.containerMO.egoPeerStableInfoSig = stableInfo.sig
1339 let bottleMO = BottleMO(context: self.moc)
1340 bottleMO.peerID = bottle.peerID
1341 bottleMO.bottleID = bottle.bottleID
1342 bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
1343 bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
1344 bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
1345 bottleMO.contents = bottle.contents
1347 self.containerMO.addToBottles(bottleMO)
1351 reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, nil)
1353 reply(nil, nil, nil, nil, nil, error)
1359 func getEgoEpoch(reply: @escaping (UInt64, Error?) -> Void) {
1360 let reply: (UInt64, Error?) -> Void = {
1361 os_log("getEgoEpoch complete: %d %@", log: tplogTrace, type: .info, $0, traceError($1))
1365 self.moc.performAndWait {
1366 guard let egoPeerID = self.containerMO.egoPeerID else {
1367 reply(0, ContainerError.noPreparedIdentity)
1370 guard let egoPeer = self.model.peer(withID: egoPeerID) else {
1371 reply(0, ContainerError.noPreparedIdentity)
1375 reply(egoPeer.permanentInfo.epoch, nil)
1378 func establish(ckksKeys: [CKKSKeychainBackedKeySet],
1379 tlkShares: [CKKSTLKShare],
1380 preapprovedKeys: [Data]?,
1381 reply: @escaping (String?, [CKRecord], Error?) -> Void) {
1382 self.semaphore.wait()
1383 let reply: (String?, [CKRecord], Error?) -> Void = {
1384 os_log("establish complete peer: %@ %@",
1385 log: tplogTrace, type: .default, ($0 ?? "NULL") as CVarArg, traceError($2))
1386 self.semaphore.signal()
1390 self.moc.performAndWait {
1391 self.onqueueEstablish(ckksKeys: ckksKeys,
1392 tlkShares: tlkShares,
1393 preapprovedKeys: preapprovedKeys,
1398 func onqueueTTRUntrusted() {
1399 let ttr = SecTapToRadar(tapToRadar: "Device not IDMS trusted",
1400 description: "Device not IDMS trusted",
1405 func onqueueEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
1406 tlkShares: [CKKSTLKShare],
1407 preapprovedKeys: [Data]?,
1408 reply: @escaping (String?, [CKRecord], Error?) -> Void) {
1409 // Fetch ego peer identity from local storage.
1410 guard let egoPeerID = self.containerMO.egoPeerID,
1411 let egoPermData = self.containerMO.egoPeerPermanentInfo,
1412 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
1413 let egoStableData = self.containerMO.egoPeerStableInfo,
1414 let egoStableSig = self.containerMO.egoPeerStableInfoSig
1416 reply(nil, [], ContainerError.noPreparedIdentity)
1420 let keyFactory = TPECPublicKeyFactory()
1421 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
1422 reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
1425 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
1426 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1427 reply(nil, [], ContainerError.invalidStableInfoOrSig)
1430 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
1431 os_log("establish: self machineID %@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
1432 self.onqueueTTRUntrusted()
1433 reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
1437 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
1438 guard let egoPeerKeys = egoPeerKeys else {
1439 os_log("Don't have my own peer keys; can't establish: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
1440 reply(nil, [], error)
1443 self.moc.performAndWait {
1444 let viewKeys: [ViewKeys] = ckksKeys.map(ViewKeys.convert)
1445 let allTLKShares: [TLKShare]
1447 let octagonShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk }, asPeer: egoPeerKeys, toPeer: egoPeerKeys, epoch: Int(selfPermanentInfo.epoch))
1448 let sosShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
1450 allTLKShares = octagonShares + sosShares
1452 os_log("Unable to make TLKShares for self: %@", log: tplogDebug, type: .default, error as CVarArg)
1453 reply(nil, [], error)
1457 let dynamicInfo: TPPeerDynamicInfo
1459 dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
1460 peerPermanentInfo: selfPermanentInfo,
1461 peerStableInfo: selfStableInfo,
1463 preapprovedKeys: preapprovedKeys,
1464 signing: egoPeerKeys.signingKey,
1465 currentMachineIDs: self.onqueueCurrentMIDList())
1467 os_log("dynamic info: %@", log: tplogDebug, type: .default, dynamicInfo)
1469 reply(nil, [], error)
1473 let peer = Peer.with {
1474 $0.peerID = egoPeerID
1475 $0.permanentInfoAndSig.peerPermanentInfo = egoPermData
1476 $0.permanentInfoAndSig.sig = egoPermSig
1477 $0.stableInfoAndSig.peerStableInfo = egoStableData
1478 $0.stableInfoAndSig.sig = egoStableSig
1479 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
1484 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
1486 reply(nil, [], error)
1489 os_log("Beginning establish for peer %@", log: tplogDebug, type: .default, egoPeerID)
1490 os_log("Establish permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
1491 os_log("Establish permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
1492 os_log("Establish stableInfo: %@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
1493 os_log("Establish stableInfoSig: %@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
1494 os_log("Establish dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
1495 os_log("Establish dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
1497 os_log("Establish introducing %d key sets, %d tlk shares", log: tplogDebug, type: .default, viewKeys.count, allTLKShares.count)
1500 os_log("Establish bottle: %@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
1501 os_log("Establish peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
1503 os_log("Establish unable to encode bottle/peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
1506 let request = EstablishRequest.with {
1509 $0.viewKeys = viewKeys
1510 $0.tlkShares = allTLKShares
1512 self.cuttlefish.establish(request) { response, error in
1513 os_log("Establish(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1514 os_log("Establish: viewKeys: %@", String(describing: viewKeys))
1515 guard let response = response, error == nil else {
1516 os_log("establish failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1517 reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
1522 os_log("Establish returned changes: %@", log: tplogDebug, type: .default, try response.changes.jsonString())
1524 os_log("Establish returned changes, but they can't be serialized", log: tplogDebug, type: .default)
1527 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
1530 try self.persist(changes: response.changes)
1532 guard response.changes.more == false else {
1533 os_log("establish succeeded, but more changes need fetching...", log: tplogDebug, type: .default)
1535 self.fetchAndPersistChanges { fetchError in
1536 guard fetchError == nil else {
1537 // This is an odd error condition: we might be able to fetch again and be in a good state...
1538 os_log("fetch-after-establish failed: %@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
1539 reply(nil, keyHierarchyRecords, fetchError)
1543 os_log("fetch-after-establish succeeded", log: tplogDebug, type: .default)
1544 reply(egoPeerID, keyHierarchyRecords, nil)
1549 os_log("establish succeeded", log: tplogDebug, type: .default)
1550 reply(egoPeerID, keyHierarchyRecords, nil)
1552 os_log("establish handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
1553 reply(nil, keyHierarchyRecords, error)
1560 func setRecoveryKey(recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
1561 self.semaphore.wait()
1562 let reply: (Error?) -> Void = {
1563 os_log("setRecoveryKey complete: %@", log: tplogTrace, type: .info, traceError($0))
1564 self.semaphore.signal()
1568 os_log("beginning a setRecoveryKey", log: tplogDebug, type: .default)
1570 self.moc.performAndWait {
1571 guard let egoPeerID = self.containerMO.egoPeerID else {
1572 os_log("no prepared identity, cannot set recovery key", log: tplogDebug, type: .default)
1573 reply(ContainerError.noPreparedIdentity)
1577 var recoveryKeys: RecoveryKey
1579 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
1581 os_log("failed to create recovery keys: %@", log: tplogDebug, type: .default, error as CVarArg)
1582 reply(ContainerError.failedToCreateRecoveryKey)
1586 let signingPublicKey: Data = recoveryKeys.peerKeys.signingVerificationKey.keyData
1587 let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionVerificationKey.keyData
1589 os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .debug, signingPublicKey.base64EncodedString())
1590 os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .debug, encryptionPublicKey.base64EncodedString())
1592 guard let stableInfoData = self.containerMO.egoPeerStableInfo else {
1593 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
1594 reply(ContainerError.nonMember)
1597 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
1598 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
1599 reply(ContainerError.nonMember)
1602 guard let permInfoData = self.containerMO.egoPeerPermanentInfo else {
1603 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
1604 reply(ContainerError.nonMember)
1607 guard let permInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
1608 os_log("permInfoSig does not exist", log: tplogDebug, type: .default)
1609 reply(ContainerError.nonMember)
1612 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
1613 os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
1614 reply(ContainerError.invalidStableInfoOrSig)
1617 let keyFactory = TPECPublicKeyFactory()
1618 guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permInfoData, sig: permInfoSig, keyFactory: keyFactory) else {
1619 os_log("cannot create TPPeerPermanentInfo", log: tplogDebug, type: .default)
1620 reply(ContainerError.invalidStableInfoOrSig)
1624 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
1625 guard let signingKeyPair = signingKeyPair else {
1626 os_log("handle: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1630 self.moc.performAndWait {
1632 let tlkShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
1633 asPeer: recoveryKeys.peerKeys,
1634 toPeer: recoveryKeys.peerKeys,
1635 epoch: Int(permanentInfo.epoch))
1637 let policyVersion = stableInfo.policyVersion
1638 let policyDoc = try self.getPolicyDoc(policyVersion)
1640 let updatedStableInfo = TPPeerStableInfo(clock: stableInfo.clock + 1,
1641 policyVersion: policyDoc.policyVersion,
1642 policyHash: policyDoc.policyHash,
1643 policySecrets: stableInfo.policySecrets,
1644 deviceName: stableInfo.deviceName,
1645 serialNumber: stableInfo.serialNumber,
1646 osVersion: stableInfo.osVersion,
1647 signing: signingKeyPair,
1648 recoverySigningPubKey: signingPublicKey,
1649 recoveryEncryptionPubKey: encryptionPublicKey,
1651 let signedStableInfo = SignedPeerStableInfo(updatedStableInfo)
1653 let request = SetRecoveryKeyRequest.with {
1654 $0.peerID = egoPeerID
1655 $0.recoverySigningPubKey = signingPublicKey
1656 $0.recoveryEncryptionPubKey = encryptionPublicKey
1657 $0.stableInfoAndSig = signedStableInfo
1658 $0.tlkShares = tlkShares
1659 $0.changeToken = self.containerMO.changeToken ?? ""
1662 self.cuttlefish.setRecoveryKey(request) { response, error in
1663 os_log("SetRecoveryKey(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
1664 guard let response = response, error == nil else {
1665 os_log("setRecoveryKey failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1666 reply(error ?? ContainerError.cloudkitResponseMissing)
1670 self.moc.performAndWait {
1672 self.containerMO.egoPeerStableInfo = updatedStableInfo.data
1673 self.containerMO.egoPeerStableInfoSig = updatedStableInfo.sig
1674 try self.onQueuePersist(changes: response.changes)
1676 os_log("setRecoveryKey succeeded", log: tplogDebug, type: .default)
1679 os_log("setRecoveryKey handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
1692 func currentSetContainerBottleID(bottleMOs: Set<BottleMO>, bottleID: String) -> (Bool) {
1693 let bmos = bottleMOs.filter {
1694 $0.bottleID == bottleID
1696 return !bmos.isEmpty
1699 func onqueueFindBottle(bottleID: String, reply: @escaping (BottleMO?, Error?) -> Void) {
1702 var bottles: Set<BottleMO> = []
1703 var shouldPerformFetch = false
1705 if let containerBottles = self.containerMO.bottles as? Set<BottleMO> {
1706 if self.currentSetContainerBottleID(bottleMOs: containerBottles, bottleID: bottleID) == false {
1707 shouldPerformFetch = true
1709 bottles = containerBottles
1712 shouldPerformFetch = true
1715 if shouldPerformFetch == true {
1716 self.fetchViableBottlesWithSemaphore { _, _, error in
1717 guard error == nil else {
1718 os_log("fetchViableBottles failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1723 guard let newBottles = self.containerMO.bottles as? Set<BottleMO> else {
1724 os_log("no bottles on container: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
1725 reply(nil, ContainerError.noBottlesPresent)
1729 guard self.currentSetContainerBottleID(bottleMOs: newBottles, bottleID: bottleID) == true else {
1730 reply(nil, ContainerError.noBottlesForEscrowRecordID)
1734 os_log("onqueueFindBottle found bottle: %@", log: tplogDebug, type: .default, newBottles)
1736 bottles = newBottles.filter {
1737 $0.bottleID == bottleID
1739 if bottles.count > 1 {
1740 reply(nil, ContainerError.tooManyBottlesForPeer)
1743 bmo = bottles.removeFirst()
1747 var filteredBottles = bottles.filter {
1748 $0.bottleID == bottleID
1750 if filteredBottles.count > 1 {
1751 reply(nil, ContainerError.tooManyBottlesForPeer)
1754 bmo = filteredBottles.removeFirst()
1759 func onqueueRecoverBottle(managedBottle: BottleMO, entropy: Data, bottleSalt: String) throws -> BottledPeer {
1760 guard let bottledContents = managedBottle.contents else {
1761 throw ContainerError.bottleDoesNotContainContents
1763 guard let signatureUsingEscrowKey = managedBottle.signatureUsingEscrowKey else {
1764 throw ContainerError.bottleDoesNotContainEscrowKeySignature
1767 guard let signatureUsingPeerKey = managedBottle.signatureUsingPeerKey else {
1768 throw ContainerError.bottleDoesNotContainerPeerKeySignature
1770 guard let sponsorPeerID = managedBottle.peerID else {
1771 throw ContainerError.bottleDoesNotContainPeerID
1774 //verify bottle signature using peer
1776 guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
1777 os_log("recover bottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
1778 throw ContainerError.bottleCreatingPeerNotFound
1780 guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
1781 os_log("recover bottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
1782 throw ContainerError.signatureVerificationFailed
1785 _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
1787 os_log("Verification of bottled signature failed: %@", log: tplogDebug, type: .default, error as CVarArg)
1788 throw ContainerError.failedToCreateBottledPeer
1792 return try BottledPeer(contents: bottledContents,
1794 bottleSalt: bottleSalt,
1795 signatureUsingEscrow: signatureUsingEscrowKey,
1796 signatureUsingPeerKey: signatureUsingPeerKey)
1798 os_log("Creation of Bottled Peer failed with bottle salt: %@,\nAttempting with empty bottle salt", bottleSalt)
1801 return try BottledPeer(contents: bottledContents,
1804 signatureUsingEscrow: signatureUsingEscrowKey,
1805 signatureUsingPeerKey: signatureUsingPeerKey)
1807 os_log("Creation of Bottled Peer failed: %@", log: tplogDebug, type: .default, error as CVarArg)
1808 throw ContainerError.failedToCreateBottledPeer
1813 func preflightVouchWithBottle(bottleID: String,
1814 reply: @escaping (String?, Error?) -> Void) {
1815 self.semaphore.wait()
1816 let reply: (String?, Error?) -> Void = {
1817 os_log("preflightVouchWithBottle complete: %@",
1818 log: tplogTrace, type: .info, traceError($1))
1819 self.semaphore.signal()
1823 self.moc.performAndWait {
1824 self.onqueueFindBottle(bottleID: bottleID) { bottleMO, error in
1825 guard let bottleMO = bottleMO else {
1826 os_log("preflightVouchWithBottle found no bottle: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1831 reply(bottleMO.peerID, nil)
1836 func vouchWithBottle(bottleID: String,
1839 tlkShares: [CKKSTLKShare],
1840 reply: @escaping (Data?, Data?, Error?) -> Void) {
1841 self.semaphore.wait()
1842 let reply: (Data?, Data?, Error?) -> Void = {
1843 os_log("vouchWithBottle complete: %@",
1844 log: tplogTrace, type: .info, traceError($2))
1845 self.semaphore.signal()
1849 self.fetchAndPersistChanges { error in
1850 guard error == nil else {
1851 os_log("vouchWithBottle unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1852 reply(nil, nil, error)
1856 self.onqueueFindBottle(bottleID: bottleID) { returnedBMO, error in
1857 self.moc.performAndWait {
1858 guard error == nil else {
1859 os_log("vouchWithBottle unable to find bottle for escrow record id: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1860 reply(nil, nil, error)
1864 guard let bmo: BottleMO = returnedBMO else {
1865 os_log("vouchWithBottle bottle is nil: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
1866 reply(nil, nil, error)
1870 guard let bottledContents = bmo.contents else {
1871 reply(nil, nil, ContainerError.bottleDoesNotContainContents)
1874 guard let signatureUsingEscrowKey = bmo.signatureUsingEscrowKey else {
1875 reply(nil, nil, ContainerError.bottleDoesNotContainEscrowKeySignature)
1879 guard let signatureUsingPeerKey = bmo.signatureUsingPeerKey else {
1880 reply(nil, nil, ContainerError.bottleDoesNotContainerPeerKeySignature)
1883 guard let sponsorPeerID = bmo.peerID else {
1884 reply(nil, nil, ContainerError.bottleDoesNotContainPeerID)
1888 //verify bottle signature using peer
1890 guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
1891 os_log("vouchWithBottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
1892 reply(nil, nil, ContainerError.bottleCreatingPeerNotFound)
1895 guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
1896 os_log("vouchWithBottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
1897 reply(nil, nil, ContainerError.signatureVerificationFailed)
1901 _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
1903 os_log("vouchWithBottle: Verification of bottled signature failed: %@", log: tplogDebug, type: .default, error as CVarArg)
1904 reply(nil, nil, ContainerError.failedToCreateBottledPeer)
1908 //create bottled peer
1909 let bottledPeer: BottledPeer
1911 bottledPeer = try BottledPeer(contents: bottledContents,
1913 bottleSalt: bottleSalt,
1914 signatureUsingEscrow: signatureUsingEscrowKey,
1915 signatureUsingPeerKey: signatureUsingPeerKey)
1917 os_log("Creation of Bottled Peer failed with bottle salt: %@,\nAttempting with empty bottle salt", bottleSalt)
1920 bottledPeer = try BottledPeer(contents: bottledContents,
1923 signatureUsingEscrow: signatureUsingEscrowKey,
1924 signatureUsingPeerKey: signatureUsingPeerKey)
1927 os_log("Creation of Bottled Peer failed: %@", log: tplogDebug, type: .default, error as CVarArg)
1928 reply(nil, nil, ContainerError.failedToCreateBottledPeer)
1933 os_log("Have a bottle for peer %@", log: tplogDebug, type: .default, bottledPeer.peerID)
1935 // Extract any TLKs we have been given
1936 extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys)
1938 self.moc.performAndWait {
1939 // I must have an ego identity in order to vouch using bottle
1940 guard let egoPeerID = self.containerMO.egoPeerID else {
1941 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
1942 reply(nil, nil, ContainerError.nonMember)
1945 guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
1946 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
1947 reply(nil, nil, ContainerError.nonMember)
1950 guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
1951 os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
1952 reply(nil, nil, ContainerError.nonMember)
1955 guard let stableInfo = self.containerMO.egoPeerStableInfo else {
1956 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
1957 reply(nil, nil, ContainerError.nonMember)
1960 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
1961 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
1962 reply(nil, nil, ContainerError.nonMember)
1965 let keyFactory = TPECPublicKeyFactory()
1966 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
1967 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
1968 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
1971 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
1972 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
1973 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
1978 let voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
1979 stableInfo: beneficiaryStableInfo,
1980 withSponsorID: sponsorPeerID,
1981 reason: TPVoucherReason.restore,
1982 signing: bottledPeer.peerKeys.signingKey)
1983 reply(voucher.data, voucher.sig, nil)
1986 os_log("Error creating voucher: %@", log: tplogDebug, type: .default, error as CVarArg)
1987 reply(nil, nil, error)
1996 func vouchWithRecoveryKey(recoveryKey: String,
1998 tlkShares: [CKKSTLKShare],
1999 reply: @escaping (Data?, Data?, Error?) -> Void) {
2000 self.semaphore.wait()
2001 let reply: (Data?, Data?, Error?) -> Void = {
2002 os_log("vouchWithRecoveryKey complete: %@",
2003 log: tplogTrace, type: .info, traceError($2))
2004 self.semaphore.signal()
2008 self.moc.performAndWait {
2009 os_log("beginning a vouchWithRecoveryKey", log: tplogDebug, type: .default)
2011 // I must have an ego identity in order to vouch using bottle
2012 guard let egoPeerID = self.containerMO.egoPeerID else {
2013 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2014 reply(nil, nil, ContainerError.nonMember)
2017 guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
2018 os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
2019 reply(nil, nil, ContainerError.nonMember)
2022 guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
2023 os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
2024 reply(nil, nil, ContainerError.nonMember)
2027 guard let stableInfo = self.containerMO.egoPeerStableInfo else {
2028 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
2029 reply(nil, nil, ContainerError.nonMember)
2032 guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2033 os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
2034 reply(nil, nil, ContainerError.nonMember)
2037 let keyFactory = TPECPublicKeyFactory()
2038 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2039 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2040 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2043 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2044 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2045 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2049 //create recovery key set
2050 var recoveryKeys: RecoveryKey
2052 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
2054 os_log("failed to create recovery keys: %@", log: tplogDebug, type: .default, error as CVarArg)
2055 reply(nil, nil, ContainerError.failedToCreateRecoveryKey)
2059 extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys)
2061 let signingPublicKey: Data = recoveryKeys.peerKeys.signingKey.publicKey.keyData
2062 let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionKey.publicKey.keyData
2064 os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .debug, signingPublicKey.base64EncodedString())
2065 os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .debug, encryptionPublicKey.base64EncodedString())
2067 guard self.model.isRecoveryKeyEnrolled() else {
2068 os_log("Recovery Key is not enrolled", log: tplogDebug, type: .default)
2069 reply(nil, nil, ContainerError.recoveryKeysNotEnrolled)
2073 //find matching peer containing recovery keys
2074 guard let sponsorPeerID = self.model.peerIDThatTrustsRecoveryKeys(TPRecoveryKeyPair(signingSPKI: signingPublicKey, encryptionSPKI: encryptionPublicKey)) else {
2075 os_log("Untrusted recovery key set", log: tplogDebug, type: .default)
2076 reply(nil, nil, ContainerError.untrustedRecoveryKeys)
2081 let voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2082 stableInfo: beneficiaryStableInfo,
2083 withSponsorID: sponsorPeerID,
2084 reason: TPVoucherReason.recoveryKey,
2085 signing: recoveryKeys.peerKeys.signingKey)
2086 reply(voucher.data, voucher.sig, nil)
2089 os_log("Error creating voucher using recovery key set: %@", log: tplogDebug, type: .default, error as CVarArg)
2090 reply(nil, nil, error)
2096 func vouch(peerID: String,
2097 permanentInfo: Data,
2098 permanentInfoSig: Data,
2100 stableInfoSig: Data,
2101 ckksKeys: [CKKSKeychainBackedKeySet],
2102 reply: @escaping (Data?, Data?, Error?) -> Void) {
2103 self.semaphore.wait()
2104 let reply: (Data?, Data?, Error?) -> Void = {
2105 os_log("vouch complete: %@", log: tplogTrace, type: .info, traceError($2))
2106 self.semaphore.signal()
2110 self.moc.performAndWait {
2111 // I must have an ego identity in order to vouch for someone else.
2112 guard let egoPeerID = self.containerMO.egoPeerID,
2113 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2114 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
2115 os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
2116 reply(nil, nil, ContainerError.nonMember)
2120 let keyFactory = TPECPublicKeyFactory()
2122 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2123 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2127 guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: peerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
2128 os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
2129 reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
2133 guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
2134 os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
2135 reply(nil, nil, ContainerError.invalidStableInfoOrSig)
2139 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
2140 guard let egoPeerKeys = egoPeerKeys else {
2141 os_log("Don't have my own keys: can't vouch for %@: %@", log: tplogDebug, type: .default, beneficiaryPermanentInfo, (error as CVarArg?) ?? "no error")
2142 reply(nil, nil, error)
2145 self.moc.performAndWait {
2146 let voucher: TPVoucher
2148 voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
2149 stableInfo: beneficiaryStableInfo,
2150 withSponsorID: egoPeerID,
2151 reason: TPVoucherReason.secureChannel,
2152 signing: egoPeerKeys.signingKey)
2155 os_log("Error creating voucher: %@", log: tplogDebug, type: .default, error as CVarArg)
2156 reply(nil, nil, error)
2160 // And generate and upload any tlkShares
2161 // Note that this might not be the whole list: <rdar://problem/47899980> Octagon: Limited Peers
2162 let tlkShares: [TLKShare]
2164 // Note: we only want to send up TLKs for uploaded ckks zones
2165 let ckksTLKs = ckksKeys.filter { !$0.newUpload }.map { $0.tlk }
2167 tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
2168 asPeer: egoPeerKeys,
2169 toPeer: beneficiaryPermanentInfo,
2170 epoch: Int(selfPermanentInfo.epoch))
2172 os_log("Unable to make TLKShares for beneficiary %@: %@", log: tplogDebug, type: .default, beneficiaryPermanentInfo, error as CVarArg)
2173 reply(nil, nil, error)
2177 guard !tlkShares.isEmpty else {
2178 os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
2179 reply(voucher.data, voucher.sig, nil)
2183 self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
2185 stableInfoAndSig: nil,
2186 dynamicInfoAndSig: nil,
2187 tlkShares: tlkShares,
2188 viewKeys: []) { response, error in
2189 guard let response = response, error == nil else {
2190 os_log("Unable to upload new tlkshares: %@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
2191 reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
2195 let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
2196 os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
2197 // We don't need to save these; CKKS will refetch them as needed
2199 reply(voucher.data, voucher.sig, nil)
2206 func departByDistrustingSelf(reply: @escaping (Error?) -> Void) {
2207 self.semaphore.wait()
2208 let reply: (Error?) -> Void = {
2209 os_log("departByDistrustingSelf complete: %@", log: tplogTrace, type: .info, traceError($0))
2210 self.semaphore.signal()
2214 self.moc.performAndWait {
2215 guard let egoPeerID = self.containerMO.egoPeerID else {
2216 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2217 reply(ContainerError.noPreparedIdentity)
2221 self.onqueueDistrust(peerIDs: [egoPeerID], reply: reply)
2225 func distrust(peerIDs: Set<String>,
2226 reply: @escaping (Error?) -> Void) {
2227 self.semaphore.wait()
2228 let reply: (Error?) -> Void = {
2229 os_log("distrust complete: %@", log: tplogTrace, type: .info, traceError($0))
2230 self.semaphore.signal()
2234 self.moc.performAndWait {
2235 guard let egoPeerID = self.containerMO.egoPeerID else {
2236 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2237 reply(ContainerError.noPreparedIdentity)
2241 guard !peerIDs.contains(egoPeerID) else {
2242 os_log("Self-distrust via peerID not allowed", log: tplogDebug, type: .default)
2243 reply(ContainerError.invalidPeerID)
2247 self.onqueueDistrust(peerIDs: peerIDs, reply: reply)
2251 func onqueueDistrust(peerIDs: Set<String>,
2252 reply: @escaping (Error?) -> Void) {
2254 guard let egoPeerID = self.containerMO.egoPeerID else {
2255 os_log("No dynamic info for self?", log: tplogDebug, type: .default)
2256 reply(ContainerError.noPreparedIdentity)
2260 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
2261 guard let signingKeyPair = signingKeyPair else {
2262 os_log("No longer have signing key pair; can't sign distrust: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
2267 self.moc.performAndWait {
2268 let dynamicInfo: TPPeerDynamicInfo
2270 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
2272 removingPeerIDs: Array(peerIDs),
2273 preapprovedKeys: nil,
2274 signing: signingKeyPair,
2275 currentMachineIDs: self.onqueueCurrentMIDList())
2278 os_log("Error preparing dynamic info: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
2283 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
2284 os_log("attempting distrust for %@ with: %@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
2286 let request = UpdateTrustRequest.with {
2287 $0.changeToken = self.containerMO.changeToken ?? ""
2288 $0.peerID = egoPeerID
2289 $0.dynamicInfoAndSig = signedDynamicInfo
2291 self.cuttlefish.updateTrust(request) { response, error in
2292 os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2293 guard let response = response, error == nil else {
2294 os_log("updateTrust failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2295 reply(error ?? ContainerError.cloudkitResponseMissing)
2300 try self.persist(changes: response.changes)
2301 os_log("distrust succeeded", log: tplogDebug, type: .default)
2304 os_log("distrust handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
2312 func fetchEscrowContents(reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
2313 self.semaphore.wait()
2314 let reply: (Data?, String?, Data?, Error?) -> Void = {
2315 os_log("fetchEscrowContents complete: %@", log: tplogTrace, type: .info, traceError($3))
2316 self.semaphore.signal()
2317 reply($0, $1, $2, $3)
2319 os_log("beginning a fetchEscrowContents", log: tplogDebug, type: .default)
2321 self.moc.performAndWait {
2322 guard let egoPeerID = self.containerMO.egoPeerID else {
2323 os_log("fetchEscrowContents failed", log: tplogDebug, type: .default)
2324 reply(nil, nil, nil, ContainerError.noPreparedIdentity)
2328 guard let bottles = self.containerMO.bottles as? Set<BottleMO> else {
2329 os_log("fetchEscrowContents failed", log: tplogDebug, type: .default)
2330 reply(nil, nil, nil, ContainerError.noBottleForPeer)
2334 var bmoSet = bottles.filter { $0.peerID == egoPeerID }
2335 let bmo = bmoSet.removeFirst()
2336 let bottleID = bmo.bottleID
2340 guard let loaded = try loadSecret(label: egoPeerID) else {
2341 os_log("fetchEscrowContents failed to load entropy", log: tplogDebug, type: .default)
2342 reply(nil, nil, nil, ContainerError.failedToFetchEscrowContents)
2347 os_log("fetchEscrowContents failed to load entropy: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2348 reply(nil, nil, nil, error)
2352 guard let signingPublicKey = bmo.escrowedSigningSPKI else {
2353 os_log("fetchEscrowContents no escrow signing spki", log: tplogDebug, type: .default)
2354 reply(nil, nil, nil, ContainerError.bottleDoesNotContainerEscrowKeySPKI)
2357 reply(entropy, bottleID, signingPublicKey, nil)
2361 func fetchViableBottles(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
2362 self.semaphore.wait()
2363 let reply: ([String]?, [String]?, Error?) -> Void = {
2364 os_log("fetchViableBottles complete: %@", log: tplogTrace, type: .info, traceError($2))
2365 self.semaphore.signal()
2369 self.fetchViableBottlesWithSemaphore(reply: reply)
2372 func onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: TPCachedViableBottles) -> Bool {
2373 guard let egoPeerID = self.containerMO.egoPeerID else {
2374 os_log("bottleForEgoPeer: No identity.", log: tplogDebug, type: .default)
2377 guard let bottles: Set<BottleMO> = self.containerMO.bottles as? Set<BottleMO> else {
2378 os_log("bottleForEgoPeer: No Bottles.", log: tplogDebug, type: .default)
2381 var matchesCached: Bool = false
2382 for bottle in bottles {
2383 guard let bottleID: String = bottle.bottleID else {
2386 if bottle.peerID == egoPeerID && (cachedBottles.viableBottles.contains(bottleID) || cachedBottles.partialBottles.contains(bottleID)) {
2387 matchesCached = true
2391 return matchesCached
2394 func fetchViableBottlesWithSemaphore(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
2395 os_log("beginning a fetchViableBottles", log: tplogDebug, type: .default)
2397 let cachedBottles: TPCachedViableBottles = self.model.currentCachedViableBottlesSet()
2398 self.moc.performAndWait {
2399 if self.onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: cachedBottles)
2400 && (cachedBottles.viableBottles.count > 0 || cachedBottles.partialBottles.count > 0) {
2401 os_log("returning from fetchViableBottles, using cached bottles", log: tplogDebug, type: .default)
2402 reply(cachedBottles.viableBottles, cachedBottles.partialBottles, nil)
2406 self.cuttlefish.fetchViableBottles { response, error in
2407 guard error == nil else {
2408 os_log("fetchViableBottles failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2409 reply(nil, nil, error)
2413 self.moc.performAndWait {
2415 guard let escrowPairs = response?.viableBottles else {
2416 os_log("fetchViableBottles returned no viable bottles: %@", log: tplogDebug, type: .default)
2421 var partialPairs: [EscrowPair] = []
2422 if let partial = response?.partialBottles {
2423 partialPairs = partial
2425 os_log("fetchViableBottles returned no partially viable bottles, but that's ok: %@", log: tplogDebug, type: .default)
2428 let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
2429 os_log("fetchViableBottles returned viable bottles: %@", log: tplogDebug, type: .default, viableBottleIDs)
2431 let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
2432 os_log("fetchViableBottles returned partial bottles: %@", log: tplogDebug, type: .default, partialBottleIDs)
2434 escrowPairs.forEach { pair in
2435 let bottle = pair.bottle
2437 // Save this bottle only if we don't already have it
2438 if let existingBottles = self.containerMO.bottles as? Set<BottleMO> {
2439 let matchingBottles: Set<BottleMO> = existingBottles.filter { existing in
2440 existing.peerID == bottle.peerID &&
2441 existing.bottleID == bottle.bottleID &&
2442 existing.escrowedSigningSPKI == bottle.escrowedSigningSpki &&
2443 existing.signatureUsingEscrowKey == bottle.signatureUsingEscrowKey &&
2444 existing.signatureUsingPeerKey == bottle.signatureUsingPeerKey &&
2445 existing.contents == bottle.contents
2447 if !matchingBottles.isEmpty {
2448 os_log("fetchViableBottles already knows about bottle", log: tplogDebug, type: .default, bottle.bottleID)
2453 let bmo = BottleMO(context: self.moc)
2454 bmo.peerID = bottle.peerID
2455 bmo.bottleID = bottle.bottleID
2456 bmo.escrowedSigningSPKI = bottle.escrowedSigningSpki
2457 bmo.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
2458 bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
2459 bmo.contents = bottle.contents
2461 os_log("fetchViableBottles saving new bottle: %@", log: tplogDebug, type: .default, bmo)
2462 self.containerMO.addToBottles(bmo)
2465 partialPairs.forEach { pair in
2466 let bottle = pair.bottle
2468 // Save this bottle only if we don't already have it
2469 if let existingBottles = self.containerMO.bottles as? Set<BottleMO> {
2470 let matchingBottles: Set<BottleMO> = existingBottles.filter { existing in
2471 existing.peerID == bottle.peerID &&
2472 existing.bottleID == bottle.bottleID &&
2473 existing.escrowedSigningSPKI == bottle.escrowedSigningSpki &&
2474 existing.signatureUsingEscrowKey == bottle.signatureUsingEscrowKey &&
2475 existing.signatureUsingPeerKey == bottle.signatureUsingPeerKey &&
2476 existing.contents == bottle.contents
2478 if !matchingBottles.isEmpty {
2479 os_log("fetchViableBottles already knows about bottle", log: tplogDebug, type: .default, bottle.bottleID)
2484 let bmo = BottleMO(context: self.moc)
2485 bmo.peerID = bottle.peerID
2486 bmo.bottleID = bottle.bottleID
2487 bmo.escrowedSigningSPKI = bottle.escrowedSigningSpki
2488 bmo.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
2489 bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
2490 bmo.contents = bottle.contents
2492 os_log("fetchViableBottles saving new bottle: %@", log: tplogDebug, type: .default, bmo)
2493 self.containerMO.addToBottles(bmo)
2498 os_log("fetchViableBottles saved bottles", log: tplogDebug, type: .default)
2499 let cached = TPCachedViableBottles(viableBottles: viableBottleIDs, partialBottles: partialBottleIDs)
2500 self.model.setViableBottles(cached)
2501 reply(viableBottleIDs, partialBottleIDs, nil)
2503 os_log("fetchViableBottles unable to save bottles: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2504 reply(nil, nil, error)
2512 func fetchPolicy(reply: @escaping (TPPolicy?, Error?) -> Void) {
2513 self.semaphore.wait()
2514 let reply: (TPPolicy?, Error?) -> Void = {
2515 os_log("fetchPolicy complete: %@", log: tplogTrace, type: .info, traceError($1))
2516 self.semaphore.signal()
2520 self.moc.performAndWait {
2521 var keys: [NSNumber: String] = [:]
2523 guard let stableInfoData = self.containerMO.egoPeerStableInfo,
2524 let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
2525 os_log("fetchPolicy failed to find ego peer stableinfodata/sig", log: tplogDebug, type: .error)
2526 reply(nil, ContainerError.noPreparedIdentity)
2529 guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
2530 os_log("fetchPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
2531 reply(nil, ContainerError.invalidStableInfoOrSig)
2535 let policyVersionCounter = stableInfo.policyVersion
2536 let policyVersion = NSNumber(value: policyVersionCounter)
2537 keys[policyVersion] = stableInfo.policyHash
2539 if let policyDocument = self.model.policy(withVersion: policyVersionCounter) {
2540 os_log("fetchPolicy: have a local version of policy %@: %@", log: tplogDebug, type: .default, policyVersion, policyDocument)
2542 let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
2546 os_log("TPPolicyDocument failed: %@", log: tplogDebug, type: .default, error as CVarArg)
2552 self.fetchPolicyDocuments(keys: keys) { result, error in
2553 guard error == nil else {
2557 guard let result = result else {
2558 os_log("fetchPolicy: nil policies returned")
2559 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2562 guard result.count == 1 else {
2563 os_log("fetchPolicy: wrong length returned")
2564 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2567 guard let r = result[policyVersion] else {
2568 os_log("fetchPolicy: version not found")
2569 reply(nil, ContainerError.unknownPolicyVersion(policyVersion.uint64Value))
2572 guard let data = r[1].data(using: .utf8) else {
2573 os_log("fetchPolicy: failed to convert data")
2574 reply(nil, ContainerError.unknownPolicyVersion(policyVersion.uint64Value))
2577 guard let pd = TPPolicyDocument.policyDoc(withHash: r[0], data: data) else {
2578 os_log("fetchPolicy: pd is nil")
2579 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2583 let policy = try pd.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
2586 os_log("TPPolicyDocument: %@", log: tplogDebug, type: .default, error as CVarArg)
2593 // All-or-nothing: return an error in case full list cannot be returned.
2594 // Completion handler data format: [version : [hash, data]]
2595 func fetchPolicyDocuments(keys: [NSNumber: String],
2596 reply: @escaping ([NSNumber: [String]]?, Error?) -> Void) {
2597 self.semaphore.wait()
2598 let reply: ([NSNumber: [String]]?, Error?) -> Void = {
2599 os_log("fetchPolicyDocuments complete: %@", log: tplogTrace, type: .info, traceError($1))
2600 self.semaphore.signal()
2605 var docs: [NSNumber: [String]] = [:]
2607 self.moc.performAndWait {
2608 for (version, hash) in keys {
2609 if let policydoc = try? self.getPolicyDoc(version.uint64Value), policydoc.policyHash == hash {
2610 docs[version] = [policydoc.policyHash, policydoc.protobuf.base64EncodedString()]
2621 let request = FetchPolicyDocumentsRequest.with {
2622 $0.keys = keys.map { key, value in
2623 PolicyDocumentKey.with { $0.version = key.uint64Value; $0.hash = value }}
2626 self.cuttlefish.fetchPolicyDocuments(request) { response, error in
2627 os_log("FetchPolicyDocuments(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2628 guard let response = response, error == nil else {
2629 os_log("FetchPolicyDocuments failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2630 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
2634 self.moc.performAndWait {
2635 for mapEntry in response.entries {
2636 // TODO: validate the policy's signature
2638 guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
2639 os_log("Can't make policy document with hash %@ and data %@",
2640 log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
2641 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2645 guard let hash = keys[NSNumber(value: doc.policyVersion)], hash == doc.policyHash else {
2646 os_log("Requested hash %@ does not match fetched hash %@", log: tplogDebug, type: .default,
2647 keys[NSNumber(value: doc.policyVersion)] ?? "<nil>", doc.policyHash)
2648 reply(nil, ContainerError.policyDocumentDoesNotValidate)
2651 keys[NSNumber(value: doc.policyVersion)] = nil // Server responses should be unique, let's enforce
2652 docs[NSNumber(value: doc.policyVersion)] = [doc.policyHash, doc.protobuf.base64EncodedString()]
2653 self.model.register(doc)
2657 try self.moc.save() // if this fails callers might make bad data assumptions
2664 let (unknownVersion, _) = keys.first!
2665 reply(nil, ContainerError.unknownPolicyVersion(unknownVersion.uint64Value))
2674 // Must be on moc queue to call this.
2675 // Caller is responsible for saving the moc afterwards.
2677 private func registerPeerMO(permanentInfo: TPPeerPermanentInfo,
2678 stableInfo: TPPeerStableInfo? = nil,
2679 dynamicInfo: TPPeerDynamicInfo? = nil,
2680 vouchers: [TPVoucher]? = nil,
2681 isEgoPeer: Bool = false) throws -> PeerMO {
2682 let peerID = permanentInfo.peerID
2684 let peer = PeerMO(context: self.moc)
2685 peer.peerID = peerID
2686 peer.permanentInfo = permanentInfo.data
2687 peer.permanentInfoSig = permanentInfo.sig
2688 peer.stableInfo = stableInfo?.data
2689 peer.stableInfoSig = stableInfo?.sig
2690 peer.dynamicInfo = dynamicInfo?.data
2691 peer.dynamicInfoSig = dynamicInfo?.sig
2692 peer.isEgoPeer = isEgoPeer
2693 self.containerMO.addToPeers(peer)
2695 self.model.registerPeer(with: permanentInfo)
2696 if let stableInfo = stableInfo {
2697 try self.model.update(stableInfo, forPeerWithID: peerID)
2699 if let dynamicInfo = dynamicInfo {
2700 try self.model.update(dynamicInfo, forPeerWithID: peerID)
2702 vouchers?.forEach { voucher in
2703 self.model.register(voucher)
2704 let voucherMO = VoucherMO(context: self.moc)
2705 voucherMO.voucherInfo = voucher.data
2706 voucherMO.voucherInfoSig = voucher.sig
2707 peer.addToVouchers(voucherMO)
2712 /* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
2713 func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
2714 tlkShares: [CKKSTLKShare],
2715 egoPeerKeys: OctagonSelfPeerKeys,
2716 egoPeerDynamicInfo: TPPeerDynamicInfo,
2717 epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
2718 let newCKKSKeys = ckksKeys.filter { $0.newUpload }
2719 let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
2721 let octagonSelfShares = try makeTLKShares(ckksTLKs: ckksKeys.map { $0.tlk },
2722 asPeer: egoPeerKeys,
2723 toPeer: egoPeerKeys,
2725 let extraShares = tlkShares.map { TLKShare.convert(ckksTLKShare: $0) }
2727 var peerShares: [TLKShare] = []
2729 for keyset in newCKKSKeys {
2731 let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
2732 toAccessView: keyset.tlk.zoneID.zoneName)
2733 os_log("Planning to share %@ with peers %@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
2735 let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
2736 let viewPeerShares = try peers.map { receivingPeer in
2737 TLKShare.convert(ckksTLKShare: try CKKSTLKShare(keyset.tlk,
2739 to: receivingPeer.permanentInfo,
2744 peerShares = peerShares + viewPeerShares
2747 os_log("Unable to create TLKShares for keyset %@: %@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
2751 return (newViewKeys, octagonSelfShares + peerShares + extraShares)
2754 func onqueuePreparePeerForJoining(egoPeerID: String,
2755 peerPermanentInfo: TPPeerPermanentInfo,
2756 stableInfo: TPPeerStableInfo,
2758 preapprovedKeys: [Data]?,
2759 vouchers: [SignedVoucher],
2760 egoPeerKeys: OctagonSelfPeerKeys) throws -> (Peer, TPPeerDynamicInfo) {
2761 let dynamicInfo = try self.model.dynamicInfo(forJoiningPeerID: egoPeerID,
2762 peerPermanentInfo: peerPermanentInfo,
2763 peerStableInfo: stableInfo,
2764 sponsorID: sponsorID,
2765 preapprovedKeys: preapprovedKeys,
2766 signing: egoPeerKeys.signingKey,
2767 currentMachineIDs: self.onqueueCurrentMIDList())
2769 let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: nil,
2770 egoPeerID: egoPeerID,
2771 dynamicInfo: dynamicInfo,
2772 signingKeyPair: egoPeerKeys.signingKey)
2774 let peer = Peer.with {
2775 $0.peerID = egoPeerID
2776 $0.permanentInfoAndSig = SignedPeerPermanentInfo(peerPermanentInfo)
2777 $0.stableInfoAndSig = SignedPeerStableInfo(newStableInfo ?? stableInfo)
2778 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
2779 $0.vouchers = vouchers
2782 return (peer, dynamicInfo)
2785 func join(voucherData: Data,
2787 ckksKeys: [CKKSKeychainBackedKeySet],
2788 tlkShares: [CKKSTLKShare],
2789 preapprovedKeys: [Data]?,
2790 reply: @escaping (String?, [CKRecord], Error?) -> Void) {
2791 self.semaphore.wait()
2792 let reply: (String?, [CKRecord], Error?) -> Void = {
2793 os_log("join complete: %@", log: tplogTrace, type: .info, traceError($2))
2794 self.semaphore.signal()
2798 self.fetchAndPersistChanges { error in
2799 guard error == nil else {
2800 reply(nil, [], error)
2803 self.moc.performAndWait {
2804 guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
2805 reply(nil, [], ContainerError.invalidVoucherOrSig)
2808 guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
2809 reply(nil, [], ContainerError.sponsorNotRegistered(voucher.sponsorID))
2813 // Fetch ego peer identity from local storage.
2814 guard let egoPeerID = self.containerMO.egoPeerID,
2815 let egoPermData = self.containerMO.egoPeerPermanentInfo,
2816 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
2817 let egoStableData = self.containerMO.egoPeerStableInfo,
2818 let egoStableSig = self.containerMO.egoPeerStableInfoSig
2820 reply(nil, [], ContainerError.noPreparedIdentity)
2824 let keyFactory = TPECPublicKeyFactory()
2825 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
2826 reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
2829 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
2830 reply(nil, [], ContainerError.invalidStableInfoOrSig)
2833 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
2834 os_log("join: self machineID %@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
2835 self.onqueueTTRUntrusted()
2836 reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
2840 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
2841 guard let egoPeerKeys = egoPeerKeys else {
2842 os_log("Don't have my own peer keys; can't join: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
2843 reply(nil, [], error)
2846 self.moc.performAndWait {
2848 let newDynamicInfo: TPPeerDynamicInfo
2850 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
2851 peerPermanentInfo: selfPermanentInfo,
2852 stableInfo: selfStableInfo,
2853 sponsorID: sponsor.peerID,
2854 preapprovedKeys: preapprovedKeys,
2855 vouchers: [SignedVoucher.with {
2856 $0.voucher = voucher.data
2857 $0.sig = voucher.sig
2859 egoPeerKeys: egoPeerKeys)
2861 os_log("Unable to create peer for joining: %@", log: tplogDebug, type: .default, error as CVarArg)
2862 reply(nil, [], error)
2866 let allTLKShares: [TLKShare]
2867 let viewKeys: [ViewKeys]
2869 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
2870 tlkShares: tlkShares,
2871 egoPeerKeys: egoPeerKeys,
2872 egoPeerDynamicInfo: newDynamicInfo,
2873 epoch: Int(selfPermanentInfo.epoch))
2875 os_log("Unable to process keys before joining: %@", log: tplogDebug, type: .default, error as CVarArg)
2876 reply(nil, [], error)
2881 try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
2882 stableInfo: peer.stableInfoAndSig.toStableInfo(),
2883 withSponsorID: sponsor.peerID)
2885 os_log("Error checking introduction: %@", log: tplogDebug, type: .default, error as CVarArg)
2886 reply(nil, [], error)
2892 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
2894 reply(nil, [], error)
2898 os_log("Beginning join for peer %@", log: tplogDebug, type: .default, egoPeerID)
2899 os_log("Join permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
2900 os_log("Join permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
2901 os_log("Join stableInfo: %@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
2902 os_log("Join stableInfoSig: %@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
2903 os_log("Join dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
2904 os_log("Join dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
2906 os_log("Join vouchers: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
2907 os_log("Join voucher signatures: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
2909 os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
2912 os_log("Join peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
2914 os_log("Join unable to encode peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
2917 let changeToken = self.containerMO.changeToken ?? ""
2918 let request = JoinWithVoucherRequest.with {
2919 $0.changeToken = changeToken
2922 $0.tlkShares = allTLKShares
2923 $0.viewKeys = viewKeys
2925 self.cuttlefish.joinWithVoucher(request) { response, error in
2926 os_log("JoinWithVoucher(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
2927 guard let response = response, error == nil else {
2928 os_log("joinWithVoucher failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
2929 reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
2933 self.moc.performAndWait {
2935 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
2936 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
2937 try self.onQueuePersist(changes: response.changes)
2938 os_log("JoinWithVoucher succeeded", log: tplogDebug)
2940 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
2941 reply(egoPeerID, keyHierarchyRecords, nil)
2943 os_log("JoinWithVoucher failed: %@", log: tplogDebug, String(describing: error))
2944 reply(nil, [], error)
2954 func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Error?) -> Void) {
2955 self.semaphore.wait()
2956 let reply: (Bool, Bool, Bool, Error?) -> Void = {
2957 os_log("health check complete: %@", log: tplogTrace, type: .info, traceError($3))
2958 self.semaphore.signal()
2959 reply($0, $1, $2, $3)
2962 os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
2964 self.moc.performAndWait {
2965 guard let egoPeerID = self.containerMO.egoPeerID else {
2966 // No identity, nothing to do
2967 os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
2968 reply(false, false, false, ContainerError.noPreparedIdentity)
2971 let request = GetRepairActionRequest.with {
2972 $0.peerID = egoPeerID
2973 $0.requiresEscrowCheck = requiresEscrowCheck
2976 self.cuttlefish.getRepairAction(request) { response, error in
2977 guard error == nil else {
2978 reply(false, false, false, error)
2981 guard let action = response?.repairAction else {
2982 os_log("repair response is empty, returning false: %@", log: tplogDebug, type: .default)
2983 reply(false, false, false, nil)
2986 var postRepairAccount: Bool = false
2987 var postRepairEscrow: Bool = false
2988 var resetOctagon: Bool = false
2993 case .postRepairAccount:
2994 postRepairAccount = true
2996 case .postRepairEscrow:
2997 postRepairEscrow = true
3005 reply(postRepairAccount, postRepairEscrow, resetOctagon, nil)
3010 func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
3011 self.semaphore.wait()
3012 let reply: (Data?, Error?) -> Void = {
3013 os_log("getSupportAppInfo complete: %@", log: tplogTrace, type: .info, traceError($1))
3014 self.semaphore.signal()
3018 self.cuttlefish.getSupportAppInfo { response, error in
3019 os_log("getSupportAppInfo(): %@, error: %@", log: tplogDebug,
3020 "(\(String(describing: response))", "\(String(describing: error))")
3021 guard let response = response, error == nil else {
3022 os_log("getSupportAppInfo failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3023 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3027 guard let data = try? response.serializedData() else {
3028 reply(nil, ContainerError.failedToSerializeData)
3037 func preflightPreapprovedJoin(reply: @escaping (Bool, Error?) -> Void) {
3038 self.semaphore.wait()
3039 let reply: (Bool, Error?) -> Void = {
3040 os_log("preflightPreapprovedJoin complete: %@", log: tplogTrace, type: .info, traceError($1))
3041 self.semaphore.signal()
3045 self.fetchAndPersistChanges { error in
3046 guard error == nil else {
3047 os_log("preflightPreapprovedJoin unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3052 // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
3054 guard !self.model.allPeerIDs().isEmpty else {
3055 // If, after fetch and handle changes, there's no peers, then we can likely establish.
3060 guard let egoPeerID = self.containerMO.egoPeerID,
3061 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3062 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3064 os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3065 reply(false, ContainerError.noPreparedIdentity)
3069 let keyFactory = TPECPublicKeyFactory()
3070 guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3071 os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
3072 reply(false, ContainerError.invalidPermanentInfoOrSig)
3076 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
3077 os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3078 reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
3086 func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
3087 tlkShares: [CKKSTLKShare],
3088 preapprovedKeys: [Data]?,
3089 reply: @escaping (String?, [CKRecord], Error?) -> Void) {
3090 self.semaphore.wait()
3091 let reply: (String?, [CKRecord], Error?) -> Void = {
3092 os_log("preapprovedJoin complete: %@", log: tplogTrace, type: .info, traceError($2))
3093 self.semaphore.signal()
3097 self.fetchAndPersistChangesIfNeeded { error in
3098 guard error == nil else {
3099 os_log("preapprovedJoin unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
3100 reply(nil, [], error)
3103 self.moc.performAndWait {
3104 // If, after fetch and handle changes, there's no peers, then fire off an establish
3105 // Note that if the establish fails, retrying this call might work.
3106 // That's up to the caller.
3107 if self.model.allPeerIDs().isEmpty {
3108 os_log("preapprovedJoin but no existing peers, attempting establish", log: tplogDebug, type: .debug)
3109 self.onqueueEstablish(ckksKeys: ckksKeys,
3110 tlkShares: tlkShares,
3111 preapprovedKeys: preapprovedKeys,
3116 // Fetch ego peer identity from local storage.
3117 guard let egoPeerID = self.containerMO.egoPeerID,
3118 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3119 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
3120 let egoStableData = self.containerMO.egoPeerStableInfo,
3121 let egoStableSig = self.containerMO.egoPeerStableInfoSig
3123 os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
3124 reply(nil, [], ContainerError.noPreparedIdentity)
3128 let keyFactory = TPECPublicKeyFactory()
3129 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
3130 reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
3133 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
3134 reply(nil, [], ContainerError.invalidStableInfoOrSig)
3138 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
3139 os_log("preapprovedJoin: self machineID %@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
3140 self.onqueueTTRUntrusted()
3141 reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
3144 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3145 guard let egoPeerKeys = egoPeerKeys else {
3146 os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3147 reply(nil, [], error)
3151 guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
3152 os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
3153 reply(nil, [], ContainerError.noPeersPreapprovePreparedIdentity)
3157 self.moc.performAndWait {
3160 let newDynamicInfo: TPPeerDynamicInfo
3162 (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
3163 peerPermanentInfo: selfPermanentInfo,
3164 stableInfo: selfStableInfo,
3166 preapprovedKeys: preapprovedKeys,
3168 egoPeerKeys: egoPeerKeys)
3170 os_log("Unable to create peer for joining: %@", log: tplogDebug, type: .default, error as CVarArg)
3171 reply(nil, [], error)
3175 let allTLKShares: [TLKShare]
3176 let viewKeys: [ViewKeys]
3178 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3179 tlkShares: tlkShares,
3180 egoPeerKeys: egoPeerKeys,
3181 egoPeerDynamicInfo: newDynamicInfo,
3182 epoch: Int(selfPermanentInfo.epoch))
3184 os_log("Unable to process keys before joining: %@", log: tplogDebug, type: .default, error as CVarArg)
3185 reply(nil, [], error)
3191 bottle = try self.assembleBottle(egoPeerID: egoPeerID)
3193 reply(nil, [], error)
3197 os_log("Beginning preapprovedJoin for peer %@", log: tplogDebug, type: .default, egoPeerID)
3198 os_log("preapprovedJoin permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
3199 os_log("preapprovedJoin permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
3200 os_log("preapprovedJoin stableInfo: %@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
3201 os_log("preapprovedJoin stableInfoSig: %@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
3202 os_log("preapprovedJoin dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
3203 os_log("preapprovedJoin dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
3205 os_log("preapprovedJoin vouchers: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
3206 os_log("preapprovedJoin voucher signatures: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
3208 os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
3211 os_log("preapprovedJoin peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
3213 os_log("preapprovedJoin unable to encode peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
3216 let changeToken = self.containerMO.changeToken ?? ""
3217 let request = JoinWithVoucherRequest.with {
3218 $0.changeToken = changeToken
3221 $0.tlkShares = allTLKShares
3222 $0.viewKeys = viewKeys
3224 self.cuttlefish.joinWithVoucher(request) { response, error in
3225 os_log("preapprovedJoin(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3226 guard let response = response, error == nil else {
3227 os_log("preapprovedJoin failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3228 reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
3232 self.moc.performAndWait {
3234 self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
3235 self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
3236 try self.onQueuePersist(changes: response.changes)
3237 os_log("preapprovedJoin succeeded", log: tplogDebug)
3239 let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
3240 reply(egoPeerID, keyHierarchyRecords, nil)
3242 os_log("preapprovedJoin failed: %@", log: tplogDebug, String(describing: error))
3243 reply(nil, [], error)
3253 func update(deviceName: String?,
3254 serialNumber: String?,
3256 policyVersion: UInt64?,
3257 policySecrets: [String: Data]?,
3258 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3259 self.semaphore.wait()
3260 let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
3261 os_log("update complete: %@", log: tplogTrace, type: .info, traceError($1))
3262 self.semaphore.signal()
3266 // Get (and save) the latest from cuttlefish
3267 let stableChanges = StableChanges(deviceName: deviceName,
3268 serialNumber: serialNumber,
3269 osVersion: osVersion,
3270 policyVersion: policyVersion,
3271 policySecrets: policySecrets,
3272 recoverySigningPubKey: nil,
3273 recoveryEncryptionPubKey: nil)
3274 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
3277 func set(preapprovedKeys: [Data],
3278 reply: @escaping (Error?) -> Void) {
3279 self.semaphore.wait()
3280 let reply: (Error?) -> Void = {
3281 os_log("setPreapprovedKeys complete: %@", log: tplogTrace, type: .info, traceError($0))
3282 self.semaphore.signal()
3286 self.moc.performAndWait {
3287 os_log("setPreapprovedKeys: %@", log: tplogDebug, type: .default, preapprovedKeys)
3289 guard let egoPeerID = self.containerMO.egoPeerID else {
3290 // No identity, nothing to do
3291 os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
3292 reply(ContainerError.noPreparedIdentity)
3295 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3296 guard let signingKeyPair = signingKeyPair else {
3297 os_log("setPreapprovedKeys: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3298 reply(error ?? ContainerError.unableToCreateKeyPair)
3302 self.moc.performAndWait {
3303 let dynamicInfo: TPPeerDynamicInfo
3305 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3307 removingPeerIDs: nil,
3308 preapprovedKeys: preapprovedKeys,
3309 signing: signingKeyPair,
3310 currentMachineIDs: self.onqueueCurrentMIDList())
3312 os_log("setPreapprovedKeys: couldn't calculate dynamic info: %@", log: tplogDebug, type: .default, error as CVarArg)
3317 os_log("setPreapprovedKeys: produced a dynamicInfo: %@", log: tplogDebug, type: .default, dynamicInfo)
3319 if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
3320 os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
3325 os_log("setPreapprovedKeys: attempting updateTrust for %@ with: %@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3326 let request = UpdateTrustRequest.with {
3327 $0.changeToken = self.containerMO.changeToken ?? ""
3328 $0.peerID = egoPeerID
3329 $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
3332 self.cuttlefish.updateTrust(request) { response, error in
3333 os_log("setPreapprovedKeys(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3334 guard let response = response, error == nil else {
3335 os_log("setPreapprovedKeys failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3336 reply(error ?? ContainerError.cloudkitResponseMissing)
3339 os_log("setPreapprovedKeys: updateTrust suceeded", log: tplogDebug, type: .default)
3342 try self.persist(changes: response.changes)
3344 os_log("setPreapprovedKeys: could not persist changes: %@", log: tplogDebug, type: .default, error as CVarArg)
3356 func updateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
3357 tlkShares: [CKKSTLKShare],
3358 reply: @escaping ([CKRecord]?, Error?) -> Void) {
3359 self.semaphore.wait()
3360 let reply: ([CKRecord]?, Error?) -> Void = {
3361 os_log("updateTLKs complete: %@", log: tplogTrace, type: .info, traceError($1))
3362 self.semaphore.signal()
3366 os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
3368 self.moc.performAndWait {
3369 guard let egoPeerID = self.containerMO.egoPeerID,
3370 let egoPermData = self.containerMO.egoPeerPermanentInfo,
3371 let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
3373 os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
3374 reply(nil, ContainerError.noPreparedIdentity)
3378 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
3379 os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
3380 reply(nil, ContainerError.invalidPermanentInfoOrSig)
3384 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
3385 guard let egoPeerKeys = egoPeerKeys else {
3386 os_log("Don't have my own peer keys; can't upload new TLKs: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
3390 self.moc.performAndWait {
3391 guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
3392 os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
3393 reply(nil, ContainerError.missingDynamicInfo)
3397 let allTLKShares: [TLKShare]
3398 let viewKeys: [ViewKeys]
3400 (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
3401 tlkShares: tlkShares,
3402 egoPeerKeys: egoPeerKeys,
3403 egoPeerDynamicInfo: egoPeerDynamicInfo,
3404 epoch: Int(selfPermanentInfo.epoch))
3406 os_log("Unable to process keys before uploading: %@", log: tplogDebug, type: .default, error as CVarArg)
3411 let request = UpdateTrustRequest.with {
3412 $0.changeToken = self.containerMO.changeToken ?? ""
3413 $0.peerID = egoPeerID
3414 $0.tlkShares = allTLKShares
3415 $0.viewKeys = viewKeys
3418 self.cuttlefish.updateTrust(request) { response, error in
3419 os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3421 guard error == nil else {
3426 let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
3427 os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
3428 reply(keyHierarchyRecords, nil)
3435 func getState(reply: @escaping (ContainerState) -> Void) {
3436 self.semaphore.wait()
3437 let reply: (ContainerState) -> Void = {
3438 os_log("getState complete: %@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
3439 self.semaphore.signal()
3443 self.moc.performAndWait {
3444 var state = ContainerState()
3445 state.egoPeerID = self.containerMO.egoPeerID
3447 if self.containerMO.bottles != nil {
3448 self.containerMO.bottles!.forEach { bottle in
3449 state.bottles.insert(bottle as! BottleMO)
3453 self.model.allPeers().forEach { peer in
3454 state.peers[peer.peerID] = peer
3456 state.vouchers = Array(self.model.allVouchers())
3461 // This will only fetch changes if no changes have ever been fetched before
3462 private func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
3463 self.moc.performAndWait {
3464 if self.containerMO.changeToken == nil {
3465 self.onqueueFetchAndPersistChanges(reply: reply)
3472 private func fetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3473 self.moc.performAndWait {
3474 self.onqueueFetchAndPersistChanges(reply: reply)
3478 private func onqueueFetchAndPersistChanges(reply: @escaping (Error?) -> Void) {
3479 let request = FetchChangesRequest.with {
3480 $0.changeToken = self.containerMO.changeToken ?? ""
3482 os_log("Fetching with change token: %@", log: tplogDebug, type: .default, request.changeToken.count > 0 ? request.changeToken : "empty")
3484 self.cuttlefish.fetchChanges(request) { response, error in
3485 os_log("FetchChanges(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3486 guard let response = response, error == nil else {
3488 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
3489 os_log("change token is expired; resetting local CK storage", log: tplogDebug, type: .default)
3491 self.moc.performAndWait {
3493 try self.deleteLocalCloudKitData()
3495 os_log("Failed to reset local data: %@", log: tplogDebug, type: .default, error as CVarArg)
3500 self.fetchAndPersistChanges(reply: reply)
3505 os_log("Fetch error is an unknown error: %@", log: tplogDebug, type: .default, String(describing: error))
3508 os_log("Could not fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3514 try self.persist(changes: response.changes)
3516 os_log("Could not persist changes: %@", log: tplogDebug, type: .default, error as CVarArg)
3521 if response.changes.more {
3522 os_log("persist: More changes indicated. Fetching...", log: tplogDebug, type: .default)
3523 self.fetchAndPersistChanges(reply: reply)
3526 os_log("persist: no more changes!", log: tplogDebug, type: .default)
3533 // Check for delta update in trust lists, that should lead to update of TLKShares
3535 private func haveTrustMemberChanges(newDynamicInfo: TPPeerDynamicInfo, oldDynamicInfo: TPPeerDynamicInfo?) -> Bool {
3536 guard let oldDynamicInfo = oldDynamicInfo else {
3539 if (newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs) {
3542 if (newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs) {
3545 if (newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals) {
3551 // Fetch and persist changes from Cuttlefish. If this results
3552 // in us calculating a new dynamicInfo then give the new dynamicInfo
3553 // to Cuttlefish. If Cuttlefish returns more changes then persist
3554 // them locally, update dynamicInfo if needed, and keep doing that
3555 // until dynamicInfo is unchanged and there are no more changes to fetch.
3557 // Must be holding the semaphore to call this, and it remains
3558 // the caller's responsibility to release it after it completes
3559 // (i.e. after reply is invoked).
3560 internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
3561 changesPending: Bool = false,
3562 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3563 self.fetchAndPersistChanges { error in
3564 if let error = error {
3565 os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %@", log: tplogDebug, type: .default, error as CVarArg)
3570 self.updateTrustIfNeeded(stableChanges: stableChanges, changesPending: changesPending, reply: reply)
3574 // If this results in us calculating a new dynamicInfo then,
3575 // upload the new dynamicInfo
3576 // to Cuttlefish. If Cuttlefish returns more changes then persist
3577 // them locally, update dynamicInfo if needed, and keep doing that
3578 // until dynamicInfo is unchanged and there are no more changes to fetch.
3580 // Must be holding the semaphore to call this, and it remains
3581 // the caller's responsibility to release it after it completes
3582 // (i.e. after reply is invoked).
3583 private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
3584 changesPending: Bool = false,
3585 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
3586 self.moc.performAndWait {
3587 guard let egoPeerID = self.containerMO.egoPeerID else {
3588 // No identity, nothing to do
3589 os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
3590 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: changesPending, unknownMachineIDs: false, osVersion: nil), nil)
3593 loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
3594 guard let signingKeyPair = signingKeyPair else {
3595 os_log("updateTrustIfNeeded: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3596 reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: changesPending, unknownMachineIDs: false, osVersion: nil), error)
3599 guard self.model.hasPeer(withID: egoPeerID) else {
3600 // Not in circle, nothing to do
3601 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
3602 os_log("updateTrustIfNeeded: ego peer is not in model, is %@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
3603 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3604 isPreapproved: isPreapproved,
3606 memberChanges: changesPending,
3607 unknownMachineIDs: false,
3612 self.moc.performAndWait {
3613 let dynamicInfo: TPPeerDynamicInfo
3614 var stableInfo: TPPeerStableInfo?
3616 // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
3617 // and then only load the key if it has changed and we need to sign a new one. This would also
3618 // help make our detection of change immune from non-canonical serialization of dynamicInfo.
3619 dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
3621 removingPeerIDs: nil,
3622 preapprovedKeys: nil,
3623 signing: signingKeyPair,
3624 currentMachineIDs: self.onqueueCurrentMIDList())
3626 stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
3627 egoPeerID: egoPeerID,
3628 dynamicInfo: dynamicInfo,
3629 signingKeyPair: signingKeyPair)
3631 os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %@", log: tplogDebug, type: .default, error as CVarArg)
3632 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3633 isPreapproved: false,
3634 status: self.model.statusOfPeer(withID: egoPeerID),
3635 memberChanges: changesPending,
3636 unknownMachineIDs: false,
3642 os_log("updateTrustIfNeeded: produced a stableInfo: %@", log: tplogDebug, type: .default, String(describing: stableInfo))
3643 os_log("updateTrustIfNeeded: produced a dynamicInfo: %@", log: tplogDebug, type: .default, dynamicInfo)
3645 let peer = self.model.peer(withID: egoPeerID)
3646 if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
3647 dynamicInfo == peer?.dynamicInfo {
3648 os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
3649 // No change to the dynamicInfo: update the MID list now that we've reached a steady state
3651 self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
3654 os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %@", log: tplogDebug, type: .default, error as CVarArg)
3657 reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
3658 isPreapproved: false,
3659 status: self.model.statusOfPeer(withID: egoPeerID),
3660 memberChanges: changesPending,
3661 unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
3662 osVersion: peer?.stableInfo?.osVersion),
3666 // Check if we change that should trigger a notification that should trigger TLKShare updates
3667 let haveChanges = changesPending || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
3669 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
3670 os_log("updateTrustIfNeeded: attempting updateTrust for %@ with: %@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
3671 var request = UpdateTrustRequest.with {
3672 $0.changeToken = self.containerMO.changeToken ?? ""
3673 $0.peerID = egoPeerID
3674 $0.dynamicInfoAndSig = signedDynamicInfo
3676 if let stableInfo = stableInfo {
3677 request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
3679 self.cuttlefish.updateTrust(request) { response, error in
3680 os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
3681 guard let response = response, error == nil else {
3682 os_log("UpdateTrust failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
3683 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
3688 try self.persist(changes: response.changes)
3690 os_log("updateTrust failed: %@", log: tplogDebug, String(describing: error))
3695 if response.changes.more {
3696 self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
3697 changesPending: haveChanges,
3700 self.updateTrustIfNeeded(stableChanges: stableChanges,
3701 changesPending: haveChanges,
3710 private func persist(changes: Changes) throws {
3711 // This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
3712 // So, do it ourself
3713 var outsideBlockError: Error?
3715 self.moc.performAndWait {
3716 os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
3717 changes.differences.count,
3719 os_log("persist: New change token: %@", log: tplogDebug, type: .default, changes.changeToken)
3722 try self.onQueuePersist(changes: changes)
3724 outsideBlockError = error
3728 if let outsideBlockError = outsideBlockError {
3729 throw outsideBlockError
3733 // Must be on moc queue to call this.
3734 // Changes are registered in the model and stored in moc.
3735 private func onQueuePersist(changes: Changes) throws {
3736 self.containerMO.changeToken = changes.changeToken
3737 self.containerMO.moreChanges = changes.more
3739 if changes.differences.count > 0 {
3740 self.model.clearViableBottles()
3743 try changes.differences.forEach { peerDifference in
3744 if let operation = peerDifference.operation {
3746 case .add(let peer):
3747 try self.addOrUpdate(peer: peer)
3749 case .update(let peer):
3750 try self.addOrUpdate(peer: peer)
3751 // Update containerMO ego data if it has changed.
3752 if peer.peerID == self.containerMO.egoPeerID {
3753 guard let stableInfoAndSig: TPPeerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
3756 self.containerMO.egoPeerStableInfo = stableInfoAndSig.data
3757 self.containerMO.egoPeerStableInfoSig = stableInfoAndSig.sig
3760 case .remove(let peer):
3761 self.model.deletePeer(withID: peer.peerID)
3762 if let peerMO = try self.fetchPeerMO(peerID: peer.peerID) {
3763 self.moc.delete(peerMO)
3769 let signingKey = changes.recoverySigningPubKey
3770 let encryptionKey = changes.recoveryEncryptionPubKey
3772 if signingKey.count > 0 && encryptionKey.count > 0 {
3773 self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
3778 // Must be on moc queue to call this
3779 // Deletes all things that should come back from CloudKit. Keeps the egoPeer data, though.
3780 private func deleteLocalCloudKitData() throws {
3781 os_log("Deleting all CloudKit data", log: tplogDebug, type: .default)
3784 let peerRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
3785 peerRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
3786 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: peerRequest))
3788 let bottleRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Bottle")
3789 bottleRequest.predicate = NSPredicate(format: "container == %@", self.containerMO)
3790 try self.moc.execute(NSBatchDeleteRequest(fetchRequest: bottleRequest))
3792 self.containerMO.peers = nil
3793 self.containerMO.bottles = nil
3794 self.containerMO.changeToken = nil
3795 self.containerMO.moreChanges = false
3797 self.model = Container.loadModel(from: self.containerMO)
3800 os_log("Local delete failed: %@", log: tplogDebug, type: .default, error as CVarArg)
3804 os_log("Saved model with %d peers", log: tplogDebug, type: .default, self.model.allPeerIDs().count)
3807 // Must be on moc queue to call this.
3808 private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
3809 self.model.setRecoveryKeys(
3810 TPRecoveryKeyPair(signingSPKI: signingKey, encryptionSPKI: encryptionKey))
3813 // Must be on moc queue to call this.
3814 private func addOrUpdate(peer: Peer) throws {
3815 if !self.model.hasPeer(withID: peer.peerID) {
3817 guard let permanentInfo = peer.permanentInfoAndSig.toPermanentInfo(peerID: peer.peerID) else {
3818 // Ignoring bad peer
3821 let stableInfo = peer.stableInfoAndSig.toStableInfo()
3822 let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo()
3823 let vouchers = peer.vouchers.compactMap {
3824 TPVoucher(infoWith: $0.voucher, sig: $0.sig)
3826 let isEgoPeer = peer.peerID == self.containerMO.egoPeerID
3827 try self.registerPeerMO(permanentInfo: permanentInfo,
3828 stableInfo: stableInfo,
3829 dynamicInfo: dynamicInfo,
3831 isEgoPeer: isEgoPeer)
3834 // The assertion here is that every peer registered in model is also present in containerMO
3835 let peerMO = try self.fetchPeerMO(peerID: peer.peerID)!
3837 if let stableInfo = peer.stableInfoAndSig.toStableInfo() {
3838 try self.model.update(stableInfo, forPeerWithID: peer.peerID)
3839 // Pull the stableInfo back out of the model, and persist that.
3840 // The model checks signatures and clocks to prevent replay attacks.
3841 let modelPeer = self.model.peer(withID: peer.peerID)
3842 peerMO.stableInfo = modelPeer?.stableInfo?.data
3843 peerMO.stableInfoSig = modelPeer?.stableInfo?.sig
3845 if let dynamicInfo = peer.dynamicInfoAndSig.toDynamicInfo() {
3846 try self.model.update(dynamicInfo, forPeerWithID: peer.peerID)
3847 // Pull the dynamicInfo back out of the model, and persist that.
3848 // The model checks signatures and clocks to prevent replay attacks.
3849 let modelPeer = self.model.peer(withID: peer.peerID)
3850 peerMO.dynamicInfo = modelPeer?.dynamicInfo?.data
3851 peerMO.dynamicInfoSig = modelPeer?.dynamicInfo?.sig
3853 peer.vouchers.forEach {
3854 if let voucher = TPVoucher(infoWith: $0.voucher, sig: $0.sig) {
3855 self.model.register(voucher)
3856 let voucherMO = VoucherMO(context: self.moc)
3857 voucherMO.voucherInfo = voucher.data
3858 voucherMO.voucherInfoSig = voucher.sig
3859 peerMO.addToVouchers(voucherMO)
3865 // Must be on moc queue to call this.
3866 private func fetchPeerMO(peerID: String) throws -> PeerMO? {
3867 let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Peer")
3868 fetch.predicate = NSPredicate(format: "peerID == %@ && container == %@", peerID, self.containerMO)
3869 let peers = try self.moc.fetch(fetch)
3870 return peers.first as? PeerMO
3873 // Must be on moc queue to call this.
3874 private func getPolicyDoc(_ policyVersion: UInt64) throws -> TPPolicyDocument {
3875 guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
3876 throw ContainerError.unknownPolicyVersion(policyVersion)
3878 assert(policyVersion == policyDoc.policyVersion)
3879 if policyVersion == prevailingPolicyVersion {
3880 assert(policyDoc.policyHash == prevailingPolicyHash)
3885 // Must be on moc queue to call this.
3886 private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
3888 dynamicInfo: TPPeerDynamicInfo,
3889 signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
3890 func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
3891 return (nil == change) || change == existing
3893 let existingStableInfo = self.model.peer(withID: egoPeerID)?.stableInfo
3894 if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
3895 noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
3896 noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
3897 noChange(stableChanges?.policyVersion, existingStableInfo?.policyVersion) &&
3898 noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
3899 noChange(stableChanges?.recoverySigningPubKey, existingStableInfo?.recoverySigningPublicKey) &&
3900 noChange(stableChanges?.recoveryEncryptionPubKey, existingStableInfo?.recoveryEncryptionPublicKey) &&
3901 self.model.doesPeerRecoveryKeyMatchPeers(egoPeerID) {
3904 let policyHash: String?
3905 if let policyVersion = stableChanges?.policyVersion {
3906 let policyDoc = try self.getPolicyDoc(policyVersion)
3907 policyHash = policyDoc.policyHash
3912 // Determine which recovery key we'd like to be using, given our current idea of who to trust
3913 let newRecoveryKeys = self.model.bestRecoveryKey(with: dynamicInfo)
3915 return try self.model.createStableInfo(withPolicyVersion: stableChanges?.policyVersion ?? existingStableInfo?.policyVersion ?? prevailingPolicyVersion,
3916 policyHash: policyHash ?? existingStableInfo?.policyHash ?? prevailingPolicyHash,
3917 policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
3918 deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
3919 serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
3920 osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
3921 signing: signingKeyPair,
3922 recoverySigningPubKey: newRecoveryKeys?.signingSPKI ?? existingStableInfo?.recoverySigningPublicKey,
3923 recoveryEncryptionPubKey: newRecoveryKeys?.encryptionSPKI ?? existingStableInfo?.recoveryEncryptionPublicKey)
3926 private func assembleBottle(egoPeerID: String) throws -> Bottle {
3927 let bottleMoSet = containerMO.bottles as? Set<BottleMO>
3929 var bottleMOs = bottleMoSet?.filter {
3930 $0.peerID == egoPeerID
3933 if let count = bottleMOs?.count {
3935 throw ContainerError.tooManyBottlesForPeer
3936 } else if count == 0 {
3937 throw ContainerError.noBottleForPeer
3940 throw ContainerError.failedToAssembleBottle
3943 let BM: BottleMO? = (bottleMOs?.removeFirst())
3945 let bottle = try Bottle.with {
3946 $0.peerID = egoPeerID
3948 if let bottledPeerData = BM?.contents {
3949 $0.contents = bottledPeerData
3951 throw ContainerError.failedToAssembleBottle
3954 if let bottledPeerEscrowSigningSPKI = BM?.escrowedSigningSPKI {
3955 $0.escrowedSigningSpki = bottledPeerEscrowSigningSPKI
3957 throw ContainerError.failedToAssembleBottle
3960 if let bottledPeerSignatureUsingEscrowKey = BM?.signatureUsingEscrowKey {
3961 $0.signatureUsingEscrowKey = bottledPeerSignatureUsingEscrowKey
3963 throw ContainerError.failedToAssembleBottle
3966 if let bottledPeerSignatureUsingPeerKey = BM?.signatureUsingPeerKey {
3967 $0.signatureUsingPeerKey = bottledPeerSignatureUsingPeerKey
3969 throw ContainerError.failedToAssembleBottle
3972 if let bID = BM?.bottleID {
3975 throw ContainerError.failedToAssembleBottle
3982 func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
3983 self.semaphore.wait()
3984 let reply: (Error?) -> Void = {
3985 os_log("reportHealth complete %@", log: tplogTrace, type: .info, traceError($0))
3986 self.semaphore.signal()
3990 var updatedRequest = request
3992 if let egoPeerID = self.containerMO.egoPeerID {
3993 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, _ in
3994 updatedRequest.octagonEgoIdentity = (egoPeerKeys != nil)
3998 self.moc.performAndWait {
3999 self.cuttlefish.reportHealth(updatedRequest) { response, error in
4000 os_log("reportHealth(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
4001 guard error == nil else {
4010 func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
4011 self.semaphore.wait()
4012 let reply: (Error?) -> Void = {
4013 os_log("reportHealth complete %@", log: tplogTrace, type: .info, traceError($0))
4014 self.semaphore.signal()
4018 self.moc.performAndWait {
4019 self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { response, error in
4020 os_log("pushHealthInquiry(): %@, error: %@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
4021 guard error == nil else {