feat: auth-failure bond recovery + BlueZ bond import for dual-boot BT

- On Authentication Failure (Auth Complete status!=0 -- previously swallowed
  silently -- or disconnect reason 0x05), drop the stale local link key so
  the next connect falls back to fresh SSP pairing instead of failing
  identically forever (BlueZ behavior).  Log the link-key exchange.

- scripts/import-bluez-bond.sh: copy a BlueZ link key into the MontaukOS
  key store on the installed root.  Root cause: the AX211 BD_ADDR override
  (0xFC31) is cosmetic -- the baseband answers pages on the FACTORY address,
  so peers see Linux and MontaukOS as ONE device with ONE key slot, and each
  OS's pairing clobbers the other's key.  Sharing identity + key ends the
  fight: both OSes reconnect (incl. autoconnect) without re-pairing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 13:44:47 +02:00
parent 5314af3718
commit 13a841d0f2
3 changed files with 142 additions and 1 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 29
#define MONTAUK_BUILD_NUMBER 31
+45
View File
@@ -793,6 +793,29 @@ namespace Drivers::USB::Bluetooth::Hci {
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (g_connections[i].Active && g_connections[i].Handle == handle) {
// Reason 0x05 = Authentication Failure: our stored
// link key no longer matches the remote's (typical
// after another OS's session made the headset evict
// or rotate its copy). Keeping the stale key makes
// EVERY reconnect fail the same way until the
// headset is power-cycled. Do what BlueZ does:
// drop the key, so the next connect sends a
// negative key reply and falls back to fresh SSP
// pairing (Just Works -- fully automated here).
// Invalidate only (no disk I/O in this nested
// context); the dirty flag gets flushed from
// process context.
if (reason == 0x05) {
int idx = FindBondIndex(g_connections[i].BdAddr);
if (idx >= 0) {
g_bonds[idx].valid = false;
memset(g_bonds[idx].key, 0, 16);
g_bondsDirty = true;
KernelLogStream(WARNING, "BT-HCI")
<< "Auth failure with stored key: bond dropped;"
<< " next connect will re-pair";
}
}
g_connections[i].Active = false;
break;
}
@@ -869,11 +892,13 @@ namespace Drivers::USB::Bluetooth::Hci {
memcpy(reply, &params[0], 6);
memcpy(&reply[6], g_bonds[idx].key, 16);
EnqueueHciCmd(OP_LINK_KEY_REQ_REPLY, reply, 22);
KernelLogStream(INFO, "BT-HCI") << "Link key request: stored key sent";
} else {
// Unknown device: tell the controller we have no key so
// the remote falls back to fresh Secure Simple Pairing
// (Just Works) instead of failing auth (reason 0x05).
EnqueueHciCmd(OP_LINK_KEY_REQ_NEG_REPLY, &params[0], 6);
KernelLogStream(INFO, "BT-HCI") << "Link key request: no key, will re-pair";
}
}
break;
@@ -900,6 +925,26 @@ namespace Drivers::USB::Bluetooth::Hci {
if (evtParamLen >= 3 && params[0] == 0) {
uint8_t enc[3] = { params[1], params[2], 0x01 };
EnqueueHciCmd(OP_SET_CONN_ENCRYPT, enc, 3);
} else if (evtParamLen >= 3) {
// Failed authentication was previously swallowed silently.
// Drop the stale bond here too (the remote may not follow
// up with a reason-5 disconnect on every firmware).
KernelLogStream(WARNING, "BT-HCI") << "Authentication failed, status="
<< (uint64_t)params[0];
uint16_t handle = (uint16_t)params[1] | ((uint16_t)params[2] << 8);
for (int i = 0; i < MAX_CONNECTIONS; i++) {
if (g_connections[i].Active && g_connections[i].Handle == handle) {
int idx = FindBondIndex(g_connections[i].BdAddr);
if (idx >= 0) {
g_bonds[idx].valid = false;
memset(g_bonds[idx].key, 0, 16);
g_bondsDirty = true;
KernelLogStream(WARNING, "BT-HCI")
<< "Stale bond dropped; next connect will re-pair";
}
break;
}
}
}
break;
}
+96
View File
@@ -0,0 +1,96 @@
#!/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
# Pin the configured MAC to the factory address (truthful display; the 0xFC31
# override is cosmetic on this hardware anyway).
CONF="$TARGET/config/bluetooth.toml"
mkdir -p "$TARGET/config"
if [ -f "$CONF" ] && grep -q '^ *mac *=' "$CONF"; then
sed -i "s/^ *mac *=.*/mac = \"$ADAPTER\"/" "$CONF"
else
printf 'mac = "%s"\n' "$ADAPTER" >> "$CONF"
fi
echo "set $CONF mac = $ADAPTER"
echo "done — boot MontaukOS; the device should reconnect without re-pairing."