mirror of
https://github.com/seemoo-lab/openhaystack.git
synced 2026-08-23 21:46:15 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,239 @@
|
||||
// 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 XCTest
|
||||
import CryptoKit
|
||||
@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 testMicrobitDeploy() throws {
|
||||
let urls = try MicrobitController.findMicrobits()
|
||||
|
||||
if let mBitURL = urls.first {
|
||||
let firmware = try Data(contentsOf: Bundle(for: Self.self).url(forResource: "sample", withExtension: "bin")!)
|
||||
try MicrobitController.deployToMicrobit(mBitURL, firmwareFile: firmware)
|
||||
}
|
||||
}
|
||||
|
||||
func testBinaryPatching() throws {
|
||||
// Patching sample.bin should fail
|
||||
do {
|
||||
let firmware = try Data(contentsOf: Bundle(for: Self.self).url(forResource: "sample", withExtension: "bin")!)
|
||||
let pattern = Data([0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x1])
|
||||
let key = Data([1, 1, 1, 1, 1, 1, 1, 1])
|
||||
_ = try MicrobitController.patchFirmware(firmware, pattern: pattern, with: key)
|
||||
XCTFail("Should thrown an erorr before")
|
||||
} catch PatchingError.patternNotFound {
|
||||
// This should be thrown
|
||||
} catch {
|
||||
XCTFail("Unexpected error")
|
||||
}
|
||||
|
||||
// Patching the sample should be successful
|
||||
do {
|
||||
let firmware = try Data(contentsOf: Bundle(for: Self.self).url(forResource: "pattern_sample", withExtension: "bin")!)
|
||||
let pattern = Data([0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xcc])
|
||||
let key = Data([1, 1, 1, 1, 1, 1, 1, 1])
|
||||
_ = try MicrobitController.patchFirmware(firmware, pattern: pattern, with: key)
|
||||
} catch {
|
||||
XCTFail("Unexpected error \(String(describing: error))")
|
||||
}
|
||||
|
||||
// Patching key too short
|
||||
|
||||
// Patching the sample should be successful
|
||||
do {
|
||||
let firmware = try Data(contentsOf: Bundle(for: Self.self).url(forResource: "pattern_sample", withExtension: "bin")!)
|
||||
let pattern = Data([0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xcc])
|
||||
let key = Data([1, 1, 1, 1, 1, 1, 1])
|
||||
_ = try MicrobitController.patchFirmware(firmware, pattern: pattern, with: key)
|
||||
} catch PatchingError.inequalLength {
|
||||
|
||||
} catch {
|
||||
XCTFail("Unexpected error \(String(describing: error))")
|
||||
}
|
||||
|
||||
// Testing with the actual firmware
|
||||
do {
|
||||
let firmware = try Data(contentsOf: Bundle(for: Self.self).url(forResource: "offline-finding", withExtension: "bin")!)
|
||||
let pattern = "OFFLINEFINDINGPUBLICKEYHERE!".data(using: .ascii)!
|
||||
let key = Data(repeating: 0xaa, count: 28)
|
||||
_ = try MicrobitController.patchFirmware(firmware, pattern: pattern, with: key)
|
||||
} catch PatchingError.inequalLength {
|
||||
|
||||
} catch {
|
||||
XCTFail("Unexpected error \(String(describing: error))")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func testPluginInstallation() {
|
||||
do {
|
||||
let pluginManager = MailPluginManager()
|
||||
if pluginManager.isMailPluginInstalled {
|
||||
try pluginManager.uninstallMailPlugin()
|
||||
}
|
||||
try pluginManager.installMailPlugin()
|
||||
|
||||
XCTAssert(FileManager.default.fileExists(atPath: pluginManager.pluginURL.path))
|
||||
|
||||
} catch {
|
||||
XCTFail(String(describing: error))
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user