-
Notifications
You must be signed in to change notification settings - Fork 640
Expand file tree
/
Copy pathOpenHaystackTests.swift
More file actions
186 lines (145 loc) · 6.99 KB
/
OpenHaystackTests.swift
File metadata and controls
186 lines (145 loc) · 6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//
// OpenHaystack – Tracking personal Bluetooth devices via Apple's Find My network
//
// Copyright © 2021 Secure Mobile Networking Lab (SEEMOO)
// Copyright © 2021 The Open Wireless Link Project
//
// SPDX-License-Identifier: AGPL-3.0-only
//
import CryptoKit
import XCTest
@testable import OpenHaystack
class OpenHaystackTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
measure {
// Put the code you want to measure the time of here.
}
}
func testAnisetteDataFromAltStore() throws {
let manager = AnisetteDataManager.shared
let expect = self.expectation(description: "Anisette data fetched")
manager.requestAnisetteData { result in
switch result {
case .failure(let error):
XCTFail(String(describing: error))
case .success(let data):
print("Accessed anisette data \(data.description)")
}
expect.fulfill()
}
self.wait(for: [expect], timeout: 3.0)
}
func testKeyGeneration() throws {
let key = BoringSSL.generateNewPrivateKey()!
XCTAssertNotEqual(key, Data(repeating: 0, count: 28))
}
func testDerivePublicKey() throws {
let privateKey = BoringSSL.generateNewPrivateKey()!
let publicKeyBytes = BoringSSL.derivePublicKey(fromPrivateKey: privateKey)
XCTAssertNotNil(publicKeyBytes)
}
func testGetPublicKey() throws {
let accessory = try Accessory(name: "Some item")
let publicKey = try accessory.getAdvertisementKey()
XCTAssertEqual(publicKey.count, 28)
XCTAssertNotEqual(publicKey, Data(repeating: 0, count: 28))
XCTAssertNotEqual(publicKey, accessory.privateKey)
}
func testStoreAccessories() throws {
let accessory = try Accessory(name: "Test accessory")
try KeychainController.storeInKeychain(accessories: [accessory], test: true)
let fetchedAccessories = KeychainController.loadAccessoriesFromKeychain(test: true)
XCTAssertEqual(accessory, fetchedAccessories[0])
// Add an accessory
let updatedAccessories = fetchedAccessories + [try Accessory(name: "Test 2")]
try KeychainController.storeInKeychain(accessories: updatedAccessories, test: true)
let fetchedAccessories2 = KeychainController.loadAccessoriesFromKeychain(test: true)
XCTAssertEqual(updatedAccessories, fetchedAccessories2)
// Remove the accessories
try KeychainController.storeInKeychain(accessories: [], test: true)
}
func testKeyIDGeneration() throws {
// Import keys with their respective id from a plist
let plist = try Data(contentsOf: Bundle(for: Self.self).url(forResource: "sampleKeys", withExtension: "plist")!)
let devices = try PropertyListDecoder().decode([FindMyDevice].self, from: plist)
let keys = devices.first!.keys
for key in keys {
let publicKey = key.advertisedKey
var sha = SHA256()
sha.update(data: publicKey)
let digest = sha.finalize()
let hashedKey = Data(digest)
XCTAssertEqual(key.hashedKey, hashedKey)
}
}
func testECDHWithPublicKey() throws {
let receivedAccessory = try Accessory(name: "test")
let receivedPublicKey = try receivedAccessory.getActualPublicKey()
// Generate ephemeral key pair by using a second accessory
let ephAccessory = try Accessory(name: "Ephemeral Key")
let ephPrivate = ephAccessory.privateKey
let ephPublicKey = try ephAccessory.getActualPublicKey()
// Now we need a ECDH key exchange
// In the first round ephemeral key is the public key
let sharedKey = BoringSSL.deriveSharedKey(fromPrivateKey: ephPrivate, andEphemeralKey: receivedPublicKey)!
XCTAssertNotNil(sharedKey)
// Now we follow the standard key derivation used in OF
let derivedKey = DecryptReports.kdf(fromSharedSecret: sharedKey, andEphemeralKey: ephPublicKey)
// Let's encrypt some test string
let message = "This is a message that should be encrypted"
let messageData = message.data(using: .ascii)!
let encryptionKey = derivedKey.subdata(in: derivedKey.startIndex..<16)
let encryptionIV = derivedKey.subdata(in: 16..<derivedKey.endIndex)
let sealed = try AES.GCM.seal(messageData, using: SymmetricKey(data: encryptionKey), nonce: .init(data: encryptionIV))
// Now we decrypt it by performing it the other way around
// ECDH with public ephemeral and private received key
let sharedKey2 = BoringSSL.deriveSharedKey(fromPrivateKey: receivedAccessory.privateKey, andEphemeralKey: ephPublicKey)!
XCTAssertNotNil(sharedKey2)
XCTAssertEqual(sharedKey2, sharedKey)
// Decrypt to see if we get the same text
let derivedKey2 = DecryptReports.kdf(fromSharedSecret: sharedKey2, andEphemeralKey: ephPublicKey)
XCTAssertEqual(derivedKey2, derivedKey)
let decryptionKey = derivedKey2.subdata(in: derivedKey2.startIndex..<16)
let decryptionIV = derivedKey2.subdata(in: 16..<derivedKey2.endIndex)
XCTAssertEqual(decryptionIV, encryptionIV)
XCTAssertEqual(decryptionKey, encryptionKey)
let decryptedMessage = try AES.GCM.open(sealed, using: SymmetricKey(data: decryptionKey))
XCTAssertEqual(decryptedMessage, messageData)
let decryptedText = String(data: decryptedMessage, encoding: .ascii)
XCTAssertEqual(message, decryptedText)
}
func testGenerateKeyPair() {
let keyData = BoringSSL.generateNewPrivateKey()
XCTAssertNotNil(keyData)
}
// Test installation of Mail bundle plugin (skipped on macOS 13+ where Mail extensions replace bundles)
func testPluginInstallation() throws {
if #available(macOS 13, *) {
try XCTSkip("Mail bundle plugins are unsupported on macOS 13 or newer")
}
do {
let pluginManager = MailPluginManager()
// Ensure clean state
if pluginManager.isMailPluginInstalled {
try pluginManager.uninstallMailPlugin()
}
// Install bundle plugin
try pluginManager.installMailPlugin()
// Verify plugin bundle exists
XCTAssert(FileManager.default.fileExists(atPath: pluginManager.pluginURL.path))
} catch {
XCTFail(String(describing: error))
}
}
}