-
-
Notifications
You must be signed in to change notification settings - Fork 192
AirPod Keys
Note (CAPod 5.0+): On phones with the updated Android Bluetooth stack — confirmed on Pixel devices on Android 16 / 17 builds as of April 2026, with other OEMs (Samsung, Xiaomi, OnePlus, etc.) rolling out the fix at their own pace — CAPod usually retrieves the identity and encryption keys automatically via an AAP connection, so no PC extraction is needed. Manual extraction (this guide) is still useful when: (1) your phone can't establish an AAP connection yet, or (2) auto-retrieval doesn't complete for your specific device profile.
AirPods regularly broadcast a status message using Bluetooth Low Energy (BLE), and this message contains valuable information.
Each broadcast includes the public address of the AirPods, but to maintain privacy, AirPods frequently rotate this address to prevent tracking. The broadcast message consists of two parts: a public and an encrypted section.
-
Public part: This is the part CAPod uses by default, as it contains publicly accessible information like battery status, charging state, etc.
-
Encrypted part: To access the encrypted data (like more precise battery levels), an encryption key is required. This key, along with an address key, is exchanged when the AirPods are paired with a new device.
The two key pieces of information that we need are:
-
Identity Resolving Key (IRK): This key links the randomized Bluetooth addresses broadcasted by the AirPods. It allows CAPod to uniquely identify your AirPods, eliminating the need for heuristics or signal strength checks. This makes detection much more reliable, especially when there are multiple AirPods nearby.
-
Encryption Key: This key decrypts the private part of the broadcasted message, allowing CAPod to retrieve more accurate battery levels (e.g., 1% steps instead of 10%).
At present, there’s no direct method to retrieve the keys on an Android device. However, the IRK and encryption key remain the same, regardless of the device the AirPods are paired with. This means we can retrieve the keys by pairing the AirPods with a device that supports extracting them.
It's possible to retrieve the keys on any platform by using a virtual machine:
Thanks to @akshaynexus ❤️
airpod_key_retrieval.sh
#!/usr/bin/env bash
set -u
SYSTEM_KEYCHAIN="/Library/Keychains/System.keychain"
usage() {
cat <<'EOF'
Usage:
./get_airpod_keys.sh
./get_airpod_keys.sh --address AA:BB:CC:DD:EE:FF
Extracts AirPods / Beats MagicAcc keys from the macOS MobileBluetooth
Keychain item for CAPod:
- MagicAccIRK -> CAPod Identity key
- MagicAccEncKey -> CAPod Encryption key
macOS may show Keychain permission prompts. These keys are sensitive.
EOF
}
normalize_mac() {
printf '%s\n' "$1" | tr '[:lower:]' '[:upper:]'
}
discover_bluetooth_addresses() {
system_profiler SPBluetoothDataType -json 2>/dev/null \
| awk -F '"' '/"device_address"[[:space:]]*:/ { print $4 }' \
| sort -u
}
lookup_mobilebluetooth_item() {
local address="$1"
security find-generic-password \
-a "$address" \
-s MobileBluetooth \
-g "$SYSTEM_KEYCHAIN" 2>&1
}
extract_plist_value() {
local key="$1"
# security(1) prints the password plist as an escaped one-line string, so
# this intentionally parses the <key>...</key><string>...</string> pattern.
sed -nE "s/.*<key>${key}<\\/key>[^<]*<string>([^<]+)<\\/string>.*/\\1/p"
}
print_keys_for_address() {
local address="$1"
local output
local irk
local enc_key
local local_address
output="$(lookup_mobilebluetooth_item "$address")"
irk="$(printf '%s\n' "$output" | extract_plist_value "MagicAccIRK")"
enc_key="$(printf '%s\n' "$output" | extract_plist_value "MagicAccEncKey")"
local_address="$(printf '%s\n' "$output" | extract_plist_value "LocalAddress")"
if [[ -z "$irk" || -z "$enc_key" ]]; then
return 1
fi
printf 'Bluetooth address: %s\n' "$address"
if [[ -n "$local_address" ]]; then
printf 'Local Mac address: %s\n' "$local_address"
fi
printf 'CAPod Identity key / MagicAccIRK:\n%s\n\n' "$irk"
printf 'CAPod Encryption key / MagicAccEncKey:\n%s\n' "$enc_key"
printf '%s\n' '---'
return 0
}
main() {
local addresses=()
local found=0
while [[ $# -gt 0 ]]; do
case "$1" in
--address|-a)
if [[ $# -lt 2 ]]; then
usage >&2
exit 2
fi
addresses+=("$(normalize_mac "$2")")
shift 2
;;
--help|-h)
usage
exit 0
;;
*)
printf 'Unknown argument: %s\n\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
if [[ ${#addresses[@]} -eq 0 ]]; then
while IFS= read -r address; do
[[ -n "$address" ]] && addresses+=("$(normalize_mac "$address")")
done < <(discover_bluetooth_addresses)
fi
if [[ ${#addresses[@]} -eq 0 ]]; then
printf 'No Bluetooth device addresses found. Try --address AA:BB:CC:DD:EE:FF\n' >&2
exit 1
fi
for address in "${addresses[@]}"; do
if print_keys_for_address "$address"; then
found=1
fi
done
if [[ "$found" -eq 0 ]]; then
printf 'No MobileBluetooth entries with MagicAccIRK/MagicAccEncKey were found.\n' >&2
printf 'Make sure the AirPods are paired with this Mac, then try again.\n' >&2
exit 1
fi
}
main "$@"Via https://github.com/d4rken-org/capod/pull/597
Might not work on all macOS versions. This example is from a Mac Air (M1) running Sonoma 14.6.1.
Manual guide
- Pair the AirPods. They should show up in the list of Bluetooth devices.
- Find the Bluetooth address. To do this you can hold the
Optionkey and then click the Bluetooth icon in the Macbook statusbar. Find your AirPods and remember their address. Mine start with9C:FC:....
- Open the MacOS KeyChain app. Here we will look for the keys related to our paired AirPods.
- Find the entry related to your paired Airpods. It should be called
MobileBluetooth, and is usually under theiCloudtab. There may be multipleMobileBluetoothentries. You are looking for the entry that contains the Bluetooth address from step 2 as "Account" name.
- Open that entry. At the bottom is an unticked checkbox called
Show password. Click that. You will be prompted 3 times (yes actually 3 times, "Just MacOS Things ™️) to enter your password. If all done correctly, the text field next to the checkbox should show<?xml version="1.0"...(it's a lot of text in a very tiny textfield).
- Copy the text from the text field in step 5. We are interested in the values of
MagicAccEncKey(encryption key) andMagicAccIRK(address key). The full copied text should look similar to this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LinkKey</key>
<string>AA-D4-58-F6-53-45-61-BC-CA-BD-41-C1-EA-AA-63-A5</string>
<key>LinkKeyType</key>
<string>UnAuthenticated</string>
<key>LocalAddress</key>
<string>AA:A6:F6:FF:FA:17</string>
<key>MagicAccEncKey</key>
<string>3A-9C-81-57-E6-44-7F-F2-FB-8E-07-63-6C-99-E1-29</string>
<key>MagicAccHint</key>
<string>8A-6A-FE-91-62-46-AE-31-AB-C6-F6-FC-A5-D9-DC-96</string>
<key>MagicAccIRK</key>
<string>71-44-65-1E-E2-CA-D1-26-F2-4E-20-EE-3E-CC-DE-77</string>
<key>MagicAccKey</key>
<string>F1-E6-7F-23-B5-21-22-CE-81-19-72-EB-66-37-D0-BE</string>
<key>MagicAccRachet</key>
<string>00-00-00-01-02</string>
</dict>
</plist>- Open CAPod (v2.17.0-rc0 or later) and go to Settings>Settings. Enter the value of
MagicAccIRKin the "Identity key" setting. Enter the value of and the value ofMagicAccEncKeyinto theEncryption Keysetting
- If all done correctly, your Airpods should show a little key icon in the dashboard.
You can retrieve the keys via this custom python script, thanks to @kavishdevar ❤️
airpod_key_retrieval.py
import sys
import socket
PROXIMITY_KEY_TYPES = {
0x01: "IRK",
0x04: "ENC_KEY",
}
def parse_proximity_keys_response(data):
if len(data) < 7 or data[4] != 0x31:
return None
key_count = data[6]
keys = []
offset = 7
for _ in range(key_count):
if offset + 3 >= len(data):
break
key_type = data[offset]
key_length = data[offset + 2]
offset += 4
if offset + key_length > len(data):
break
key_bytes = data[offset:offset + key_length]
keys.append((PROXIMITY_KEY_TYPES.get(key_type, f"TYPE_{key_type:02X}"), key_bytes))
offset += key_length
return keys
def hexdump(data):
return " ".join(f"{b:02X}" for b in data)
def main():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <MAC>")
sys.exit(1)
bdaddr = sys.argv[1]
PSM = 0x1001
handshake = bytes.fromhex("00 00 04 00 01 00 02 00 00 00 00 00 00 00 00 00")
key_req = bytes.fromhex("04 00 04 00 30 00 05 00")
sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
sock.connect((bdaddr, PSM))
sock.send(handshake)
sock.send(key_req)
try:
while True:
pkt = sock.recv(1024)
keys = parse_proximity_keys_response(pkt)
if keys is not None:
print("Proximity Keys:")
for name, key_bytes in keys:
print(f" {name}: {hexdump(key_bytes)}")
break
finally:
sock.close()
if __name__ == "__main__":
main()Via https://github.com/d4rken-org/capod/issues/290#issuecomment-3046151031
Similarly, you can retrieve the keys on Windows using the Home Assistant Private BLE Device Integration.