mirror of
https://github.com/malmeloo/FindMy.py.git
synced 2026-04-17 19:53:53 +02:00
feat: dump alignment info when importing accessories
This commit is contained in:
@@ -8,7 +8,7 @@ import logging
|
||||
from importlib.metadata import version
|
||||
from pathlib import Path
|
||||
|
||||
from .plist import get_key, list_accessories
|
||||
from .plist import list_accessories
|
||||
|
||||
|
||||
def main() -> None: # noqa: D103
|
||||
@@ -96,8 +96,7 @@ def decrypt_all(out_dir: str | Path | None = None) -> None:
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
return d / f"{acc.identifier}.json"
|
||||
|
||||
key = get_key()
|
||||
accs = list_accessories(key=key)
|
||||
accs = list_accessories()
|
||||
jsons = [acc.to_json(get_path(out_dir, acc)) for acc in accs]
|
||||
print(json.dumps(jsons, indent=4, ensure_ascii=False)) # noqa: T201
|
||||
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
"""Utils for decrypting the encypted .record files into .plist files."""
|
||||
"""
|
||||
Utils for decrypting the encypted .record files into .plist files.
|
||||
|
||||
Originally from:
|
||||
Author: Shane B. <shane@wander.dev>
|
||||
in https://github.com/parawanderer/OpenTagViewer/blob/08a59cab551721afb9dc9f829ad31dae8d5bd400/python/airtag_decryptor.py
|
||||
which was based on:
|
||||
Based on: https://gist.github.com/airy10/5205dc851fbd0715fcd7a5cdde25e7c8
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import plistlib
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
@@ -11,24 +20,56 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
from .accessory import FindMyAccessory
|
||||
|
||||
# Originally from:
|
||||
# Author: Shane B. <shane@wander.dev>
|
||||
# in https://github.com/parawanderer/OpenTagViewer/blob/08a59cab551721afb9dc9f829ad31dae8d5bd400/python/airtag_decryptor.py
|
||||
# which was based on:
|
||||
# Based on: https://gist.github.com/airy10/5205dc851fbd0715fcd7a5cdde25e7c8
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_DEFAULT_SEARCH_PATH = Path.home() / "Library" / "com.apple.icloud.searchpartyd"
|
||||
|
||||
|
||||
# consider switching to this library https://github.com/microsoft/keyper
|
||||
# once they publish a version of it that includes my MR with the changes to make it compatible
|
||||
# with keys that are non-utf-8 encoded (like the BeaconStore one)
|
||||
# if I contribute this, properly escape the label argument here...
|
||||
def get_key() -> bytes:
|
||||
def _get_beaconstore_key() -> bytes:
|
||||
"""Get the decryption key for BeaconStore using the system password prompt window."""
|
||||
# This thing will pop up 2 Password Input windows...
|
||||
key_in_hex = subprocess.getoutput("/usr/bin/security find-generic-password -l 'BeaconStore' -w") # noqa: S605
|
||||
return bytes.fromhex(key_in_hex)
|
||||
|
||||
|
||||
def _get_accessory_name(
|
||||
accessory_id: str,
|
||||
key: bytes,
|
||||
*,
|
||||
search_path: Path | None = None,
|
||||
) -> str | None:
|
||||
search_path = search_path or _DEFAULT_SEARCH_PATH
|
||||
path = next((search_path / "BeaconNamingRecord" / accessory_id).glob(pattern="*.record"), None)
|
||||
if path is None:
|
||||
logger.warning(
|
||||
"Accessory %s does not have a BeaconNamingRecord, defaulting to None", accessory_id
|
||||
)
|
||||
return None
|
||||
|
||||
naming_record_plist = decrypt_plist(path, key)
|
||||
return naming_record_plist.get("name", None)
|
||||
|
||||
|
||||
def _get_alignment_plist(
|
||||
accessory_id: str,
|
||||
key: bytes,
|
||||
*,
|
||||
search_path: Path | None = None,
|
||||
) -> dict | None:
|
||||
search_path = search_path or _DEFAULT_SEARCH_PATH
|
||||
path = next((search_path / "KeyAlignmentRecords" / accessory_id).glob(pattern="*.record"), None)
|
||||
if path is None:
|
||||
logger.warning("Accessory %s does not have a KeyAlignmentRecord", accessory_id)
|
||||
return None
|
||||
|
||||
return decrypt_plist(path, key)
|
||||
|
||||
|
||||
def decrypt_plist(encrypted: str | Path | bytes | IO[bytes], key: bytes) -> dict:
|
||||
"""
|
||||
Decrypts the encrypted plist file at :meth:`encrypted` using the provided :meth:`key`.
|
||||
@@ -76,15 +117,15 @@ def list_accessories(
|
||||
search_path = Path.home() / "Library" / "com.apple.icloud.searchpartyd"
|
||||
search_path = Path(search_path)
|
||||
if key is None:
|
||||
key = get_key()
|
||||
key = _get_beaconstore_key()
|
||||
|
||||
accesories = []
|
||||
encrypted_plist_paths = search_path.glob("OwnedBeacons/*.record")
|
||||
for path in encrypted_plist_paths:
|
||||
plist = decrypt_plist(path, key)
|
||||
naming_record_path = next((search_path / "BeaconNamingRecord" / path.stem).glob("*.record"))
|
||||
naming_record_plist = decrypt_plist(naming_record_path, key)
|
||||
name = naming_record_plist["name"]
|
||||
accessory = FindMyAccessory.from_plist(plist, name=name)
|
||||
name = _get_accessory_name(path.stem, key)
|
||||
alignment_plist = _get_alignment_plist(path.stem, key)
|
||||
|
||||
accessory = FindMyAccessory.from_plist(plist, alignment_plist, name=name)
|
||||
accesories.append(accessory)
|
||||
return accesories
|
||||
|
||||
Reference in New Issue
Block a user