1 // Copyright (C) 2018 Apple Inc. All Rights Reserved.
4 import CoreDeviceAutomation
5 import CoreDeviceAutomationFrameworkFacade
7 import OctagonTestHarnessXPCServiceProtocol
9 extension CDAIOSDevice {
10 func octagonFacade(block: @escaping (AnyObject) -> Void) throws {
11 try self.withFrameworkFacade(bundlePath: "/AppleInternal/Library/Frameworks/OctagonTestHarness.framework",
12 xpcService: "com.apple.trieste.OctagonTestHarnessXPCService",
13 ofType: OctagonTestHarnessXPCServiceProtocol.self,
18 final class OctagonTests: CDTTestCase {
20 let username: String? = nil
21 let password: String? = nil
22 var signedIn: Bool = false
24 let ckksTool = "/usr/sbin/ckksctl"
25 let securityTool = "/usr/local/bin/security"
27 private func getDevice() throws -> CDAIOSDevice {
28 let common = CDAIOSCapabilities(
30 .location(country: "SE", building: "AP1"),
31 .installedFrameworkFacade(true),
32 .allowAnyCodeSignature(true)
35 if let rawPrkitUrl = ProcessInfo.processInfo.environment["PRKIT_URL"] {
36 if let prkitUrl = URL(string: rawPrkitUrl) {
37 let capabilities = common
39 let device = try self.trieste.getIOSDevice(capabilities: capabilities)
45 try device.installPRKit(at: prkitUrl)
49 throw CDTFail("PRKIT_URL was set, but can't be parsed as URL, aborting: '\(rawPrkitUrl)'")
52 let device: CDAIOSDevice
54 if !ProcessInfo.processInfo.environment.keys.contains("NO_XCODE") {
55 let capabilities = common.merging(CDAIOSCapabilities(
60 device = try self.trieste.getIOSDevice(capabilities: capabilities)
62 let targets = CDAInstalledXcodeTargetsSpec(
63 CDAInstalledXcodeTargetsSpec.Target(projectFile: "Security.xcodeproj", configuration: "Debug", scheme: "OctagonTestHarness", sdk: "iphoneos13.0.internal", roots:
64 CDAInstalledXcodeTargetsSpec.Root(product: "OctagonTestHarness.framework", installDirectory: "/AppleInternal/Library/Frameworks"),
65 CDAInstalledXcodeTargetsSpec.Root(product: "Security.framework", installDirectory: "/System/Library/Frameworks")
69 try device.installXcodeTargets(targets, rebootWhenDone: true)
71 let capabilities = common.merging(CDAIOSCapabilities(
76 device = try self.trieste.getIOSDevice(capabilities: capabilities)
82 let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["--no-confirmation", "deleteAccountsForUsername", self.username!])
83 XCTAssertEqual(listAccounts.returnCode, 0, "deleteAccountsForUsername")
84 print("accounts_tool deleteAccountsForUsername\n\(String(data: listAccounts.standardOutput, encoding: .utf8)!)\n")
92 if self.username != nil {
93 CDALog(at: .infoLevel, "Signing in to iCloud here \(self.username!)")
95 let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["listAccounts", "-v"])
96 print("accounts_tool listAccounts -v\n\(String(data: listAccounts.standardOutput, encoding: .utf8)!)\n")
98 let result = try device.executeFile(atPath: "/usr/local/bin/appleAccountSetupTool", withArguments: [self.username!, self.password!])
99 XCTAssertEqual(result.returnCode, 0, "appleAccountSetupTool failed")
100 print("appleAccountSetupTool\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
102 let enableKVS = try device.executeFile(atPath: "/bin/sh", withArguments: ["-c", "accounts_tool enableDataclass $(accounts_tool listAccountsForType com.apple.account.AppleAccount | grep identifier: | cut -f2 -d: ) com.apple.Dataclass.KeyValue"])
103 XCTAssertEqual(enableKVS.returnCode, 0, "enableKVS failed")
104 print("enableKVS\n\(String(data: enableKVS.standardOutput, encoding: .utf8)!)\n")
113 func compareCKKSZone(name zone: String, status1: NSDictionary, status2: NSDictionary) -> Bool {
115 let zone1 = status1[zone] as! NSDictionary
116 let zone2 = status2[zone] as! NSDictionary
118 let keystate1 = zone1["keystate"] as! NSString
119 let keystate2 = zone2["keystate"] as! NSString
121 XCTAssertEqual(keystate1, "ready", "keystate should be ready for zone \(zone)")
122 XCTAssertEqual(keystate2, "ready", "keystate should be ready for zone \(zone)")
124 let ckaccountstatus1 = zone1["ckaccountstatus"] as! NSString
125 let ckaccountstatus2 = zone2["ckaccountstatus"] as! NSString
127 XCTAssertEqual(ckaccountstatus1, "logged in", "ckaccountstatus should be 'logged in' for zone \(zone)")
128 XCTAssertEqual(ckaccountstatus2, "logged in", "ckaccountstatus should be 'logged in' for zone \(zone)")
130 let currentTLK1 = zone1["currentTLK"] as? NSString
131 let currentTLK2 = zone2["currentTLK"] as? NSString
133 XCTAssertEqual(currentTLK1, currentTLK2, "TLK for zone \(zone) should be the same")
138 func compareCKKSStatus(c1: NSDictionary, c2: NSDictionary) -> Bool {
140 let status1 = c1["status"] as! NSDictionary
141 let status2 = c2["status"] as! NSDictionary
143 XCTAssert(compareCKKSZone(name: "ApplePay", status1: status1, status2: status2))
144 XCTAssert(compareCKKSZone(name: "Home", status1: status1, status2: status2))
145 XCTAssert(compareCKKSZone(name: "Manatee", status1: status1, status2: status2))
146 XCTAssert(compareCKKSZone(name: "AutoUnlock", status1: status1, status2: status2))
151 func sosStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws {
152 let result = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-i"])
154 print("security sync -i\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
157 func ckksStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws -> NSDictionary {
158 let ckks = try device.executeFile(atPath: self.ckksTool, withArguments: ["status", "--json"])
160 print("ckks status\n\(String(data: ckks.standardOutput, encoding: .utf8)!)\n")
163 return try JSONSerialization.jsonObject(with: ckks.standardOutput) as! NSDictionary
166 func sosApplication(_ device: CDAIOSDevice, verbose: Bool = false) throws {
167 if self.password != nil {
169 print("submitting application\n")
171 let password = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
172 XCTAssertEqual(password.returnCode, 0, "setting password worked")
174 let application = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-e"])
175 XCTAssertEqual(application.returnCode, 0, "submissing application worked password worked")
177 try self.sosStatus(device, verbose: true)
179 print("waiting after application done\n")
184 func sosApprove(_ device: CDAIOSDevice, verbose: Bool = false) throws {
185 if self.password != nil {
187 print("approving applications\n")
189 let password = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
190 XCTAssertEqual(password.returnCode, 0, "setting password worked")
192 let approve = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-a"])
193 XCTAssertEqual(approve.returnCode, 0, "submissing application worked password worked")
195 try self.sosStatus(device, verbose: true)
197 print("waiting after approving applications\n")
202 func forceResetSOS(_ device: CDAIOSDevice, resetCKKS: Bool = false) throws {
203 if self.password != nil {
204 _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
205 _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-R"])
206 _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-C"])
207 _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
208 _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-R"])
209 _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-O"])
211 try self.sosStatus(device, verbose: true)
213 print("sleeping some to allow cdpd, cloudd and friends to catch up \n")
217 _ = try device.executeFile(atPath: self.ckksTool, withArguments: ["reset-cloudkit"])
219 print("sleeps some after ckksctl reset (should be removed)\n")
225 func testBasiciCloudSignInSignOut() throws {
226 let device = try getDevice()
228 let result = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["listAccounts", "-v"])
229 print("accounts_tool listAccounts -v\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
231 try forceResetSOS(device, resetCKKS: true)
233 _ = try self.ckksStatus(device)
236 func test2DeviceSOS() throws {
237 if self.password == nil {
238 print("this test only works with password")
242 let device1 = try getDevice()
243 let device2 = try getDevice()
245 try forceResetSOS(device1, resetCKKS: true)
246 try sosApplication(device2)
247 try sosApprove(device1, verbose: true)
248 try sosStatus(device2, verbose: true)
250 print("waiting some \(Date())")
251 Thread.sleep(until: Date().addingTimeInterval(30.0)) // CK really really slow to get us data
252 print("waiting done \(Date())")
254 let ckks1 = try ckksStatus(device1)
255 let ckks2 = try ckksStatus(device2)
257 print("comparing status \(Date())")
258 XCTAssert(self.compareCKKSStatus(c1: ckks1, c2: ckks2), "compare of CKKS status")
260 print("Done \(Date())")
263 func testOctagonReset() throws {
264 let device = try getDevice()
266 try forceResetSOS(device, resetCKKS: true)
268 CDALog(at: .infoLevel, "Obtained signed in device \(device)")
270 try device.octagonFacade { octagon in
271 CDALog(at: .infoLevel, "Obtained framework facade \(octagon)")
274 CDALog(at: .infoLevel, "Reset \(i)")
275 octagon.octagonReset("altDSID") { _, error in
276 CDTAssert(error == nil, "Octagon wasn't reset, error was \(String(describing: error))")
281 CDALog(at: .infoLevel, "Done running")