9 Commits

Author SHA1 Message Date
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
13 changed files with 87 additions and 170 deletions

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

@@ -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

@@ -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

@@ -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

@@ -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.