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