d9d32deb3e
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>
96 lines
4.0 KiB
Bash
Executable File
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."
|