]> git.saurik.com Git - apple/security.git/blob - keychain/tpctl/main.swift
Security-59306.101.1.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 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 nil == policySecrets {
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
274 } else {
275 guard let voucherBase64 = argIterator.next() else {
276 print("Error: join needs a voucher")
277 print()
278 exitUsage(1)
279 }
280
281 guard let voucherData = Data(base64Encoded: voucherBase64) else {
282 print("Error: voucher must be base-64 data")
283 print()
284 exitUsage(1)
285 }
286
287 guard let voucherSigBase64 = argIterator.next() else {
288 print("Error: join needs a voucherSig")
289 print()
290 exitUsage(1)
291 }
292
293 guard let voucherSigData = Data(base64Encoded: voucherSigBase64) else {
294 print("Error: voucherSig must be base-64 data")
295 print()
296 exitUsage(1)
297 }
298
299 voucher = voucherData
300 voucherSig = voucherSigData
301 }
302 commands.append(.join(voucher, voucherSig))
303
304 case "local-reset":
305 commands.append(.localReset)
306
307 case "prepare":
308 commands.append(.prepare)
309
310 case "healthInquiry":
311 commands.append(.healthInquiry)
312
313 case "reset":
314 commands.append(.reset)
315
316 case "update":
317 commands.append(.update)
318
319 case "supportApp":
320 commands.append(.supportApp)
321
322 case "validate":
323 commands.append(.validate)
324
325 case "viable-bottles":
326 commands.append(.viableBottles)
327
328 case "vouch":
329 let peerID: String
330 let permanentInfo: Data
331 let permanentInfoSig: Data
332 let stableInfo: Data
333 let stableInfoSig: Data
334
335 if let configuration = configurationData {
336 guard let peerIDString = configuration["peerID"] as? String else {
337 print("Error: vouch needs a peerID")
338 exitUsage(EXIT_FAILURE)
339 }
340
341 guard let permanentInfoData = extractJSONData(dictionary: configuration, key: "permanentInfo") else {
342 print("Error: vouch needs a permanentInfo")
343 exitUsage(EXIT_FAILURE)
344 }
345 guard let permanentInfoSigData = extractJSONData(dictionary: configuration, key: "permanentInfoSig") else {
346 print("Error: vouch needs a permanentInfoSig")
347 exitUsage(EXIT_FAILURE)
348 }
349 guard let stableInfoData = extractJSONData(dictionary: configuration, key: "stableInfo") else {
350 print("Error: vouch needs a stableInfo")
351 exitUsage(EXIT_FAILURE)
352 }
353 guard let stableInfoSigData = extractJSONData(dictionary: configuration, key: "stableInfoSig") else {
354 print("Error: vouch needs a stableInfoSig")
355 exitUsage(EXIT_FAILURE)
356 }
357
358 peerID = peerIDString
359 permanentInfo = permanentInfoData
360 permanentInfoSig = permanentInfoSigData
361 stableInfo = stableInfoData
362 stableInfoSig = stableInfoSigData
363
364 } else {
365
366 guard let peerIDString = argIterator.next() else {
367 print("Error: vouch needs a peerID")
368 print()
369 exitUsage(1)
370 }
371 guard let permanentInfoBase64 = argIterator.next() else {
372 print("Error: vouch needs a permanentInfo")
373 print()
374 exitUsage(1)
375 }
376 guard let permanentInfoSigBase64 = argIterator.next() else {
377 print("Error: vouch needs a permanentInfoSig")
378 print()
379 exitUsage(1)
380 }
381 guard let stableInfoBase64 = argIterator.next() else {
382 print("Error: vouch needs a stableInfo")
383 print()
384 exitUsage(1)
385 }
386 guard let stableInfoSigBase64 = argIterator.next() else {
387 print("Error: vouch needs a stableInfoSig")
388 print()
389 exitUsage(1)
390 }
391
392 guard let permanentInfoData = Data(base64Encoded: permanentInfoBase64) else {
393 print("Error: permanentInfo must be base-64 data")
394 print()
395 exitUsage(1)
396 }
397
398 guard let permanentInfoSigData = Data(base64Encoded: permanentInfoSigBase64) else {
399 print("Error: permanentInfoSig must be base-64 data")
400 print()
401 exitUsage(1)
402 }
403 guard let stableInfoData = Data(base64Encoded: stableInfoBase64) else {
404 print("Error: stableInfo must be base-64 data")
405 print()
406 exitUsage(1)
407 }
408
409 guard let stableInfoSigData = Data(base64Encoded: stableInfoSigBase64) else {
410 print("Error: stableInfoSig must be base-64 data")
411 print()
412 exitUsage(1)
413 }
414
415 peerID = peerIDString
416 permanentInfo = permanentInfoData
417 permanentInfoSig = permanentInfoSigData
418 stableInfo = stableInfoData
419 stableInfoSig = stableInfoSigData
420 }
421
422 commands.append(.vouch(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig))
423
424 case "vouchWithBottle":
425 guard let bottleID = argIterator.next() else {
426 print("Error: vouchWithBottle needs a bottleID")
427 print()
428 exitUsage(1)
429 }
430 guard let entropyBase64 = argIterator.next() else {
431 print("Error: vouchWithBottle needs entropy")
432 print()
433 exitUsage(1)
434 }
435 guard let salt = argIterator.next() else {
436 print("Error: vouchWithBottle needs a salt")
437 print()
438 exitUsage(1)
439 }
440
441 guard let entropy = Data(base64Encoded: entropyBase64) else {
442 print("Error: entropy must be base-64 data")
443 print()
444 exitUsage(1)
445 }
446
447 commands.append(.vouchWithBottle(bottleID, entropy, salt))
448
449 case "allow":
450 var machineIDs = Set<String>()
451 var performIDMS = false
452 while let arg = argIterator.next() {
453 if arg == "--idms" {
454 performIDMS = true
455 } else {
456 machineIDs.insert(arg)
457 }
458 }
459 commands.append(.allow(machineIDs, performIDMS))
460
461 default:
462 print("Unknown argument:", arg)
463 exitUsage(1)
464 }
465 }
466
467 if commands.isEmpty {
468 exitUsage(0)
469 }
470
471 // JSONSerialization has no idea how to handle NSData. Help it out.
472 func cleanDictionaryForJSON(_ d: [AnyHashable: Any]) -> [AnyHashable: Any] {
473 func cleanValue(_ value: Any) -> Any {
474 switch value {
475 case let subDict as [AnyHashable: Any]:
476 return cleanDictionaryForJSON(subDict)
477 case let subArray as [Any]:
478 return subArray.map(cleanValue)
479 case let data as Data:
480 return data.base64EncodedString()
481 default:
482 return value
483 }
484 }
485
486 return d.mapValues(cleanValue)
487 }
488
489 // Bring up a connection to TrustedPeersHelper
490 let connection = NSXPCConnection(serviceName: "com.apple.TrustedPeersHelper")
491 connection.remoteObjectInterface = TrustedPeersHelperSetupProtocol(NSXPCInterface(with: TrustedPeersHelperProtocol.self))
492 connection.resume()
493 let tpHelper = connection.synchronousRemoteObjectProxyWithErrorHandler { error in print("Unable to connect to TPHelper:", error) } as! TrustedPeersHelperProtocol
494
495 for command in commands {
496 switch command {
497 case .dump:
498 os_log("dumping (%@, %@)", log: tplogDebug, type: .default, container, context)
499 tpHelper.dump(withContainer: container, context: context) { reply, error in
500 guard error == nil else {
501 print("Error dumping:", error!)
502 return
503 }
504
505 if let reply = reply {
506 do {
507 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(reply)))
508 } catch {
509 print("Error encoding JSON: \(error)")
510 }
511 } else {
512 print("Error: no results, but no error either?")
513 }
514 }
515
516 case .depart:
517 os_log("departing (%@, %@)", log: tplogDebug, type: .default, container, context)
518 tpHelper.departByDistrustingSelf(withContainer: container, context: context) { error in
519 guard error == nil else {
520 print("Error departing:", error!)
521 return
522 }
523
524 print("Depart successful")
525 }
526
527 case .distrust(let peerIDs):
528 os_log("distrusting %@ for (%@, %@)", log: tplogDebug, type: .default, peerIDs, container, context)
529 tpHelper.distrustPeerIDs(withContainer: container, context: context, peerIDs: peerIDs) { error in
530 guard error == nil else {
531 print("Error distrusting:", error!)
532 return
533 }
534 print("Distrust successful")
535 }
536
537 case .join(let voucher, let voucherSig):
538 os_log("joining (%@, %@)", log: tplogDebug, type: .default, container, context)
539 tpHelper.join(withContainer: container,
540 context: context,
541 voucherData: voucher,
542 voucherSig: voucherSig,
543 ckksKeys: [],
544 tlkShares: [],
545 preapprovedKeys: preapprovedKeys ?? []) { peerID, _, _, _, error in
546 guard error == nil else {
547 print("Error joining:", error!)
548 return
549 }
550 print("Join successful. PeerID:", peerID!)
551 }
552
553 case .establish:
554 os_log("establishing (%@, %@)", log: tplogDebug, type: .default, container, context)
555 tpHelper.establish(withContainer: container,
556 context: context,
557 ckksKeys: [],
558 tlkShares: [],
559 preapprovedKeys: preapprovedKeys ?? []) { peerID, _, error in
560 guard error == nil else {
561 print("Error establishing:", error!)
562 return
563 }
564 print("Establish successful. Peer ID:", peerID!)
565 }
566
567 case .healthInquiry:
568 os_log("healthInquiry (%@, %@)", log: tplogDebug, type: .default, container, context)
569 tpHelper.pushHealthInquiry(withContainer: container, context: context) { error in
570 guard error == nil else {
571 print("Error healthInquiry: \(String(describing: error))")
572 return
573 }
574 print("healthInquiry successful")
575 }
576
577 case .localReset:
578 os_log("local-reset (%@, %@)", log: tplogDebug, type: .default, container, context)
579 tpHelper.localReset(withContainer: container, context: context) { error in
580 guard error == nil else {
581 print("Error resetting:", error!)
582 return
583 }
584
585 os_log("local-reset (%@, %@): successful", log: tplogDebug, type: .default, container, context)
586 print("Local reset successful")
587 }
588
589 case .supportApp:
590 os_log("supportApp (%@, %@)", log: tplogDebug, type: .default, container, context)
591
592 tpHelper.getSupportAppInfo(withContainer: container, context: context) { data, error in
593 guard error == nil else {
594 print("Error getting supportApp:", error!)
595 return
596 }
597
598 if let data = data {
599 do {
600 let string = try GetSupportAppInfoResponse(serializedData: data).jsonString()
601 print("\(string)")
602 } catch {
603 print("Error decoding protobuf: \(error)")
604 }
605 } else {
606 print("Error: no results, but no error either?")
607 }
608 }
609
610 case .prepare:
611 os_log("preparing (%@, %@)", log: tplogDebug, type: .default, container, context)
612
613 if machineID == nil {
614 let anisetteController = AKAnisetteProvisioningController()
615
616 let anisetteData = try anisetteController.anisetteData()
617 machineID = anisetteData.machineID
618 guard machineID != nil else {
619 print("failed to get machineid from anisette data")
620 abort()
621 }
622 }
623
624 let deviceInfo = OTDeviceInformationActualAdapter()
625
626 tpHelper.prepare(withContainer: container,
627 context: context,
628 epoch: epoch,
629 machineID: machineID!,
630 bottleSalt: bottleSalt,
631 bottleID: UUID().uuidString,
632 modelID: modelID ?? deviceInfo.modelID(),
633 deviceName: deviceName ?? deviceInfo.deviceName(),
634 serialNumber: serialNumber ?? deviceInfo.serialNumber(),
635 osVersion: osVersion ?? deviceInfo.osVersion(),
636 policyVersion: nil,
637 policySecrets: policySecrets,
638 signingPrivKeyPersistentRef: nil,
639 encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, _, error in
640 guard error == nil else {
641 print("Error preparing:", error!)
642 return
643 }
644
645 let result = [
646 "peerID": peerID!,
647 "permanentInfo": permanentInfo!.base64EncodedString(),
648 "permanentInfoSig": permanentInfoSig!.base64EncodedString(),
649 "stableInfo": stableInfo!.base64EncodedString(),
650 "stableInfoSig": stableInfoSig!.base64EncodedString(),
651 "machineID": machineID!,
652 "views": Array(views ?? Set()),
653 ] as [String: Any]
654 do {
655 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
656 } catch {
657 print("Error encoding JSON: \(error)")
658 }
659 }
660
661 case .update:
662 os_log("updating (%@, %@)", log: tplogDebug, type: .default, container, context)
663 tpHelper.update(withContainer: container,
664 context: context,
665 deviceName: deviceName,
666 serialNumber: serialNumber,
667 osVersion: osVersion,
668 policyVersion: nil,
669 policySecrets: policySecrets) { _, error in
670 guard error == nil else {
671 print("Error updating:", error!)
672 return
673 }
674
675 print("Update complete")
676 }
677
678 case .reset:
679 os_log("resetting (%@, %@)", log: tplogDebug, type: .default, container, context)
680 tpHelper.reset(withContainer: container, context: context, resetReason: .userInitiatedReset) { error in
681 guard error == nil else {
682 print("Error during reset:", error!)
683 return
684 }
685
686 print("Reset complete")
687 }
688
689 case .validate:
690 os_log("validate (%@, %@)", log: tplogDebug, type: .default, container, context)
691 tpHelper.validatePeers(withContainer: container, context: context) { reply, error in
692 guard error == nil else {
693 print("Error validating:", error!)
694 return
695 }
696
697 if let reply = reply {
698 do {
699 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(reply)))
700 } catch {
701 print("Error encoding JSON: \(error)")
702 }
703 } else {
704 print("Error: no results, but no error either?")
705 }
706 }
707
708 case .viableBottles:
709 os_log("viableBottles (%@, %@)", log: tplogDebug, type: .default, container, context)
710 tpHelper.fetchViableBottles(withContainer: container, context: context) { sortedBottleIDs, partialBottleIDs, error in
711 guard error == nil else {
712 print("Error fetching viable bottles:", error!)
713 return
714 }
715 var result: [String: [String]] = [:]
716 result["sortedBottleIDs"] = sortedBottleIDs
717 result["partialBottleIDs"] = partialBottleIDs
718 do {
719 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
720 } catch {
721 print("Error encoding JSON: \(error)")
722 }
723 }
724
725 case .vouch(let peerID, let permanentInfo, let permanentInfoSig, let stableInfo, let stableInfoSig):
726 os_log("vouching (%@, %@)", log: tplogDebug, type: .default, container, context)
727 tpHelper.vouch(withContainer: container,
728 context: context,
729 peerID: peerID,
730 permanentInfo: permanentInfo,
731 permanentInfoSig: permanentInfoSig,
732 stableInfo: stableInfo,
733 stableInfoSig: stableInfoSig,
734 ckksKeys: []
735 ) { voucher, voucherSig, error in
736 guard error == nil else {
737 print("Error during vouch:", error!)
738 return
739 }
740
741 do {
742 let result = ["voucher": voucher!.base64EncodedString(), "voucherSig": voucherSig!.base64EncodedString()]
743 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
744 } catch {
745 print("Error during processing vouch results: \(error)")
746 }
747 }
748
749 case .vouchWithBottle(let bottleID, let entropy, let salt):
750 os_log("vouching with bottle (%@, %@)", log: tplogDebug, type: .default, container, context)
751 tpHelper.vouchWithBottle(withContainer: container,
752 context: context,
753 bottleID: bottleID,
754 entropy: entropy,
755 bottleSalt: salt,
756 tlkShares: []) { voucher, voucherSig, _, _, error in
757 guard error == nil else {
758 print("Error during vouchWithBottle", error!)
759 return
760 }
761 do {
762 let result = ["voucher": voucher!.base64EncodedString(), "voucherSig": voucherSig!.base64EncodedString()]
763 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
764 } catch {
765 print("Error during processing vouch results: \(error)")
766 }
767 }
768
769 case .allow(let machineIDs, let performIDMS):
770 os_log("allow-listing (%@, %@)", log: tplogDebug, type: .default, container, context)
771
772 var idmsDeviceIDs: Set<String> = Set()
773 var accountIsDemo: Bool = false
774
775 if performIDMS {
776 let store = ACAccountStore()
777 guard let account = store.aa_primaryAppleAccount() else {
778 print("Unable to fetch primary Apple account!")
779 abort()
780 }
781
782 let requestArguments = AKDeviceListRequestContext()
783 requestArguments.altDSID = account.aa_altDSID
784 requestArguments.services = [AKServiceNameiCloud]
785
786 let akManager = AKAccountManager.sharedInstance
787 let authKitAccount = akManager.authKitAccount(withAltDSID: account.aa_altDSID)
788 if let account = authKitAccount {
789 accountIsDemo = akManager.demoAccount(for: account)
790 }
791
792 guard let controller = AKAppleIDAuthenticationController() else {
793 print("Unable to create AKAppleIDAuthenticationController!")
794 abort()
795 }
796 let semaphore = DispatchSemaphore(value: 0)
797
798 controller.fetchDeviceList(with: requestArguments) { deviceList, error in
799 guard error == nil else {
800 print("Unable to fetch IDMS device list: \(error!)")
801 abort()
802 }
803 guard let deviceList = deviceList else {
804 print("IDMS returned empty device list")
805 return
806 }
807
808 idmsDeviceIDs = Set(deviceList.map { $0.machineId })
809 semaphore.signal()
810 }
811 semaphore.wait()
812 }
813 let allMachineIDs = machineIDs.union(idmsDeviceIDs)
814 print("Setting allowed machineIDs to \(allMachineIDs)")
815 tpHelper.setAllowedMachineIDsWithContainer(container, context: context, allowedMachineIDs: allMachineIDs, honorIDMSListChanges: accountIsDemo) { listChanged, error in
816 guard error == nil else {
817 print("Error during allow:", error!)
818 return
819 }
820
821 print("Allow complete, differences: \(listChanged)")
822 }
823 }
824 }