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