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