15 Commits

Author SHA1 Message Date
Alexander Heinrich
78fba7391c Checking if the Mail plug-in is installed in the correct version. Otherwise the new mail plug-in will be installed 2021-08-06 11:46:56 +02:00
Alexander Heinrich
aa7c0a50af Updating workflows to macOS 11 2021-08-06 11:23:47 +02:00
Alexander Heinrich
48ceb9550c Small icon changes 2021-08-06 11:19:19 +02:00
Alexander Heinrich
6105a9454a Updating preview for better control of Screenshots 2021-08-06 11:19:19 +02:00
VladutLP
71fb26da56 Added a bunch of ID's into the plist for Mail app version 14 2021-08-06 11:16:10 +02:00
Milan Stute
c7a15fe0e4 Add WiSec demo 2021-06-02 14:09:57 +02:00
Alexander Heinrich
ffc5170ea4 Added a fix for the cropped rows on macOS 11.3
This is clearly a SwiftUI bug and it has been reported with FB9092071
2021-04-29 11:16:01 +02:00
Alexander Heinrich
f73c1ac636 Fixing memory leaks in ReportsFetcher 2021-04-29 11:08:41 +02:00
Alexander Heinrich
5dc6158da7 Fixing leaks in boring ssl usage 2021-04-29 11:08:41 +02:00
Alexander Heinrich
ba174196c0 Calling the completion handler in the case of a nil self 2021-04-29 11:08:41 +02:00
Tomas Harkema
c618aab843 make it a todo 2021-04-29 11:08:41 +02:00
Tomas Harkema
f8fb99cc41 burn some leaks 2021-04-29 11:08:41 +02:00
Frank Hessel
9f41994380 ESP32 Firmware: Consider Port and De-Duplicate Flashing Script 2021-04-29 09:05:31 +02:00
Sascha Mowtschan
b5a577ec4e Add "cleanup" to the deployment script #44 (enhancement) 2021-04-19 09:31:37 +02:00
Alexander Heinrich
b513d47ddc Updated Readme with info for missing Manage Plug-Ins button 2021-04-15 09:15:33 +02:00
23 changed files with 213 additions and 183 deletions

View File

@@ -18,7 +18,7 @@ defaults:
jobs:
format-swift:
runs-on: macos-latest
runs-on: macos-11
steps:
- name: "Checkout code"
uses: actions/checkout@v2

View File

@@ -16,7 +16,7 @@ defaults:
jobs:
lint-swiftlint:
runs-on: macos-latest
runs-on: macos-11
steps:
- name: "Checkout code"
uses: actions/checkout@v2

View File

@@ -16,7 +16,7 @@ defaults:
jobs:
build-firmware:
runs-on: macos-latest
runs-on: macos-11
steps:
- uses: actions/checkout@v2

View File

@@ -30,7 +30,7 @@ jobs:
build-and-release:
name: "Create release on GitHub"
runs-on: macos-latest
runs-on: macos-11
env:
APP: OpenHaystack
PROJECT_DIR: OpenHaystack

3
Firmware/ESP32/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"idf.port": "/dev/cu.usbserial-0001"
}

View File

@@ -1,5 +1,10 @@
#!/bin/bash
cleanup() {
echo "cleanup ..."
rm "$KEYFILE"
}
# Directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
@@ -127,13 +132,13 @@ fi
# Call esptool.py. Errors from here on are critical
set -e
trap cleanup INT TERM EXIT
# Clear NVM
esptool.py --after no_reset \
esptool.py --after no_reset --port "$PORT" \
erase_region 0x9000 0x5000
esptool.py --before no_reset --baud $BAUDRATE \
esptool.py --before no_reset --baud $BAUDRATE --port "$PORT" \
write_flash 0x1000 "$SCRIPT_DIR/build/bootloader/bootloader.bin" \
0x8000 "$SCRIPT_DIR/build/partition_table/partition-table.bin" \
0xe000 "$KEYFILE" \
0x10000 "$SCRIPT_DIR/build/openhaystack.bin"
rm "$KEYFILE"

View File

@@ -52,7 +52,11 @@
BIO_free(bio);
}
NSLog(@"Shared key: %@", [sharedKey base64EncodedStringWithOptions:0]);
// NSLog(@"Shared key: %@", [sharedKey base64EncodedStringWithOptions:0]);
//Free
EC_KEY_free(key);
EC_GROUP_free(curve);
EC_POINT_free(publicKey);
return sharedKey;
}
@@ -90,26 +94,32 @@
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
// Read in the private key data
BIGNUM *privateKeyNum = BN_bin2bn(privateKeyData.bytes, privateKeyData.length, nil);
int res = EC_POINT_mul(group, point, privateKeyNum, nil, nil, ctx);
if (res != 1) {
NSLog(@"Failed");
return nil;
}
res = EC_KEY_set_public_key(key, point);
EC_POINT_free(point);
if (res != 1) {
NSLog(@"Failed");
return nil;
}
privateKeyNum = BN_bin2bn(privateKeyData.bytes, privateKeyData.length, nil);
EC_KEY_set_private_key(key, privateKeyNum);
BN_free(privateKeyNum);
// Free the big numbers
// Free
BN_CTX_free(ctx);
return key;
}
@@ -126,6 +136,10 @@
size_t size = EC_POINT_point2oct(curve, publicKey, POINT_CONVERSION_COMPRESSED, publicKeyBytes.mutableBytes, keySize, NULL);
//Free
EC_KEY_free(key);
EC_GROUP_free(curve);
if (size == 0) {
return nil;
}
@@ -146,6 +160,7 @@
size_t size = BN_bn2bin(privateKey, privateKeyBytes.mutableBytes);
EC_KEY_free(key);
if (size == 0) {
return nil;
}

View File

@@ -32,7 +32,11 @@ class FindMyController: ObservableObject {
self.devices = devices
// Decrypt the reports with the imported keys
DispatchQueue.global(qos: .background).async {
DispatchQueue.global(qos: .background).async { [weak self] in
guard let self = self else {
completion()
return
}
var d = self.devices
// Add the reports to the according device by finding the right key for the report
@@ -57,8 +61,8 @@ class FindMyController: ObservableObject {
}
// Decrypt the reports
self.decryptReports {
self.exportDevices()
self.decryptReports { [weak self] in
self?.exportDevices()
DispatchQueue.main.async {
completion()
}
@@ -108,7 +112,11 @@ class FindMyController: ObservableObject {
func fetchReports(with searchPartyToken: Data, completion: @escaping (Error?) -> Void) {
DispatchQueue.global(qos: .background).async {
DispatchQueue.global(qos: .background).async { [weak self] in
guard let self = self else {
completion(FindMyErrors.objectReleased)
return
}
let fetchReportGroup = DispatchGroup()
let fetcher = ReportsFetcher()
@@ -166,7 +174,11 @@ class FindMyController: ObservableObject {
}
#endif
DispatchQueue.main.async {
DispatchQueue.main.async { [weak self] in
guard let self = self else {
completion(FindMyErrors.objectReleased)
return
}
self.devices = devices
self.decryptReports {
@@ -228,4 +240,5 @@ class FindMyController: ObservableObject {
enum FindMyErrors: Error {
case decodingPlistFailed(message: String)
case objectReleased
}

View File

@@ -30,12 +30,12 @@ class AccessoryController: ObservableObject {
}
func initAccessoryObserver() {
self.selfObserver = self.objectWillChange.sink { _ in
self.selfObserver = self.objectWillChange.sink { [weak self] _ in
// objectWillChange is called before the values are actually changed,
// so we dispatch the call to save()
DispatchQueue.main.async {
self.initObserver()
try? self.save()
DispatchQueue.main.async { [weak self] in
self?.initObserver()
try? self?.save()
}
}
}
@@ -45,7 +45,7 @@ class AccessoryController: ObservableObject {
$0.cancel()
})
self.accessories.forEach({
let c = $0.objectWillChange.sink(receiveValue: { self.objectWillChange.send() })
let c = $0.objectWillChange.sink(receiveValue: { [weak self] in self?.objectWillChange.send() })
// Important: You have to keep the returned value allocated,
// otherwise the sink subscription gets cancelled
self.listElementsObserver.append(c)
@@ -160,7 +160,11 @@ class AccessoryController: ObservableObject {
///
/// - Parameter completion: called when the reports have been succesfully downloaded or the request has failed
func downloadLocationReports(completion: @escaping (Result<Void, OpenHaystackMainView.AlertType>) -> Void) {
AnisetteDataManager.shared.requestAnisetteData { result in
AnisetteDataManager.shared.requestAnisetteData { [weak self] result in
guard let self = self else {
completion(.failure(.noReportsFound))
return
}
switch result {
case .failure(_):
completion(.failure(.activatePlugin))
@@ -173,7 +177,7 @@ class AccessoryController: ObservableObject {
return
}
self.findMyController.fetchReports(for: self.accessories, with: token) { result in
self.findMyController.fetchReports(for: self.accessories, with: token) { [weak self] result in
switch result {
case .failure(let error):
os_log(.error, "Downloading reports failed %@", error.localizedDescription)
@@ -183,7 +187,7 @@ class AccessoryController: ObservableObject {
if reports.isEmpty {
completion(.failure(.noReportsFound))
} else {
self.updateWithDecryptedReports(devices: devices)
self?.updateWithDecryptedReports(devices: devices)
completion(.success(()))
}
}

View File

@@ -28,8 +28,8 @@ class AccessoryNearbyMonitor: BluetoothAccessoryDelegate {
}
func initTimer() {
self.cleanup = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.removeNearbyAccessories()
self.cleanup = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.removeNearbyAccessories()
}
}

View File

@@ -30,8 +30,14 @@ extension FileManager {
if isDir.boolValue == true {
try self.copyFolder(from: fileURL, to: to.appendingPathComponent(file))
} else {
// Copy file
try FileManager.default.copyItem(at: fileURL, to: to.appendingPathComponent(file))
do {
// Copy file
try FileManager.default.copyItem(at: fileURL, to: to.appendingPathComponent(file))
} catch {
if fileURL.lastPathComponent != "CodeResources" {
throw error
}
}
}
}
}

View File

@@ -1,139 +0,0 @@
#!/bin/bash
# Directory of this script
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# Defaults: Directory for the virtual environment
VENV_DIR="$SCRIPT_DIR/venv"
# Defaults: Serial port to access the ESP32
PORT=/dev/ttyS0
# Defaults: Fast baud rate
BAUDRATE=921600
# Parameter parsing
while [[ $# -gt 0 ]]; do
KEY="$1"
case "$KEY" in
-p|--port)
PORT="$2"
shift
shift
;;
-s|--slow)
BAUDRATE=115200
shift
;;
-v|--venvdir)
VENV_DIR="$2"
shift
shift
;;
-h|--help)
echo "flash_esp32.sh - Flash the OpenHaystack firmware onto an ESP32 module"
echo ""
echo " This script will create a virtual environment for the required tools."
echo ""
echo "Call: flash_esp32.sh [-p <port>] [-v <dir>] [-s] PUBKEY"
echo ""
echo "Required Arguments:"
echo " PUBKEY"
echo " The base64-encoded public key"
echo ""
echo "Optional Arguments:"
echo " -h, --help"
echo " Show this message and exit."
echo " -p, --port <port>"
echo " Specify the serial interface to which the device is connected."
echo " -s, --slow"
echo " Use 115200 instead of 921600 baud when flashing."
echo " Might be required for long/bad USB cables or slow USB-to-Serial converters."
echo " -v, --venvdir <dir>"
echo " Select Python virtual environment with esptool installed."
echo " If the directory does not exist, it will be created."
exit 1
;;
*)
if [[ -z "$PUBKEY" ]]; then
PUBKEY="$1"
shift
else
echo "Got unexpected parameter $1"
exit 1
fi
;;
esac
done
# Sanity check: Pubkey exists
if [[ -z "$PUBKEY" ]]; then
echo "Missing public key, call with --help for usage"
exit 1
fi
# Sanity check: Port
if [[ ! -e "$PORT" ]]; then
echo "$PORT does not exist, please specify a valid serial interface with the -p argument"
exit 1
fi
# Setup the virtual environment
if [[ ! -d "$VENV_DIR" ]]; then
# Create the virtual environment
PYTHON="$(which python3)"
if [[ -z "$PYTHON" ]]; then
PYTHON="$(which python)"
fi
if [[ -z "$PYTHON" ]]; then
echo "Could not find a Python installation, please install Python 3."
exit 1
fi
if ! ($PYTHON -V 2>&1 | grep "Python 3" > /dev/null); then
echo "Executing \"$PYTHON\" does not run Python 3, please make sure that python3 or python on your PATH points to Python 3"
exit 1
fi
if ! ($PYTHON -c "import venv" &> /dev/null); then
echo "Python 3 module \"venv\" was not found."
exit 1
fi
$PYTHON -m venv "$VENV_DIR"
if [[ $? != 0 ]]; then
echo "Creating the virtual environment in $VENV_DIR failed."
exit 1
fi
source "$VENV_DIR/bin/activate"
pip install --upgrade pip
pip install esptool
if [[ $? != 0 ]]; then
echo "Could not install Python 3 module esptool in $VENV_DIR";
exit 1
fi
else
source "$VENV_DIR/bin/activate"
fi
# Prepare the key
KEYFILE="$SCRIPT_DIR/tmp.key"
if [[ -f "$KEYFILE" ]]; then
echo "$KEYFILE already exists, stopping here not to override files..."
exit 1
fi
echo "$PUBKEY" | python3 -m base64 -d - > "$KEYFILE"
if [[ $? != 0 ]]; then
echo "Could not parse the public key. Please provide valid base64 input"
exit 1
fi
# Call esptool.py. Errors from here on are critical
set -e
# Clear NVM
esptool.py --after no_reset \
erase_region 0x9000 0x5000
esptool.py --before no_reset --baud $BAUDRATE \
write_flash 0x1000 "$SCRIPT_DIR/build/bootloader/bootloader.bin" \
0x8000 "$SCRIPT_DIR/build/partition_table/partition-table.bin" \
0xe000 "$KEYFILE" \
0x10000 "$SCRIPT_DIR/build/openhaystack.bin"
rm "$KEYFILE"

View File

@@ -20,8 +20,36 @@ struct MailPluginManager {
let pluginURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/Mail/Bundles").appendingPathComponent(mailBundleName + ".mailbundle")
let localPluginURL = Bundle.main.url(forResource: mailBundleName, withExtension: "mailbundle")!
var isMailPluginInstalled: Bool {
return FileManager.default.fileExists(atPath: pluginURL.path)
//Check if the plug-in is compatible by comparing the IDs
guard FileManager.default.fileExists(atPath: pluginURL.path) else {
return false
}
let infoPlistURL = pluginURL.appendingPathComponent("Contents/Info.plist")
let localInfoPlistURL = localPluginURL.appendingPathComponent("Contents/Info.plist")
guard let infoPlistData = try? Data(contentsOf: infoPlistURL),
let infoPlistDict = try? PropertyListSerialization.propertyList(from: infoPlistData, options: [], format: nil) as? [String: AnyHashable],
let localInfoPlistData = try? Data(contentsOf: localInfoPlistURL),
let localInfoPlistDict = try? PropertyListSerialization.propertyList(from: localInfoPlistData, options: [], format: nil) as? [String: AnyHashable]
else { return false }
//Compare the supported plug-ins
let uuidEntries = localInfoPlistDict.keys.filter({ $0.contains("PluginCompatibilityUUIDs") })
for uuidEntry in uuidEntries {
guard let localEntry = localInfoPlistDict[uuidEntry] as? [String],
let installedEntry = infoPlistDict[uuidEntry] as? [String]
else { return false }
if localEntry != installedEntry {
return false
}
}
return true
}
/// Shows a NSSavePanel to install the mail plugin at the required place.
@@ -58,9 +86,12 @@ struct MailPluginManager {
throw PluginError.permissionNotGranted
}
let localPluginURL = Bundle.main.url(forResource: mailBundleName, withExtension: "mailbundle")!
do {
//Remove old plug-ins first
if FileManager.default.fileExists(atPath: pluginURL.path) {
try FileManager.default.removeItem(at: pluginURL)
}
try FileManager.default.createDirectory(at: pluginsFolderURL, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription)

View File

@@ -19,10 +19,10 @@ struct PreviewData {
static let latitude: Double = 49.878046
static let longitude: Double = 8.656993
static func randomLocation() -> CLLocation {
static func randomLocation(lat: Double = latitude, lng: Double = longitude, distance: Double = 0.005) -> CLLocation {
return CLLocation(
latitude: latitude + Double.random(in: 0..<0.005) * (Bool.random() ? -1 : 1),
longitude: longitude + Double.random(in: 0..<0.005) * (Bool.random() ? -1 : 1)
latitude: lat + Double.random(in: 0..<distance) * (Bool.random() ? -1 : 1),
longitude: lng + Double.random(in: 0..<distance) * (Bool.random() ? -1 : 1)
)
}
@@ -37,6 +37,16 @@ struct PreviewData {
accessory.isDeployed = true
accessory.isActive = true
accessory.isNearby = Bool.random()
//Generate recent locations
let startDate = Date().addingTimeInterval(-60 * 60 * 24)
var date = startDate
var locations: [FindMyLocationReport] = []
while date < Date() {
let location = randomLocation(lat: accessory.lastLocation!.coordinate.latitude, lng: accessory.lastLocation!.coordinate.longitude, distance: 0.0005)
locations.append(FindMyLocationReport(lat: location.coordinate.latitude, lng: location.coordinate.longitude, acc: 10, dP: date, t: date, c: 0))
date += 30 * 60
}
accessory.locations = locations
return accessory
}

View File

@@ -64,6 +64,7 @@ struct AccessoryListEntry: View {
.fill(accessory.isNearby ? Color.green : accessory.isActive ? Color.orange : Color.red)
.frame(width: 8, height: 8)
}
.listRowBackground(Color.clear)
.padding(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 0))
.contextMenu {
Button("Delete", action: { self.delete(accessory) })

View File

@@ -45,7 +45,8 @@ class AccessoryAnnotationView: MKAnnotationView {
func updateView() {
guard let accessory = (self.annotation as? AccessoryAnnotation)?.accessory else { return }
self.pinView?.removeFromSuperview()
self.pinView = NSHostingView(rootView: AccessoryPinView(accessory: accessory))
self.pinView = nil
self.pinView = NSHostingView(rootView: AccessoryPinView(accessory: accessory)) // TODO: LEAK! This view is not release properly
self.addSubview(pinView!)

View File

@@ -56,6 +56,7 @@ struct ManageAccessoriesView: View {
/// Accessory List view.
var accessoryList: some View {
List(self.accessories, id: \.self, selection: $focusedAccessory) { accessory in
AccessoryListEntry(
accessory: accessory,
@@ -74,9 +75,10 @@ struct ManageAccessoriesView: View {
alertType: self.$alertType,
delete: self.delete(accessory:),
deployAccessoryToMicrobit: self.deploy(accessory:),
zoomOn: { self.focusedAccessory = $0 })
zoomOn: { self.focusedAccessory = $0 }
)
}
.listStyle(SidebarListStyle())
.listStyle(PlainListStyle())
}
@@ -271,3 +273,11 @@ struct ManageAccessoriesView_Previews: PreviewProvider {
ManageAccessoriesView(alertType: self.$alertType, focusedAccessory: self.$focussed, accessoryToDeploy: self.$deploy, showESP32DeploySheet: self.$showESPSheet)
}
}
//FIXME: This is a workaround, because the List with Default style (and clear background) started to crop the rows on macOS 11.3
extension NSTableView {
open override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
self.backgroundColor = .clear
}
}

View File

@@ -52,8 +52,8 @@ final class MapViewController: NSViewController, MKMapViewDelegate {
}
func zoomInOn(annotations: [MKAnnotation]) {
DispatchQueue.main.async {
self.mapView.showAnnotations(annotations, animated: true)
DispatchQueue.main.async { [weak self] in
self?.mapView.showAnnotations(annotations, animated: true)
}
}

View File

@@ -13,12 +13,16 @@ import SwiftUI
struct OpenHaystackApp: App {
@StateObject var accessoryController: AccessoryController
var accessoryNearbyMonitor: AccessoryNearbyMonitor?
var frameWidth: CGFloat? = nil
var frameHeight: CGFloat? = nil
init() {
let accessoryController: AccessoryController
if ProcessInfo().arguments.contains("-preview") {
accessoryController = AccessoryControllerPreview(accessories: PreviewData.accessories, findMyController: FindMyController())
self.accessoryNearbyMonitor = nil
// self.frameWidth = 1920
// self.frameHeight = 1080
} else {
accessoryController = AccessoryController()
self.accessoryNearbyMonitor = AccessoryNearbyMonitor(accessoryController: accessoryController)
@@ -30,6 +34,7 @@ struct OpenHaystackApp: App {
WindowGroup {
OpenHaystackMainView()
.environmentObject(self.accessoryController)
.frame(width: self.frameWidth, height: self.frameHeight)
}
.commands {
SidebarCommands()

View File

@@ -26,10 +26,12 @@
CFTypeRef item;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &item);
if (status == errSecSuccess) {
NSData *securityToken = (__bridge NSData *)(item);
CFRelease(item);
NSLog(@"Fetched token %@", [[NSString alloc] initWithData:securityToken encoding:NSUTF8StringEncoding]);
if (securityToken.length == 0) {
@@ -79,7 +81,8 @@
if (status == errSecSuccess) {
NSDictionary *itemDict = (__bridge NSDictionary *)(item);
CFRelease(item);
NSString *accountId = itemDict[(NSString *)kSecAttrAccount];
return accountId;

View File

@@ -60,5 +60,65 @@
<array>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.5PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.13 to 99.99.99</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3608.60.0.2.1) on OS X Version 10.15 (build 19D49f)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
<string># For mail version 14.0 (3652.0.5.2.1) on OS X Version 11.0 (build 20A5343i)</string>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.7PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.13 to 99.99.99</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3608.60.0.2.1) on OS X Version 10.15 (build 19D49f)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
<string># For mail version 14.0 (3652.0.5.2.1) on OS X Version 11.0 (build 20A5343i)</string>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported10.14PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.13 to 99.99.99</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3608.60.0.2.1) on OS X Version 10.15 (build 19D49f)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
<string># For mail version 14.0 (3652.0.5.2.1) on OS X Version 11.0 (build 20A5343i)</string>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported10.13PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.13 to 99.99.99</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3608.60.0.2.1) on OS X Version 10.15 (build 19D49f)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
<string># For mail version 14.0 (3652.0.5.2.1) on OS X Version 11.0 (build 20A5343i)</string>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
<key>Supported11.8PluginCompatibilityUUIDs</key>
<array>
<string># UUIDs for versions from 10.13 to 99.99.99</string>
<string># For mail version 11.0 (3441.0.1) on OS X Version 10.13 (build 17A315i)</string>
<string>C86CD990-4660-4E36-8CDA-7454DEB2E199</string>
<string># For mail version 12.0 (3445.100.39) on OS X Version 10.14.1 (build 18B45d)</string>
<string>A4343FAF-AE18-40D0-8A16-DFAE481AF9C1</string>
<string># For mail version 13.0 (3608.60.0.2.1) on OS X Version 10.15 (build 19D49f)</string>
<string>6EEA38FB-1A0B-469B-BB35-4C2E0EEA9053</string>
<string># For mail version 14.0 (3652.0.5.2.1) on OS X Version 11.0 (build 20A5343i)</string>
<string>D985F0E4-3BBC-4B95-BBA1-12056AC4A531</string>
</array>
</dict>
</plist>

View File

@@ -58,6 +58,7 @@ Our plugin does not access any other private data such as emails (see [source co
2. Open OpenHaystack. This will ask you to install the Mail plugin in `~/Library/Mail/Bundle`.
3. Open a terminal and run `sudo spctl --master-disable`, which will disable Gatekeeper and allow our Apple Mail plugin to run.
4. Open Apple Mail. Go to _Preferences__General__Manage Plug-Ins..._ and activate the checkbox next to _OpenHaystackMail.mailbundle_.
* If the _Manage Plug-Ins..._ button does not appear. Run this command in terminal `sudo defaults write "/Library/Preferences/com.apple.mail" EnableBundles 1`
5. Allow access and restart Mail.
6. Open a terminal and enter `sudo spctl --master-enable`, which will enable Gatekeeper again.
@@ -122,7 +123,8 @@ Feel free to port OpenHaystack to other devices that support Bluetooth Low Energ
## References
- Alexander Heinrich, Milan Stute, Tim Kornhuber, Matthias Hollick. **Who Can _Find My_ Devices? Security and Privacy of Apple's Crowd-Sourced Bluetooth Location Tracking System.** _Proceedings on Privacy Enhancing Technologies (PoPETs)_, 2021. [📄 Preprint](https://arxiv.org/abs/2103.02282).
- Alexander Heinrich, Milan Stute, Tim Kornhuber, Matthias Hollick. **Who Can _Find My_ Devices? Security and Privacy of Apple's Crowd-Sourced Bluetooth Location Tracking System.** _Proceedings on Privacy Enhancing Technologies (PoPETs)_, 2021. [doi:10.2478/popets-2021-0045](https://doi.org/10.2478/popets-2021-0045) [📄 Paper](https://www.petsymposium.org/2021/files/papers/issue3/popets-2021-0045.pdf) [📄 Preprint](https://arxiv.org/abs/2103.02282).
- Alexander Heinrich, Milan Stute, and Matthias Hollick. **DEMO: OpenHaystack: A Framework for Tracking Personal Bluetooth Devices via Apples Massive Find My Network.** _14th ACM Conference on Security and Privacy in Wireless and Mobile (WiSec 21)_, 2021.
- Tim Kornhuber. **Analysis of Apple's Crowd-Sourced Location Tracking System.** _Technical University of Darmstadt_, Master's thesis, 2020.
- Apple Inc. **Find My Network Accessory Specification Developer Preview Release R3.** 2020. [📄 Download](https://developer.apple.com/find-my/).