]> git.saurik.com Git - apple/security.git/blob - keychain/tpctl/main.swift
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / tpctl / main.swift
1 import Foundation
2 import os
3
4 let logger = Logger(subsystem: "com.apple.security.trustedpeers", category: "tpctl")
5
6 // This should definitely use the ArgumentParser library from the Utility package.
7 // However, right now that's not accessible from this code due to build system issues.
8 // Do it the hard way.
9
10 let programName = CommandLine.arguments[0]
11 var args: ArraySlice<String> = CommandLine.arguments[1...]
12
13 var context: String = OTDefaultContext
14 var container: String = "com.apple.security.keychain"
15
16 // Only used for prepare, but in the absence of a real command line library this is the easiest way to get them
17 var modelID: String?
18 var machineID: String?
19 var epoch: UInt64 = 1
20 var bottleSalt: String = ""
21
22 // Only used for join, establish and update
23 var preapprovedKeys: [Data]?
24 var deviceName: String?
25 var serialNumber: String?
26 var osVersion: String?
27 var policySecrets: [String: Data]?
28
29 enum Command {
30 case dump
31 case depart
32 case distrust(Set<String>)
33 case join(Data, Data)
34 case establish
35 case localReset
36 case prepare
37 case healthInquiry
38 case update
39 case reset
40 case validate
41 case viableBottles
42 case vouch(String, Data, Data, Data, Data)
43 case vouchWithBottle(String, Data, String)
44 case allow(Set<String>, Bool)
45 case supportApp
46 }
47
48 func printUsage() {
49 print("Usage:", (CommandLine.arguments[0] as NSString).lastPathComponent,
50 "[--container CONTAINER] [--context CONTEXT] COMMAND")
51 print()
52 print("Commands:")
53 print(" allow [--idms] MACHINEID ...")
54 print(" Set the (space-separated) list of machine IDs allowed in the account. If --idms is provided, append the IDMS trusted device list")
55 print(" dump Print the state of the world as this peer knows it")
56 print(" depart Attempt to depart the account and mark yourself as untrusted")
57 print(" distrust PEERID ... Distrust one or more peers by peer ID")
58 print(" establish Calls Cuttlefish Establish, creating a new account-wide trust arena with a single peer (previously generated with prepare")
59 print(" healthInquiry Request peers to check in with reportHealth")
60 print(" join VOUCHER VOUCHERSIG Join a circle using this (base64) voucher and voucherSig")
61 print(" local-reset Resets the local cuttlefish database, and ignores all previous information. Does not change anything off-device")
62 print(" prepare [--modelid MODELID] [--machineid MACHINEID] [--epoch EPOCH] [--bottlesalt BOTTLESALT]")
63 print(" Creates a new identity and returns its attributes. If not provided, modelid and machineid will be given some defaults (ignoring the local device)")
64 print(" supportApp Get SupportApp information from Cuttlefish")
65 print(" update Fetch new information from Cuttlefish, and perform any actions this node deems necessary")
66 print(" validate Vvalidate SOS and Octagon data structures from server side")
67 print(" viable-bottles Show bottles in preference order of server")
68 print(" vouch PEERID PERMANENTINFO PERMANENTINFOSIG STABLEINFO STABLEINFOSIG")
69 print(" Create a voucher for a new peer. permanentInfo, permanentInfoSig, stableInfo, stableInfoSig should be base64 data")
70 print(" vouchWithBottle BOTTLEID ENTROPY SALT")
71 print(" Create a voucher for the ego peer using the given bottle. entropy should be base64 data.")
72 print(" reset Resets Cuttlefish for this account")
73 print()
74 print("Options applying to `join', `establish' and `update'")
75 print(" --preapprove KEY... Sets the (space-separated base64) list of public keys that are preapproved.")
76 print(" --device-name NAME Sets the device name string.")
77 print(" --os-version VERSION Sets the OS version string.")
78 print(" --policy-version VERSION Sets the policy version.")
79 print(" --policy-secret NAME DATA Adds a name-value pair to policy secrets. DATA must be base-64.")
80 print("Options applying to `vouch' and `join'")
81 print(" --config FILE Configuration file with json data.")
82 print()
83 }
84
85 func exitUsage(_ code: Int32) -> Never {
86 printUsage()
87 exit(code)
88 }
89
90 func extractJSONData(dictionary: [String: Any], key: String) -> Data? {
91 guard let b64string = dictionary[key] as? String else {
92 return nil
93 }
94 guard let data = Data(base64Encoded: b64string) else {
95 return nil
96 }
97 return data
98 }
99
100 func jsonFromFile(filename: String) -> [String: Any] {
101 let data: Data
102 let json: [String: Any]?
103 do {
104 data = try Data(contentsOf: URL(fileURLWithPath: filename), options: .mappedIfSafe)
105 json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
106 } catch {
107 print("Error: failed to parse json file \(filename): \(error)")
108 exit(EXIT_FAILURE)
109 }
110 guard let dictionary = json else {
111 print("Error: failed to get dictionary in file \(filename)")
112 exit(EXIT_FAILURE)
113 }
114 return dictionary
115 }
116
117 var commands: [Command] = []
118 var argIterator = args.makeIterator()
119 var configurationData: [String: Any]?
120
121 while let arg = argIterator.next() {
122 switch arg {
123 case "--container":
124 let newContainer = argIterator.next()
125 guard newContainer != nil else {
126 print("Error: --container takes a value")
127 print()
128 exitUsage(1)
129 }
130 container = newContainer!
131
132 case "--context":
133 let newContext = argIterator.next()
134 guard newContext != nil else {
135 print("Error: --context takes a value")
136 print()
137 exitUsage(1)
138 }
139 context = newContext!
140
141 case "--modelid":
142 let newModelID = argIterator.next()
143 guard newModelID != nil else {
144 print("Error: --modelid takes a value")
145 print()
146 exitUsage(1)
147 }
148 modelID = newModelID!
149
150 case "--machineid":
151 let newMachineID = argIterator.next()
152 guard newMachineID != nil else {
153 print("Error: --machineid takes a value")
154 print()
155 exitUsage(1)
156 }
157 machineID = newMachineID!
158
159 case "--epoch":
160 let newEpoch = argIterator.next()
161 guard newEpoch != nil else {
162 print("Error: --epoch takes a value")
163 print()
164 exitUsage(1)
165 }
166 epoch = UInt64(newEpoch!)!
167
168 case "--bottlesalt":
169 let salt = argIterator.next()
170 guard salt != nil else {
171 print("Error: --bottlesalt takes a value")
172 print()
173 exitUsage(1)
174 }
175 bottleSalt = salt!
176
177 case "--preapprove":
178 var newPreapprovedKeys: [Data] = []
179 while let arg = argIterator.next() {
180 let data = Data(base64Encoded: arg)
181 guard let key = data else {
182 print("Error: preapproved keys must be base-64 data")
183 exitUsage(1)
184 }
185 newPreapprovedKeys.append(key)
186 }
187 preapprovedKeys = newPreapprovedKeys
188
189 case "--device-name":
190 guard let newDeviceName = argIterator.next() else {
191 print("Error: --device-name takes a string argument")
192 exitUsage(1)
193 }
194 deviceName = newDeviceName
195
196 case "--serial-number":
197 guard let newSerialNumber = argIterator.next() else {
198 print("Error: --serial-number takes a string argument")
199 exitUsage(1)
200 }
201 serialNumber = newSerialNumber
202
203 case "--os-version":
204 guard let newOsVersion = argIterator.next() else {
205 print("Error: --os-version takes a string argument")
206 exitUsage(1)
207 }
208 osVersion = newOsVersion
209
210 case "--policy-version":
211 guard let _ = UInt64(argIterator.next() ?? "") else {
212 print("Error: --policy-version takes an integer argument")
213 exitUsage(1)
214 }
215 // Option ignored for now
216
217 case "--policy-secret":
218 guard let name = argIterator.next(), let dataBase64 = argIterator.next() else {
219 print("Error: --policy-secret takes a name and data")
220 exitUsage(1)
221 }
222 guard let data = Data(base64Encoded: dataBase64) else {
223 print("Error: --policy-secret data must be base-64")
224 exitUsage(1)
225 }
226 if policySecrets == nil {
227 policySecrets = [:]
228 }
229 policySecrets![name] = data
230
231 case "--config":
232 guard let filename = argIterator.next() else {
233 print("Error: --config file argument missing")
234 exitUsage(1)
235 }
236
237 configurationData = jsonFromFile(filename: filename)
238
239 case "--help":
240 exitUsage(0)
241
242 case "dump":
243 commands.append(.dump)
244
245 case "depart":
246 commands.append(.depart)
247
248 case "distrust":
249 var peerIDs = Set<String>()
250 while let arg = argIterator.next() {
251 peerIDs.insert(arg)
252 }
253 commands.append(.distrust(peerIDs))
254
255 case "establish":
256 commands.append(.establish)
257
258 case "join":
259 let voucher: Data
260 let voucherSig: Data
261
262 if let configuration = configurationData {
263 guard let voucherData = extractJSONData(dictionary: configuration, key: "voucher") else {
264 print("Error: join needs a voucher")
265 exitUsage(EXIT_FAILURE)
266 }
267 guard let voucherSigData = extractJSONData(dictionary: configuration, key: "voucherSig") else {
268 print("Error: join needs a voucherSig")
269 exitUsage(EXIT_FAILURE)
270 }
271 voucher = voucherData
272 voucherSig = voucherSigData
273 } else {
274 guard let voucherBase64 = argIterator.next() else {
275 print("Error: join needs a voucher")
276 print()
277 exitUsage(1)
278 }
279
280 guard let voucherData = Data(base64Encoded: voucherBase64) else {
281 print("Error: voucher must be base-64 data")
282 print()
283 exitUsage(1)
284 }
285
286 guard let voucherSigBase64 = argIterator.next() else {
287 print("Error: join needs a voucherSig")
288 print()
289 exitUsage(1)
290 }
291
292 guard let voucherSigData = Data(base64Encoded: voucherSigBase64) else {
293 print("Error: voucherSig must be base-64 data")
294 print()
295 exitUsage(1)
296 }
297
298 voucher = voucherData
299 voucherSig = voucherSigData
300 }
301 commands.append(.join(voucher, voucherSig))
302
303 case "local-reset":
304 commands.append(.localReset)
305
306 case "prepare":
307 commands.append(.prepare)
308
309 case "healthInquiry":
310 commands.append(.healthInquiry)
311
312 case "reset":
313 commands.append(.reset)
314
315 case "update":
316 commands.append(.update)
317
318 case "supportApp":
319 commands.append(.supportApp)
320
321 case "validate":
322 commands.append(.validate)
323
324 case "viable-bottles":
325 commands.append(.viableBottles)
326
327 case "vouch":
328 let peerID: String
329 let permanentInfo: Data
330 let permanentInfoSig: Data
331 let stableInfo: Data
332 let stableInfoSig: Data
333
334 if let configuration = configurationData {
335 guard let peerIDString = configuration["peerID"] as? String else {
336 print("Error: vouch needs a peerID")
337 exitUsage(EXIT_FAILURE)
338 }
339
340 guard let permanentInfoData = extractJSONData(dictionary: configuration, key: "permanentInfo") else {
341 print("Error: vouch needs a permanentInfo")
342 exitUsage(EXIT_FAILURE)
343 }
344 guard let permanentInfoSigData = extractJSONData(dictionary: configuration, key: "permanentInfoSig") else {
345 print("Error: vouch needs a permanentInfoSig")
346 exitUsage(EXIT_FAILURE)
347 }
348 guard let stableInfoData = extractJSONData(dictionary: configuration, key: "stableInfo") else {
349 print("Error: vouch needs a stableInfo")
350 exitUsage(EXIT_FAILURE)
351 }
352 guard let stableInfoSigData = extractJSONData(dictionary: configuration, key: "stableInfoSig") else {
353 print("Error: vouch needs a stableInfoSig")
354 exitUsage(EXIT_FAILURE)
355 }
356
357 peerID = peerIDString
358 permanentInfo = permanentInfoData
359 permanentInfoSig = permanentInfoSigData
360 stableInfo = stableInfoData
361 stableInfoSig = stableInfoSigData
362 } else {
363 guard let peerIDString = argIterator.next() else {
364 print("Error: vouch needs a peerID")
365 print()
366 exitUsage(1)
367 }
368 guard let permanentInfoBase64 = argIterator.next() else {
369 print("Error: vouch needs a permanentInfo")
370 print()
371 exitUsage(1)
372 }
373 guard let permanentInfoSigBase64 = argIterator.next() else {
374 print("Error: vouch needs a permanentInfoSig")
375 print()
376 exitUsage(1)
377 }
378 guard let stableInfoBase64 = argIterator.next() else {
379 print("Error: vouch needs a stableInfo")
380 print()
381 exitUsage(1)
382 }
383 guard let stableInfoSigBase64 = argIterator.next() else {
384 print("Error: vouch needs a stableInfoSig")
385 print()
386 exitUsage(1)
387 }
388
389 guard let permanentInfoData = Data(base64Encoded: permanentInfoBase64) else {
390 print("Error: permanentInfo must be base-64 data")
391 print()
392 exitUsage(1)
393 }
394
395 guard let permanentInfoSigData = Data(base64Encoded: permanentInfoSigBase64) else {
396 print("Error: permanentInfoSig must be base-64 data")
397 print()
398 exitUsage(1)
399 }
400 guard let stableInfoData = Data(base64Encoded: stableInfoBase64) else {
401 print("Error: stableInfo must be base-64 data")
402 print()
403 exitUsage(1)
404 }
405
406 guard let stableInfoSigData = Data(base64Encoded: stableInfoSigBase64) else {
407 print("Error: stableInfoSig must be base-64 data")
408 print()
409 exitUsage(1)
410 }
411
412 peerID = peerIDString
413 permanentInfo = permanentInfoData
414 permanentInfoSig = permanentInfoSigData
415 stableInfo = stableInfoData
416 stableInfoSig = stableInfoSigData
417 }
418
419 commands.append(.vouch(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig))
420
421 case "vouchWithBottle":
422 guard let bottleID = argIterator.next() else {
423 print("Error: vouchWithBottle needs a bottleID")
424 print()
425 exitUsage(1)
426 }
427 guard let entropyBase64 = argIterator.next() else {
428 print("Error: vouchWithBottle needs entropy")
429 print()
430 exitUsage(1)
431 }
432 guard let salt = argIterator.next() else {
433 print("Error: vouchWithBottle needs a salt")
434 print()
435 exitUsage(1)
436 }
437
438 guard let entropy = Data(base64Encoded: entropyBase64) else {
439 print("Error: entropy must be base-64 data")
440 print()
441 exitUsage(1)
442 }
443
444 commands.append(.vouchWithBottle(bottleID, entropy, salt))
445
446 case "allow":
447 var machineIDs = Set<String>()
448 var performIDMS = false
449 while let arg = argIterator.next() {
450 if arg == "--idms" {
451 performIDMS = true
452 } else {
453 machineIDs.insert(arg)
454 }
455 }
456 commands.append(.allow(machineIDs, performIDMS))
457
458 default:
459 print("Unknown argument:", arg)
460 exitUsage(1)
461 }
462 }
463
464 if commands.isEmpty {
465 exitUsage(0)
466 }
467
468 // JSONSerialization has no idea how to handle NSData. Help it out.
469 func cleanDictionaryForJSON(_ d: [AnyHashable: Any]) -> [AnyHashable: Any] {
470 func cleanValue(_ value: Any) -> Any {
471 switch value {
472 case let subDict as [AnyHashable: Any]:
473 return cleanDictionaryForJSON(subDict)
474 case let subArray as [Any]:
475 return subArray.map(cleanValue)
476 case let data as Data:
477 return data.base64EncodedString()
478 default:
479 return value
480 }
481 }
482
483 return d.mapValues(cleanValue)
484 }
485
486 // Bring up a connection to TrustedPeersHelper
487 let connection = NSXPCConnection(serviceName: "com.apple.TrustedPeersHelper")
488
489 connection.remoteObjectInterface = TrustedPeersHelperSetupProtocol(NSXPCInterface(with: TrustedPeersHelperProtocol.self))
490 connection.resume()
491
492 let tpHelper = connection.synchronousRemoteObjectProxyWithErrorHandler { error in print("Unable to connect to TPHelper:", error) } as! TrustedPeersHelperProtocol
493
494 for command in commands {
495 switch command {
496 case .dump:
497 logger.log("dumping (\(container), \(context))")
498 tpHelper.dump(withContainer: container, context: context) { reply, error in
499 guard error == nil else {
500 print("Error dumping:", error!)
501 return
502 }
503
504 if let reply = reply {
505 do {
506 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(reply)))
507 } catch {
508 print("Error encoding JSON: \(error)")
509 }
510 } else {
511 print("Error: no results, but no error either?")
512 }
513 }
514
515 case .depart:
516 logger.log("departing (\(container), \(context))")
517 tpHelper.departByDistrustingSelf(withContainer: container, context: context) { error in
518 guard error == nil else {
519 print("Error departing:", error!)
520 return
521 }
522
523 print("Depart successful")
524 }
525
526 case .distrust(let peerIDs):
527 logger.log("distrusting \(peerIDs.description) for (\(container), \(context))")
528 tpHelper.distrustPeerIDs(withContainer: container, context: context, peerIDs: peerIDs) { error in
529 guard error == nil else {
530 print("Error distrusting:", error!)
531 return
532 }
533 print("Distrust successful")
534 }
535
536 case let .join(voucher, voucherSig):
537 logger.log("joining (\(container), \(context))")
538 tpHelper.join(withContainer: container,
539 context: context,
540 voucherData: voucher,
541 voucherSig: voucherSig,
542 ckksKeys: [],
543 tlkShares: [],
544 preapprovedKeys: preapprovedKeys ?? []) { peerID, _, _, error in
545 guard error == nil else {
546 print("Error joining:", error!)
547 return
548 }
549 print("Join successful. PeerID:", peerID!)
550 }
551
552 case .establish:
553 logger.log("establishing (\(container), \(context))")
554 tpHelper.establish(withContainer: container,
555 context: context,
556 ckksKeys: [],
557 tlkShares: [],
558 preapprovedKeys: preapprovedKeys ?? []) { peerID, _, _, error in
559 guard error == nil else {
560 print("Error establishing:", error!)
561 return
562 }
563 print("Establish successful. Peer ID:", peerID!)
564 }
565
566 case .healthInquiry:
567 logger.log("healthInquiry (\(container), \(context))")
568 tpHelper.pushHealthInquiry(withContainer: container, context: context) { error in
569 guard error == nil else {
570 print("Error healthInquiry: \(String(describing: error))")
571 return
572 }
573 print("healthInquiry successful")
574 }
575
576 case .localReset:
577 logger.log("local-reset (\(container), \(context))")
578 tpHelper.localReset(withContainer: container, context: context) { error in
579 guard error == nil else {
580 print("Error resetting:", error!)
581 return
582 }
583
584 logger.log("local-reset (\(container), \(context)): successful")
585 print("Local reset successful")
586 }
587
588 case .supportApp:
589 logger.log("supportApp (\(container), \(context))")
590
591 tpHelper.getSupportAppInfo(withContainer: container, context: context) { data, error in
592 guard error == nil else {
593 print("Error getting supportApp:", error!)
594 return
595 }
596
597 if let data = data {
598 do {
599 let string = try GetSupportAppInfoResponse(serializedData: data).jsonString()
600 print("\(string)")
601 } catch {
602 print("Error decoding protobuf: \(error)")
603 }
604 } else {
605 print("Error: no results, but no error either?")
606 }
607 }
608
609 case .prepare:
610 logger.log("preparing (\(container), \(context))")
611
612 if machineID == nil {
613 let anisetteController = AKAnisetteProvisioningController()
614
615 let anisetteData = try anisetteController.anisetteData()
616 machineID = anisetteData.machineID
617 guard machineID != nil else {
618 print("failed to get machineid from anisette data")
619 abort()
620 }
621 }
622
623 let deviceInfo = OTDeviceInformationActualAdapter()
624
625 serialNumber = serialNumber ?? deviceInfo.serialNumber()
626 guard let serialNumber = serialNumber else {
627 print("failed to get serial number")
628 abort()
629 }
630
631 tpHelper.prepare(withContainer: container,
632 context: context,
633 epoch: epoch,
634 machineID: machineID!,
635 bottleSalt: bottleSalt,
636 bottleID: UUID().uuidString,
637 modelID: modelID ?? deviceInfo.modelID(),
638 deviceName: deviceName ?? deviceInfo.deviceName(),
639 serialNumber: serialNumber,
640 osVersion: osVersion ?? deviceInfo.osVersion(),
641 policyVersion: nil,
642 policySecrets: policySecrets,
643 syncUserControllableViews: .UNKNOWN,
644 signingPrivKeyPersistentRef: nil,
645 encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, syncingPolicy, error in
646 guard error == nil else {
647 print("Error preparing:", error!)
648 return
649 }
650
651 let result = [
652 "peerID": peerID!,
653 "permanentInfo": permanentInfo!.base64EncodedString(),
654 "permanentInfoSig": permanentInfoSig!.base64EncodedString(),
655 "stableInfo": stableInfo!.base64EncodedString(),
656 "stableInfoSig": stableInfoSig!.base64EncodedString(),
657 "machineID": machineID!,
658 "views": Array(syncingPolicy?.viewList ?? Set()),
659 ] as [String: Any]
660 do {
661 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
662 } catch {
663 print("Error encoding JSON: \(error)")
664 }
665 }
666
667 case .update:
668 logger.log("updating (\(container), \(context))")
669 tpHelper.update(withContainer: container,
670 context: context,
671 deviceName: deviceName,
672 serialNumber: serialNumber,
673 osVersion: osVersion,
674 policyVersion: nil,
675 policySecrets: policySecrets,
676 syncUserControllableViews: nil) { _, _, error in
677 guard error == nil else {
678 print("Error updating:", error!)
679 return
680 }
681
682 print("Update complete")
683 }
684
685 case .reset:
686 logger.log("resetting (\(container), \(context))")
687 tpHelper.reset(withContainer: container, context: context, resetReason: .userInitiatedReset) { error in
688 guard error == nil else {
689 print("Error during reset:", error!)
690 return
691 }
692
693 print("Reset complete")
694 }
695
696 case .validate:
697 logger.log("validate (\(container), \(context))")
698 tpHelper.validatePeers(withContainer: container, context: context) { reply, error in
699 guard error == nil else {
700 print("Error validating:", error!)
701 return
702 }
703
704 if let reply = reply {
705 do {
706 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(reply)))
707 } catch {
708 print("Error encoding JSON: \(error)")
709 }
710 } else {
711 print("Error: no results, but no error either?")
712 }
713 }
714
715 case .viableBottles:
716 logger.log("viableBottles (\(container), \(context))")
717 tpHelper.fetchViableBottles(withContainer: container, context: context) { sortedBottleIDs, partialBottleIDs, error in
718 guard error == nil else {
719 print("Error fetching viable bottles:", error!)
720 return
721 }
722 var result: [String: [String]] = [:]
723 result["sortedBottleIDs"] = sortedBottleIDs
724 result["partialBottleIDs"] = partialBottleIDs
725 do {
726 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
727 } catch {
728 print("Error encoding JSON: \(error)")
729 }
730 }
731
732 case let .vouch(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig):
733 logger.log("vouching (\(container), \(context))")
734 tpHelper.vouch(withContainer: container,
735 context: context,
736 peerID: peerID,
737 permanentInfo: permanentInfo,
738 permanentInfoSig: permanentInfoSig,
739 stableInfo: stableInfo,
740 stableInfoSig: stableInfoSig,
741 ckksKeys: []
742 ) { voucher, voucherSig, error in
743 guard error == nil else {
744 print("Error during vouch:", error!)
745 return
746 }
747
748 do {
749 let result = ["voucher": voucher!.base64EncodedString(), "voucherSig": voucherSig!.base64EncodedString()]
750 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
751 } catch {
752 print("Error during processing vouch results: \(error)")
753 }
754 }
755
756 case let .vouchWithBottle(bottleID, entropy, salt):
757 logger.log("vouching with bottle (\(container), \(context))")
758 tpHelper.vouchWithBottle(withContainer: container,
759 context: context,
760 bottleID: bottleID,
761 entropy: entropy,
762 bottleSalt: salt,
763 tlkShares: []) { voucher, voucherSig, _, _, error in
764 guard error == nil else {
765 print("Error during vouchWithBottle", error!)
766 return
767 }
768 do {
769 let result = ["voucher": voucher!.base64EncodedString(), "voucherSig": voucherSig!.base64EncodedString()]
770 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
771 } catch {
772 print("Error during processing vouch results: \(error)")
773 }
774 }
775
776 case let .allow(machineIDs, performIDMS):
777 logger.log("allow-listing (\(container), \(context))")
778
779 var idmsDeviceIDs: Set<String> = Set()
780 var accountIsDemo: Bool = false
781
782 if performIDMS {
783 let store = ACAccountStore()
784 guard let account = store.aa_primaryAppleAccount() else {
785 print("Unable to fetch primary Apple account!")
786 abort()
787 }
788
789 let requestArguments = AKDeviceListRequestContext()
790 requestArguments.altDSID = account.aa_altDSID
791 requestArguments.services = [AKServiceNameiCloud]
792
793 let akManager = AKAccountManager.sharedInstance
794 let authKitAccount = akManager.authKitAccount(withAltDSID: account.aa_altDSID)
795 if let account = authKitAccount {
796 accountIsDemo = akManager.demoAccount(for: account)
797 }
798
799 guard let controller = AKAppleIDAuthenticationController() else {
800 print("Unable to create AKAppleIDAuthenticationController!")
801 abort()
802 }
803 let semaphore = DispatchSemaphore(value: 0)
804
805 controller.fetchDeviceList(with: requestArguments) { deviceList, error in
806 guard error == nil else {
807 print("Unable to fetch IDMS device list: \(error!)")
808 abort()
809 }
810 guard let deviceList = deviceList else {
811 print("IDMS returned empty device list")
812 return
813 }
814
815 idmsDeviceIDs = Set(deviceList.map { $0.machineId })
816 semaphore.signal()
817 }
818 semaphore.wait()
819 }
820 let allMachineIDs = machineIDs.union(idmsDeviceIDs)
821 print("Setting allowed machineIDs to \(allMachineIDs)")
822 tpHelper.setAllowedMachineIDsWithContainer(container, context: context, allowedMachineIDs: allMachineIDs, honorIDMSListChanges: accountIsDemo) { listChanged, error in
823 guard error == nil else {
824 print("Error during allow:", error!)
825 return
826 }
827
828 print("Allow complete, differences: \(listChanged)")
829 }
830 }
831 }