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