Created
October 14, 2022 15:08
-
-
Save ayufan/a287300f63d0fb2ee6977469f96e71e2 to your computer and use it in GitHub Desktop.
Steam Deck dual-boot copy Windows Bluetooth Paring
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
if [[ $# -ne 1 ]]; then | |
echo "usage: $0 <windows-partition>" | |
echo " ex.: $0 /dev/nvme0n1p10" | |
echo " ex.: $0 /dev/mmcblk0p3" | |
exit 1 | |
fi | |
if [[ $(id -u) != 0 ]]; then | |
echo "run as root" | |
sudo -- "$0" "$@" | |
exit 1 | |
fi | |
set -eo pipefail | |
which chntpw || pacman -Sy chntpw | |
mkdir -p /mnt/windows | |
umount /mnt/windows || true | |
mount /dev/nvme0n1p10 /mnt/windows | |
trap "cd /; umount /mnt/windows" EXIT | |
cd /mnt/windows/Windows/System32/config | |
changed="" | |
get_value() { | |
printf "%s \ControlSet001\Services\BTHPORT\Parameters\Keys\%s\nq\n" "$1" "$2" | | |
chntpw -e SYSTEM | | |
grep ":0000" | |
} | |
ls_devices() { | |
printf "ls \ControlSet001\Services\BTHPORT\Parameters\Keys\%s\nq\n" "$1" | | |
chntpw -e SYSTEM | | |
grep '^\s*<[a-z0-9]*>\s*$' | |
} | |
filter_hex() { | |
set -- "$@" | |
for arg; do | |
if [[ ${arg} =~ ^[0-9a-fA-F][0-9a-fA-F]$ ]]; then | |
echo "$arg" | |
fi | |
done | |
} | |
get_hex() { | |
local value=($(filter_hex $(get_value hex "$1"))) | |
# join with : | |
local IFS="${2}" | |
echo "${value[*]}" | |
} | |
get_hex_r() { | |
local hexstring="" | |
for i in $(filter_hex $(get_value hex "$@")); do | |
hexstring="$i$hexstring" | |
done | |
echo "$hexstring" | |
} | |
get_dec() { | |
local hexstring=$(get_hex_r "$1") | |
if [[ -n "$hexstring" ]]; then | |
printf "%u" "0x${hexstring}" | |
fi | |
} | |
# chntpw -e SYSTEM | |
# 16 3 REG_BINARY <LTK> | |
# 4 4 REG_DWORD <KeyLength> 16 [0x10] | |
# 8 b REG_QWORD <ERand> | |
# 4 4 REG_DWORD <EDIV> 56572 [0xdcfc] | |
# 16 3 REG_BINARY <IRK> | |
# 8 b REG_QWORD <Address> | |
# 4 4 REG_DWORD <AddressType> 1 [0x1] | |
# 16 3 REG_BINARY <CSRK> | |
# 4 4 REG_DWORD <OutboundSignCounter> 0 [0x0] | |
# 4 4 REG_DWORD <MasterIRKStatus> 1 [0x1] | |
# 4 4 REG_DWORD <AuthReq> 45 [0x2d] | |
# git config -f /var/lib/bluetooth/14\:D4\:24\:70\:19\:B0/D4\:61\:2F\:82\:C2\:1B/info --list | |
# general.name=M720 Triathlon | |
# general.appearance=0x03c2 | |
# general.addresstype=static | |
# general.supportedtechnologies=LE | |
# general.trusted=true | |
# general.blocked=false | |
# general.wakeallowed=true | |
# general.services=00001800-0000-1000-8000-00805f9b34fb | |
# connectionparameters.mininterval=6 | |
# connectionparameters.maxinterval=9 | |
# connectionparameters.latency=44 | |
# connectionparameters.timeout=216 | |
# identityresolvingkey.key=88A1BA3D0F64994C59A9058545901A6E | |
# localsignaturekey.key=9D07AAF0395CAA4D91AE97DA1D7A0639 | |
# localsignaturekey.counter=0 | |
# localsignaturekey.authenticated=false | |
# longtermkey.key=ED235B5197B3673E12BE9F9D2AF019EB | |
# longtermkey.authenticated=0 | |
# longtermkey.encsize=16 | |
# longtermkey.ediv=42387 | |
# longtermkey.rand=16144039604717484547 | |
# deviceid.source=2 | |
# deviceid.vendor=1133 | |
# deviceid.product=45077 | |
# deviceid.version=9 | |
to_hive_key() { | |
local value="$1" | |
value="${value//:}" | |
value="${value,,}" | |
echo "$value" | |
} | |
from_hive_key() { | |
local value="$1" | |
local ret=() | |
while [[ -n "$value" ]]; do | |
ret+=("${value:0:2}") | |
value="${value:2}" | |
done | |
local IFS=":" | |
echo "${ret[*]^^}" | |
} | |
update_ini() { | |
local hiveType="$1" | |
local hivePath="$2" | |
local configPath="$3" | |
local iniPath="$4" | |
local iniValue=$(git config -f "$configPath" --get "$iniPath") | |
if [[ -z "$iniValue" ]]; then | |
return 0 | |
fi | |
local hiveValue=$(get_$hiveType "$hivePath") | |
if [[ -z "$hiveValue" ]]; then | |
return 0 | |
fi | |
if [[ "$hiveValue" == "$iniValue" ]]; then | |
echo ">> The '$iniPath' ($hivePath) is not changed." | |
return 0 | |
fi | |
echo ">> Update '$iniValue' ($iniPath) to '$hiveValue' ($hivePath)" | |
git config -f "$configPath" --replace-all "$iniPath" "$hiveValue" | |
changed="$changed\n$hivePath" | |
} | |
find_device_by_irk() { | |
local uniqueID="${1}" | |
local queryIRK="${2}" | |
if [[ -z "$queryIRK" ]]; then | |
return 1 | |
fi | |
for configPath in /var/lib/bluetooth/$uniqueID/*/info; do | |
local deviceID=$(dirname "$configPath") | |
deviceID=$(basename "$deviceID") | |
local deviceIRK=$(git config -f "$configPath" --get "IdentityResolvingKey.Key") | |
if [[ "$deviceIRK" == "$queryIRK" ]]; then | |
echo "$deviceID" | |
return 0 | |
fi | |
done | |
return 1 | |
} | |
update_device() { | |
local configPath="/var/lib/bluetooth/$1/$2/info" | |
local hivePath="$(to_hive_key "$1")\\$(to_hive_key "$2")" | |
if [[ ! -e "$configPath" ]]; then | |
echo "The '$configPath' does not exist." | |
return | |
fi | |
if [[ ! -e "$configPath.bak" ]]; then | |
cp "$configPath" "$configPath.bak" | |
fi | |
update_ini hex_r "${hivePath}\\IRK" "$configPath" IdentityResolvingKey.Key | |
update_ini hex "${hivePath}\\CSRK" "$configPath" LocalSignatureKey.Key | |
update_ini hex "${hivePath}\\LTK" "$configPath" LongTermKey.Key | |
update_ini dec "${hivePath}\\KeyLength" "$configPath" LongTermKey.EncSize | |
update_ini dec "${hivePath}\\EDIV" "$configPath" LongTermKey.EDiv | |
update_ini dec "${hivePath}\\ERand" "$configPath" LongTermKey.Rand | |
update_ini hex "${hivePath}" "$configPath" LinkKey.Key | |
} | |
for uniqueID in /var/lib/bluetooth/*:*; do | |
uniqueID=$(basename "$uniqueID") | |
hiveUniqueID="$(to_hive_key "$uniqueID")" | |
echo "Cloning existing devices with IRK..." | |
ls_devices "$hiveUniqueID" | while read hiveDeviceID; do | |
hiveDeviceID="${hiveDeviceID//[ <>]}" | |
deviceID="$(from_hive_key "$hiveDeviceID")" | |
if [[ -e "/var/lib/bluetooth/$uniqueID/$deviceID/info" ]]; then | |
continue | |
fi | |
queryIRK=$(get_hex_r "$hiveUniqueID\\$hiveDeviceID\\IRK") | |
if ! otherDeviceID=$(find_device_by_irk "$uniqueID" "$queryIRK"); then | |
continue | |
fi | |
if [[ "$deviceID" == "$otherDeviceID" ]]; then | |
continue | |
fi | |
echo ">> Clonning '$otherDeviceID' to '$deviceID'..." | |
cp -rv "/var/lib/bluetooth/$uniqueID/$otherDeviceID" "/var/lib/bluetooth/$uniqueID/$deviceID" | |
done | |
echo | |
for deviceID in /var/lib/bluetooth/$uniqueID/*:*; do | |
deviceID=$(basename "$deviceID") | |
echo "Updating $deviceID..." | |
update_device "$uniqueID" "$deviceID" | |
echo | |
done | |
done | |
if [[ -n "$changed" ]]; then | |
echo -e "Changed: $changed" | |
systemctl restart bluetooth.service | |
systemctl restart graphical.target | |
fi | |
echo Done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do this before running the script:
This requires that: