From fe534931cf4e5daa2c42447543899012199e39b2 Mon Sep 17 00:00:00 2001 From: Mike A Date: Thu, 25 Apr 2024 19:37:01 +0200 Subject: [PATCH] accessory: Integrate .plist reading into library --- examples/real_airtag.py | 34 +++------------------------------- findmy/accessory.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/examples/real_airtag.py b/examples/real_airtag.py index a259e35..fef44f6 100644 --- a/examples/real_airtag.py +++ b/examples/real_airtag.py @@ -3,13 +3,11 @@ Example showing how to fetch locations of an AirTag, or any other FindMy accesso """ from __future__ import annotations -import plistlib -from datetime import datetime, timedelta, timezone from pathlib import Path from _login import get_account_sync -from findmy import FindMyAccessory, KeyPair +from findmy import FindMyAccessory from findmy.reports import RemoteAnisetteProvider # URL to (public or local) anisette server @@ -18,37 +16,11 @@ ANISETTE_SERVER = "http://localhost:6969" # Path to a .plist dumped from the Find My app. PLIST_PATH = Path("airtag.plist") -# == The variables below are auto-filled from the plist!! == - -with PLIST_PATH.open("rb") as f: - device_data = plistlib.load(f) - -# PRIVATE master key. 28 (?) bytes. -MASTER_KEY = device_data["privateKey"]["key"]["data"][-28:] - -# "Primary" shared secret. 32 bytes. -SKN = device_data["sharedSecret"]["key"]["data"] - -# "Secondary" shared secret. 32 bytes. -SKS = device_data["secondarySharedSecret"]["key"]["data"] - -# "Paired at" timestamp (UTC) -PAIRED_AT = device_data["pairingDate"].replace(tzinfo=timezone.utc) - - -def _gen_keys(airtag: FindMyAccessory, _from: datetime, to: datetime) -> set[KeyPair]: - keys = set() - while _from < to: - keys.update(airtag.keys_at(_from)) - - _from += timedelta(minutes=15) - - return keys - def main() -> None: # Step 0: create an accessory key generator - airtag = FindMyAccessory(MASTER_KEY, SKN, SKS, PAIRED_AT) + with PLIST_PATH.open("rb") as f: + airtag = FindMyAccessory.from_plist(f) # Step 1: log into an Apple account print("Logging into account") diff --git a/findmy/accessory.py b/findmy/accessory.py index 096e216..d21d4e1 100644 --- a/findmy/accessory.py +++ b/findmy/accessory.py @@ -6,9 +6,10 @@ Accessories could be anything ranging from AirTags to iPhones. from __future__ import annotations import logging +import plistlib from abc import ABC, abstractmethod -from datetime import datetime, timedelta -from typing import Generator, overload +from datetime import datetime, timedelta, timezone +from typing import IO, Generator, overload from typing_extensions import override @@ -143,6 +144,30 @@ class FindMyAccessory(RollingKeyPairSource): return possible_keys + @classmethod + def from_plist(cls, plist: IO[bytes]) -> FindMyAccessory: + """Create a FindMyAccessory from a .plist file dumped from the FindMy app.""" + device_data = plistlib.load(plist) + + # PRIVATE master key. 28 (?) bytes. + master_key = device_data["privateKey"]["key"]["data"][-28:] + + # "Primary" shared secret. 32 bytes. + skn = device_data["sharedSecret"]["key"]["data"] + + # "Secondary" shared secret. 32 bytes. + if "secondarySharedSecret" in device_data: + # AirTag + sks = device_data["secondarySharedSecret"]["key"]["data"] + else: + # iDevice + sks = device_data["secureLocationsSharedSecret"]["key"]["data"] + + # "Paired at" timestamp (UTC) + paired_at = device_data["pairingDate"].replace(tzinfo=timezone.utc) + + return cls(master_key, skn, sks, paired_at) + class AccessoryKeyGenerator(KeyGenerator[KeyPair]): """KeyPair generator. Uses the same algorithm internally as FindMy accessories do."""