// // 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 // // swiftlint:disable identifier_name import CoreLocation import Foundation struct FindMyDevice: Codable, Hashable { let deviceId: String var keys = [FindMyKey]() var catalinaBigSurKeyFiles: [Data]? /// KeyHash: Report results var reports: [FindMyReport]? var decryptedReports: [FindMyLocationReport]? func hash(into hasher: inout Hasher) { hasher.combine(deviceId) } static func == (lhs: FindMyDevice, rhs: FindMyDevice) -> Bool { lhs.deviceId == rhs.deviceId } } struct FindMyKey: Codable { internal init( advertisedKey: Data, hashedKey: Data, privateKey: Data, startTime: Date?, duration: Double?, pu: Data?, yCoordinate: Data?, fullKey: Data? ) { self.advertisedKey = advertisedKey self.hashedKey = hashedKey // The private key should only be 28 bytes long. If a 85 bytes full private public key is entered we truncate it here if privateKey.count == 85 { self.privateKey = privateKey.subdata(in: 57.. Int32 in // Convert the endianness pointer.load(as: Int32.self).bigEndian } // It's a cocoa time stamp (counting from 2001) self.timestamp = Date(timeIntervalSinceReferenceDate: TimeInterval(timestamp)) self.confidence = payload[4] self.id = try values.decode(String.self, forKey: .id) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.datePublished.timeIntervalSince1970 * 1000, forKey: .datePublished) try container.encode(self.payload.base64EncodedString(), forKey: .payload) try container.encode(self.id, forKey: .id) try container.encode(self.statusCode, forKey: .statusCode) } } struct FindMyLocationReport: Codable { let latitude: Double let longitude: Double let accuracy: UInt8 let datePublished: Date let timestamp: Date? let confidence: UInt8? var location: CLLocation { return CLLocation(latitude: latitude, longitude: longitude) } init(lat: Double, lng: Double, acc: UInt8, dP: Date, t: Date, c: UInt8) { self.latitude = lat self.longitude = lng self.accuracy = acc self.datePublished = dP self.timestamp = t self.confidence = c } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.latitude = try values.decode(Double.self, forKey: .latitude) self.longitude = try values.decode(Double.self, forKey: .longitude) do { let uAcc = try values.decode(UInt8.self, forKey: .accuracy) self.accuracy = uAcc } catch { let iAcc = try values.decode(Int8.self, forKey: .accuracy) self.accuracy = UInt8(bitPattern: iAcc) } self.datePublished = try values.decode(Date.self, forKey: .datePublished) self.timestamp = try? values.decode(Date.self, forKey: .timestamp) self.confidence = try? values.decode(UInt8.self, forKey: .confidence) } } enum FindMyError: Error { case decryptionError(description: String) }