Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/build for web #135

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions Firmware/Linux_HCI/HCI.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ def bytes_to_strarray(bytes_, with_prefix=False):
def run_hci_cmd(cmd, hci="hci0", wait=1):
cmd_ = ["hcitool", "-i", hci, "cmd"]
cmd_ += cmd
print(cmd_)
#print(cmd_)
subprocess.run(cmd_)
if wait > 0:
time.sleep(wait)

def run_hciconf_leadv_3(hci="hci0"):
#EDIT: the above command makes the advertised service connectable. If you don't want to allow connections, change it to $ sudo hciconfig hci0 leadv 3
cmd_ = ["hciconfig", hci, "leadv", "3"]
subprocess.run(cmd_)

def start_advertising(key, interval_ms=2000):
addr = bytearray(key[:6])
Expand All @@ -46,14 +50,14 @@ def start_advertising(key, interval_ms=2000):
adv[7:29] = key[6:28]
adv[29] = key[0] >> 6

print(f"key ({len(key):2}) {key.hex()}")
print(f"address ({len(addr):2}) {addr.hex()}")
print(f"payload ({len(adv):2}) {adv.hex()}")
#print(f"key ({len(key):2}) {key.hex()}")
#print(f"address ({len(addr):2}) {addr.hex()}")
#print(f"payload ({len(adv):2}) {adv.hex()}")

# Set BLE address
run_hci_cmd(["0x3f", "0x001"] + bytes_to_strarray(addr, with_prefix=True)[::-1])
subprocess.run(["systemctl", "restart", "bluetooth"])
time.sleep(1)
time.sleep(3)

# Set BLE advertisement payload
run_hci_cmd(["0x08", "0x0008"] + [format(len(adv), "x")] + bytes_to_strarray(adv))
Expand All @@ -70,6 +74,8 @@ def start_advertising(key, interval_ms=2000):
# Start BLE advertising
run_hci_cmd(["0x08", "0x000a"] + ["01"], wait=0)

run_hciconf_leadv_3()


def main(args):
parser = argparse.ArgumentParser()
Expand Down
52 changes: 34 additions & 18 deletions openhaystack-mobile/lib/findMy/decrypt_reports.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import 'dart:convert';
import 'dart:isolate';
import 'dart:typed_data';

import 'package:flutter/foundation.dart';
import 'package:pointycastle/export.dart';
import 'package:pointycastle/src/utils.dart' as pc_utils;
import 'package:openhaystack_mobile/findMy/models.dart';

import 'package:cryptography/cryptography.dart' as nice_crypto;

class DecryptReports {
/// Decrypts a given [FindMyReport] with the given private key.
static Future<FindMyLocationReport> decryptReport(
Expand All @@ -19,26 +21,26 @@ class DecryptReports {

_decodeTimeAndConfidence(payloadData, report);

final privateKey = ECPrivateKey(
pc_utils.decodeBigIntWithSign(1, key),
curveDomainParam);
final privateKey =
ECPrivateKey(pc_utils.decodeBigIntWithSign(1, key), curveDomainParam);

final decodePoint = curveDomainParam.curve.decodePoint(ephemeralKeyBytes);
final ephemeralPublicKey = ECPublicKey(decodePoint, curveDomainParam);

final Uint8List sharedKeyBytes = _ecdh(ephemeralPublicKey, privateKey);
final Uint8List derivedKey = _kdf(sharedKeyBytes, ephemeralKeyBytes);

final decryptedPayload = _decryptPayload(encData, derivedKey, tag);
final decryptedPayload = await _decryptPayload(encData, derivedKey, tag);
final locationReport = _decodePayload(decryptedPayload, report);

return locationReport;
}

/// Decodes the unencrypted timestamp and confidence
static void _decodeTimeAndConfidence(Uint8List payloadData, FindMyReport report) {
final seenTimeStamp = payloadData.sublist(0, 4).buffer.asByteData()
.getInt32(0, Endian.big);
static void _decodeTimeAndConfidence(
Uint8List payloadData, FindMyReport report) {
final seenTimeStamp =
payloadData.sublist(0, 4).buffer.asByteData().getInt32(0, Endian.big);
final timestamp = DateTime(2001).add(Duration(seconds: seenTimeStamp));
final confidence = payloadData.elementAt(4);
report.timestamp = timestamp;
Expand All @@ -47,11 +49,12 @@ class DecryptReports {

/// Performs an Elliptic Curve Diffie-Hellman with the given keys.
/// Returns the derived raw key data.
static Uint8List _ecdh(ECPublicKey ephemeralPublicKey, ECPrivateKey privateKey) {
static Uint8List _ecdh(
ECPublicKey ephemeralPublicKey, ECPrivateKey privateKey) {
final sharedKey = ephemeralPublicKey.Q! * privateKey.d;
final sharedKeyBytes = pc_utils.encodeBigIntAsUnsigned(
sharedKey!.x!.toBigInteger()!);
print("Isolate:${Isolate.current.hashCode}: Shared Key (shared secret): ${base64Encode(sharedKeyBytes)}");
final sharedKeyBytes =
pc_utils.encodeBigIntAsUnsigned(sharedKey!.x!.toBigInteger()!);
print("Shared Key (shared secret): ${base64Encode(sharedKeyBytes)}");

return sharedKeyBytes;
}
Expand All @@ -60,7 +63,6 @@ class DecryptReports {
/// the resulting [FindMyLocationReport].
static FindMyLocationReport _decodePayload(
Uint8List payload, FindMyReport report) {

final latitude = payload.buffer.asByteData(0, 4).getUint32(0, Endian.big);
final longitude = payload.buffer.asByteData(4, 4).getUint32(0, Endian.big);
final accuracy = payload.buffer.asByteData(8, 1).getUint8(0);
Expand All @@ -74,14 +76,28 @@ class DecryptReports {

/// Decrypts the given cipher text with the key data using an AES-GCM block cipher.
/// Returns the decrypted raw data.
static Uint8List _decryptPayload(
Uint8List cipherText, Uint8List symmetricKey, Uint8List tag) {
static Future<Uint8List> _decryptPayload(
Uint8List cipherText, Uint8List symmetricKey, Uint8List tag) async {
final decryptionKey = symmetricKey.sublist(0, 16);
final iv = symmetricKey.sublist(16, symmetricKey.length);
if (kIsWeb) {
nice_crypto.SecretKey secretKey =
new nice_crypto.SecretKey(decryptionKey);

nice_crypto.SecretBox secretBox = new nice_crypto.SecretBox(cipherText,
nonce: iv, mac: nice_crypto.Mac(tag));

List<int> decrypted = await nice_crypto.AesGcm.with128bits()
.decrypt(secretBox, secretKey: secretKey);

return Uint8List.fromList(decrypted);
}

final aesGcm = GCMBlockCipher(AESEngine())
..init(false, AEADParameters(KeyParameter(decryptionKey),
tag.lengthInBytes * 8, iv, tag));
..init(
false,
AEADParameters(
KeyParameter(decryptionKey), tag.lengthInBytes * 8, iv, tag));

final plainText = Uint8List(cipherText.length);
var offset = 0;
Expand Down Expand Up @@ -109,7 +125,7 @@ class DecryptReports {
Uint8List out = Uint8List(shaDigest.digestSize);
shaDigest.doFinal(out, 0);

print("Isolate:${Isolate.current.hashCode}: Derived key: ${base64Encode(out)}");
print("Derived key: ${base64Encode(out)}");
return out;
}
}
76 changes: 44 additions & 32 deletions openhaystack-mobile/lib/findMy/find_my_controller.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:isolate';
import 'dart:typed_data';

import 'package:flutter/foundation.dart';
Expand All @@ -14,25 +13,33 @@ import 'package:openhaystack_mobile/findMy/reports_fetcher.dart';

class FindMyController {
static const _storage = FlutterSecureStorage();
static final ECCurve_secp224r1 _curveParams = ECCurve_secp224r1();
static final ECCurve_secp224r1 _curveParams = ECCurve_secp224r1();
static HashMap _keyCache = HashMap();

/// Starts a new [Isolate], fetches and decrypts all location reports
/// for the given [FindMyKeyPair].
/// Returns a list of [FindMyLocationReport]'s.
static Future<List<FindMyLocationReport>> computeResults(FindMyKeyPair keyPair) async{
static Future<List<FindMyLocationReport>> computeResults(
FindMyKeyPair keyPair) async {
await _loadPrivateKey(keyPair);
return compute(_getListedReportResults, keyPair);
}

/// Fetches and decrypts the location reports for the given
/// [FindMyKeyPair] from apples FindMy Network.
/// Returns a list of [FindMyLocationReport].
static Future<List<FindMyLocationReport>> _getListedReportResults(FindMyKeyPair keyPair) async{
static Future<List<FindMyLocationReport>> _getListedReportResults(
FindMyKeyPair keyPair) async {
List<FindMyLocationReport> results = <FindMyLocationReport>[];
final jsonResults = await ReportsFetcher.fetchLocationReports(keyPair.getHashedAdvertisementKey());
final jsonResults = await ReportsFetcher.fetchLocationReports(
keyPair.getHashedAdvertisementKey());
for (var result in jsonResults) {
results.add(await _decryptResult(result, keyPair, keyPair.privateKeyBase64!));
try {
results.add(
await _decryptResult(result, keyPair, keyPair.privateKeyBase64!));
} catch (e) {
print(e);
}
}
return results;
}
Expand All @@ -43,7 +50,8 @@ class FindMyController {
String? privateKey;
if (!_keyCache.containsKey(keyPair.hashedPublicKey)) {
privateKey = await _storage.read(key: keyPair.hashedPublicKey);
final newKey = _keyCache.putIfAbsent(keyPair.hashedPublicKey, () => privateKey);
final newKey =
_keyCache.putIfAbsent(keyPair.hashedPublicKey, () => privateKey);
assert(newKey == privateKey);
} else {
privateKey = _keyCache[keyPair.hashedPublicKey];
Expand All @@ -55,27 +63,29 @@ class FindMyController {
static ECPublicKey _derivePublicKey(ECPrivateKey privateKey) {
final pk = _curveParams.G * privateKey.d;
final publicKey = ECPublicKey(pk, _curveParams);
print("Isolate:${Isolate.current.hashCode}: Point Data: ${base64Encode(publicKey.Q!.getEncoded(false))}");
print("Point Data: ${base64Encode(publicKey.Q!.getEncoded(false))}");

return publicKey;
}

/// Decrypts the encrypted reports with the given [FindMyKeyPair] and private key.
/// Returns the decrypted report as a [FindMyLocationReport].
static Future<FindMyLocationReport> _decryptResult(dynamic result, FindMyKeyPair keyPair, String privateKey) async {
assert (result["id"]! == keyPair.getHashedAdvertisementKey(),
"Returned FindMyReport hashed key != requested hashed key");

final unixTimestampInMillis = result["datePublished"];
final datePublished = DateTime.fromMillisecondsSinceEpoch(unixTimestampInMillis);
static Future<FindMyLocationReport> _decryptResult(
dynamic result, FindMyKeyPair keyPair, String privateKey) async {
assert(result["id"]! == keyPair.getHashedAdvertisementKey(),
"Returned FindMyReport hashed key != requested hashed key");

final unixTimestampInMillis = result["datePublished"];
final datePublished =
DateTime.fromMillisecondsSinceEpoch(unixTimestampInMillis);
FindMyReport report = FindMyReport(
datePublished,
base64Decode(result["payload"]),
keyPair.getHashedAdvertisementKey(),
result["statusCode"]);

FindMyLocationReport decryptedReport = await DecryptReports
.decryptReport(report, base64Decode(privateKey));
FindMyLocationReport decryptedReport =
await DecryptReports.decryptReport(report, base64Decode(privateKey));

return decryptedReport;
}
Expand All @@ -86,10 +96,12 @@ class FindMyController {
final privateKeyBase64 = await _storage.read(key: base64HashedPublicKey);

ECPrivateKey privateKey = ECPrivateKey(
pc_utils.decodeBigIntWithSign(1, base64Decode(privateKeyBase64!)), _curveParams);
pc_utils.decodeBigIntWithSign(1, base64Decode(privateKeyBase64!)),
_curveParams);
ECPublicKey publicKey = _derivePublicKey(privateKey);

return FindMyKeyPair(publicKey, base64HashedPublicKey, privateKey, DateTime.now(), -1);
return FindMyKeyPair(
publicKey, base64HashedPublicKey, privateKey, DateTime.now(), -1);
}

/// Imports a base64 encoded private key to the local [FlutterSecureStorage].
Expand All @@ -101,14 +113,11 @@ class FindMyController {
final ECPublicKey publicKey = _derivePublicKey(privateKey);
final hashedPublicKey = getHashedPublicKey(publicKey: publicKey);
final keyPair = FindMyKeyPair(
publicKey,
hashedPublicKey,
privateKey,
DateTime.now(),
-1);

await _storage.write(key: hashedPublicKey, value: keyPair.getBase64PrivateKey());

publicKey, hashedPublicKey, privateKey, DateTime.now(), -1);

await _storage.write(
key: hashedPublicKey, value: keyPair.getBase64PrivateKey());

return keyPair;
}

Expand All @@ -117,16 +126,18 @@ class FindMyController {
static Future<FindMyKeyPair> generateKeyPair() async {
final ecCurve = ECCurve_secp224r1();
final secureRandom = SecureRandom('Fortuna')
..seed(KeyParameter(
Platform.instance.platformEntropySource().getBytes(32)));
..seed(
KeyParameter(Platform.instance.platformEntropySource().getBytes(32)));
ECKeyGenerator keyGen = ECKeyGenerator()
..init(ParametersWithRandom(ECKeyGeneratorParameters(ecCurve), secureRandom));
..init(ParametersWithRandom(
ECKeyGeneratorParameters(ecCurve), secureRandom));

final newKeyPair = keyGen.generateKeyPair();
final ECPublicKey publicKey = newKeyPair.publicKey as ECPublicKey;
final ECPrivateKey privateKey = newKeyPair.privateKey as ECPrivateKey;
final hashedKey = getHashedPublicKey(publicKey: publicKey);
final keyPair = FindMyKeyPair(publicKey, hashedKey, privateKey, DateTime.now(), -1);
final keyPair =
FindMyKeyPair(publicKey, hashedKey, privateKey, DateTime.now(), -1);
await _storage.write(key: hashedKey, value: keyPair.getBase64PrivateKey());

return keyPair;
Expand All @@ -135,12 +146,13 @@ class FindMyController {
/// Returns hashed, base64 encoded public key for given [publicKeyBytes]
/// or for an [ECPublicKey] object [publicKey], if [publicKeyBytes] equals null.
/// Returns the base64 encoded hashed public key as a [String].
static String getHashedPublicKey({Uint8List? publicKeyBytes, ECPublicKey? publicKey}) {
static String getHashedPublicKey(
{Uint8List? publicKeyBytes, ECPublicKey? publicKey}) {
var pkBytes = publicKeyBytes ?? publicKey!.Q!.getEncoded(false);
final shaDigest = SHA256Digest();
shaDigest.update(pkBytes, 0, pkBytes.lengthInBytes);
Uint8List out = Uint8List(shaDigest.digestSize);
shaDigest.doFinal(out, 0);
return base64Encode(out);
}
}
}
2 changes: 1 addition & 1 deletion openhaystack-mobile/lib/findMy/reports_fetcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:http/http.dart' as http;

class ReportsFetcher {
static const _seemooEndpoint = "https://add-your-proxy-server-here/getLocationReports"
static const _seemooEndpoint = "https://add-your-proxy-server-here/getLocationReports";

/// Fetches the location reports corresponding to the given hashed advertisement
/// key.
Expand Down
Loading