Files
MontaukOS/scripts/import-bluez-bond.sh
daniel d9d32deb3e feat: reliable Bluetooth pairing, reconnection, and A2DP setup
Four fixes, each a root cause verified on hardware (AX211 + Bose QC Ultra):

1. Link Key Request Reply TRUNCATED: the pending-command queue's params
   buffer was 16 bytes; the reply is 22 (addr 6 + key 16).  The controller
   got 10 key bytes -> every stored-key reconnection failed authentication
   (status 5) since 2026-06-03 (c119a70).  Fresh pairings never touch this
   path, which kept the bug perfectly disguised as a headset quirk.

2. Secure Connections host support (0x0C7A) now enabled: bonds are minted
   as P-256 (Type=7), interoperable with BlueZ's, and SC-bonded peers can
   actually authenticate us.

3. Never write the BD_ADDR override (0xFC31) with the factory address:
   it desyncs the firmware's crypto address from the on-air one and ALL
   SSP pairing fails with status 5.  (The spoofing feature itself was
   already known-cosmetic: the baseband answers pages on the factory
   address regardless.)  import-bluez-bond.sh now removes the override.

4. A2DP channel setup: wait for Encryption Change before dialing L2CAP
   (post-SSP sinks ignore unencrypted CONN_REQ), and LISTEN 2.5s first --
   on reconnection the sink dials AVDTP itself and ignores our dials while
   doing so.  Ends the historical connRsp=FFFF retry-then-give-up failures.

Plus: queued security replies now log delivery + controller status.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 19:46:02 +02:00

96 lines
4.0 KiB
Bash
Executable File

#!/bin/bash
# import-bluez-bond.sh — share a BlueZ (Linux) Bluetooth pairing with MontaukOS.
#
# Dual-boot problem: the AX211's BD_ADDR override (0xFC31) is cosmetic — the
# baseband keeps answering pages on the FACTORY address, so a peer (headset)
# sees Linux and MontaukOS as ONE device with ONE link-key slot. Each OS
# pairing clobbers the key the other OS depends on, and every reconnect from
# the other OS then dies with Authentication Failure (reason 0x05).
#
# Fix: both OSes share the SAME identity and the SAME link key. This script
# copies a BlueZ link key into MontaukOS's key store (os/btkeys.bin on the
# installed root) and pins MontaukOS's configured MAC to the adapter's factory
# address so the app reports the identity actually used on air.
#
# Usage:
# sudo ./scripts/import-bluez-bond.sh <montauk-root-mount> <device-mac> [adapter-mac] [--reverse-key]
#
# Example:
# sudo ./scripts/import-bluez-bond.sh /run/media/$USER/<uuid> E4:58:BC:5F:E0:30
#
# --reverse-key: byte-swap the key (fallback if authentication still fails).
#
# NOTE: replaces the MontaukOS key store with this single bond. Re-pair other
# devices from MontaukOS afterwards if you had more.
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "error: must run as root (reads /var/lib/bluetooth)" >&2
exit 1
fi
TARGET="${1:?usage: import-bluez-bond.sh <montauk-root-mount> <device-mac> [adapter-mac] [--reverse-key]}"
DEVICE="${2:?missing device MAC}"
ADAPTER="${3:-}"
REVERSE=0
for arg in "$@"; do [ "$arg" = "--reverse-key" ] && REVERSE=1; done
[ "$ADAPTER" = "--reverse-key" ] && ADAPTER=""
DEVICE="${DEVICE^^}"
# Auto-detect the adapter: the directory under /var/lib/bluetooth that holds
# this device, preferring a factory (not locally-administered "x2:") address.
if [ -z "$ADAPTER" ]; then
for dir in /var/lib/bluetooth/*/; do
a="$(basename "$dir")"
[ -f "/var/lib/bluetooth/$a/$DEVICE/info" ] || continue
if [ -z "$ADAPTER" ] || [[ "${ADAPTER:1:1}" == "2" && "${a:1:1}" != "2" ]]; then
ADAPTER="$a"
fi
done
fi
[ -n "$ADAPTER" ] || { echo "error: no adapter dir holds a bond for $DEVICE" >&2; exit 1; }
INFO="/var/lib/bluetooth/$ADAPTER/$DEVICE/info"
[ -f "$INFO" ] || { echo "error: $INFO not found" >&2; exit 1; }
KEY="$(sed -n '/^\[LinkKey\]/,/^\[/{s/^Key=//p}' "$INFO" | head -1)"
[ -n "$KEY" ] || { echo "error: no [LinkKey] Key= in $INFO" >&2; exit 1; }
[ -d "$TARGET/os" ] || { echo "error: $TARGET/os missing — is the MontaukOS root mounted there?" >&2; exit 1; }
echo "adapter (factory identity): $ADAPTER"
echo "device: $DEVICE"
echo "key: ${KEY:0:4}... (${#KEY} hex chars)"
python3 - "$TARGET/os/btkeys.bin" "$DEVICE" "$KEY" "$REVERSE" <<'EOF'
import sys
path, device, keyhex, reverse = sys.argv[1], sys.argv[2], sys.argv[3], int(sys.argv[4])
# MontaukOS on-disk layout (kernel Hci.cpp): 'BTK1' + 8 x { addr[6], key[16], valid[1] }.
# addr is stored LSB-first (HCI wire order); BlueZ's hex key is already in HCI order.
addr = bytes(int(b, 16) for b in reversed(device.split(":")))
key = bytes.fromhex(keyhex)
assert len(key) == 16, "link key must be 16 bytes"
if reverse:
key = key[::-1]
blob = bytearray(b"BTK1")
blob += addr + key + b"\x01" # slot 0: the imported bond
blob += b"\x00" * 23 * 7 # slots 1..7 empty
with open(path, "wb") as f:
f.write(blob)
print(f"wrote {path} ({len(blob)} bytes, 1 bond)")
EOF
# Remove any BD_ADDR override: the controller must run its factory address
# with NO 0xFC31 write. Verified on the AX211: writing 0xFC31 -- even with
# the factory address itself -- desyncs the address the firmware uses in SSP
# crypto from the one on air, and every pairing fails with status 5.
CONF="$TARGET/config/bluetooth.toml"
if [ -f "$CONF" ] && grep -q '^ *mac *=' "$CONF"; then
sed -i '/^ *mac *=/d' "$CONF"
echo "removed mac override from $CONF (0xFC31 breaks SSP on AX211)"
fi
echo "done — boot MontaukOS; the device should reconnect without re-pairing."