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