]> git.saurik.com Git - apple/security.git/blob - keychain/Trieste/OctagonTriesteTests/Tests/OctagonTriesteTests/OctagonTests.swift
9689ecaea15664390f231b2ab3f6b21e347c04b9
[apple/security.git] / keychain / Trieste / OctagonTriesteTests / Tests / OctagonTriesteTests / OctagonTests.swift
1 // Copyright (C) 2018 Apple Inc. All Rights Reserved.
2
3 import CloudDeviceTest
4 import CoreDeviceAutomation
5 import CoreDeviceAutomationFrameworkFacade
6 import Foundation
7 import OctagonTestHarnessXPCServiceProtocol
8
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,
14 block: block)
15 }
16 }
17
18 final class OctagonTests: CDTTestCase {
19
20 let username: String? = nil
21 let password: String? = nil
22 var signedIn: Bool = false
23
24 let ckksTool = "/usr/sbin/ckksctl"
25 let securityTool = "/usr/local/bin/security"
26
27 private func getDevice() throws -> CDAIOSDevice {
28 let common = CDAIOSCapabilities(
29 .buildTrain("Yukon"),
30 .location(country: "SE", building: "AP1"),
31 .installedFrameworkFacade(true),
32 .allowAnyCodeSignature(true)
33 )
34
35 if let rawPrkitUrl = ProcessInfo.processInfo.environment["PRKIT_URL"] {
36 if let prkitUrl = URL(string: rawPrkitUrl) {
37 let capabilities = common
38
39 let device = try self.trieste.getIOSDevice(capabilities: capabilities)
40
41 addTeardownBlock {
42 device.relinquish()
43 }
44
45 try device.installPRKit(at: prkitUrl)
46
47 return device
48 } else {
49 throw CDTFail("PRKIT_URL was set, but can't be parsed as URL, aborting: '\(rawPrkitUrl)'")
50 }
51 } else {
52 let device: CDAIOSDevice
53
54 if !ProcessInfo.processInfo.environment.keys.contains("NO_XCODE") {
55 let capabilities = common.merging(CDAIOSCapabilities(
56 .setup(.complete),
57 .collectSysdiagnose()
58 ))
59
60 device = try self.trieste.getIOSDevice(capabilities: capabilities)
61
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")
66 )
67 )
68
69 try device.installXcodeTargets(targets, rebootWhenDone: true)
70 } else {
71 let capabilities = common.merging(CDAIOSCapabilities(
72 .setup(.complete),
73 .collectSysdiagnose()
74 ))
75
76 device = try self.trieste.getIOSDevice(capabilities: capabilities)
77 }
78
79 addTeardownBlock {
80 if self.signedIn {
81 do {
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")
85 } catch {
86 }
87 }
88
89 device.relinquish()
90 }
91
92 if self.username != nil {
93 CDALog(at: .infoLevel, "Signing in to iCloud here \(self.username!)")
94
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")
97
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")
101
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")
105
106 self.signedIn = true
107 }
108
109 return device
110 }
111 }
112
113 func compareCKKSZone(name zone: String, status1: NSDictionary, status2: NSDictionary) -> Bool {
114
115 let zone1 = status1[zone] as! NSDictionary
116 let zone2 = status2[zone] as! NSDictionary
117
118 let keystate1 = zone1["keystate"] as! NSString
119 let keystate2 = zone2["keystate"] as! NSString
120
121 XCTAssertEqual(keystate1, "ready", "keystate should be ready for zone \(zone)")
122 XCTAssertEqual(keystate2, "ready", "keystate should be ready for zone \(zone)")
123
124 let ckaccountstatus1 = zone1["ckaccountstatus"] as! NSString
125 let ckaccountstatus2 = zone2["ckaccountstatus"] as! NSString
126
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)")
129
130 let currentTLK1 = zone1["currentTLK"] as? NSString
131 let currentTLK2 = zone2["currentTLK"] as? NSString
132
133 XCTAssertEqual(currentTLK1, currentTLK2, "TLK for zone \(zone) should be the same")
134
135 return true
136 }
137
138 func compareCKKSStatus(c1: NSDictionary, c2: NSDictionary) -> Bool {
139
140 let status1 = c1["status"] as! NSDictionary
141 let status2 = c2["status"] as! NSDictionary
142
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))
147
148 return true
149 }
150
151 func sosStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws {
152 let result = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-i"])
153 if verbose {
154 print("security sync -i\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
155 }
156 }
157 func ckksStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws -> NSDictionary {
158 let ckks = try device.executeFile(atPath: self.ckksTool, withArguments: ["status", "--json"])
159 if verbose {
160 print("ckks status\n\(String(data: ckks.standardOutput, encoding: .utf8)!)\n")
161 }
162
163 return try JSONSerialization.jsonObject(with: ckks.standardOutput) as! NSDictionary
164 }
165
166 func sosApplication(_ device: CDAIOSDevice, verbose: Bool = false) throws {
167 if self.password != nil {
168
169 print("submitting application\n")
170
171 let password = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
172 XCTAssertEqual(password.returnCode, 0, "setting password worked")
173
174 let application = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-e"])
175 XCTAssertEqual(application.returnCode, 0, "submissing application worked password worked")
176
177 try self.sosStatus(device, verbose: true)
178
179 print("waiting after application done\n")
180 sleep(4)
181 }
182 }
183
184 func sosApprove(_ device: CDAIOSDevice, verbose: Bool = false) throws {
185 if self.password != nil {
186
187 print("approving applications\n")
188
189 let password = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
190 XCTAssertEqual(password.returnCode, 0, "setting password worked")
191
192 let approve = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-a"])
193 XCTAssertEqual(approve.returnCode, 0, "submissing application worked password worked")
194
195 try self.sosStatus(device, verbose: true)
196
197 print("waiting after approving applications\n")
198 sleep(4)
199 }
200 }
201
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"])
210
211 try self.sosStatus(device, verbose: true)
212
213 print("sleeping some to allow cdpd, cloudd and friends to catch up \n")
214 sleep(4)
215
216 if resetCKKS {
217 _ = try device.executeFile(atPath: self.ckksTool, withArguments: ["reset-cloudkit"])
218
219 print("sleeps some after ckksctl reset (should be removed)\n")
220 sleep(4)
221 }
222 }
223 }
224
225 func testBasiciCloudSignInSignOut() throws {
226 let device = try getDevice()
227
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")
230
231 try forceResetSOS(device, resetCKKS: true)
232
233 _ = try self.ckksStatus(device)
234 }
235
236 func test2DeviceSOS() throws {
237 if self.password == nil {
238 print("this test only works with password")
239 return
240 }
241
242 let device1 = try getDevice()
243 let device2 = try getDevice()
244
245 try forceResetSOS(device1, resetCKKS: true)
246 try sosApplication(device2)
247 try sosApprove(device1, verbose: true)
248 try sosStatus(device2, verbose: true)
249
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())")
253
254 let ckks1 = try ckksStatus(device1)
255 let ckks2 = try ckksStatus(device2)
256
257 print("comparing status \(Date())")
258 XCTAssert(self.compareCKKSStatus(c1: ckks1, c2: ckks2), "compare of CKKS status")
259
260 print("Done \(Date())")
261 }
262
263 func testOctagonReset() throws {
264 let device = try getDevice()
265
266 try forceResetSOS(device, resetCKKS: true)
267
268 CDALog(at: .infoLevel, "Obtained signed in device \(device)")
269
270 try device.octagonFacade { octagon in
271 CDALog(at: .infoLevel, "Obtained framework facade \(octagon)")
272
273 for i in 0..<2 {
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))")
277 }
278 }
279 }
280
281 CDALog(at: .infoLevel, "Done running")
282 }
283 }