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