Skip to main content BLE SMP Secure Pairing | IoT Worker

BLE SMP Secure Pairing

Many BLE field logs create a false impression: the device is already connected, so security should also already be done. That is not true. Permission errors, pairing failures, or pairing again after reconnecting usually mean that “connection exists” and “trust relationship established” have been mixed into one thing.

This article does not expand on Legacy Pairing, BR/EDR security mechanisms, or vendor SDK wrapper differences. It focuses only on the most common pairing, bonding, encryption, and privacy path.

If you isolate the security chain, the main path looks like this:

Establish LE connection -> exchange pairing capabilities and public keys -> verify identity using the selected method -> derive `MacKey/LTK` from `DHKey` -> start link-layer encryption -> optionally distribute `IRK/CSRK`

If you are doing embedded bring-up, keep these four questions in mind first before going into the details:

  • Can this pairing actually protect against MITM (Man-in-the-Middle)?
  • Why does the result still end up as Just Works even though security was requested?
  • Why does the device pair again after reconnect?
  • What exactly does RPA (Resolvable Private Address) solve, and what cost does it add to debugging and bonding?

You will keep seeing four key names. First remember what each one is for:

  • LTK (Long Term Key): the long-term key used for link-layer encryption
  • IRK (Identity Resolving Key): used to resolve resolvable private addresses
  • CSRK (Connection Signature Resolving Key): used for signed data
  • DHKey (Diffie-Hellman Key): the shared secret computed by ECDH

Overview

What layer security is filling in

From an embedded development perspective, LESC is not simply “one more handshake.” It mainly fills in these four things:

  • Whether both sides want to pair, and which side starts it
  • How each side confirms that the peer is really the peer and not a man in the middle
  • Which key the link layer will later use for encryption, and how that key is derived
  • How identity and privacy are recovered in later connections after bonding

You can think of the security establishment process as this diagram:

graph TB Conn[Establish LE connection] --> Feat[Exchange Pairing Request/Response] Feat --> PK[Exchange P-256 public keys] PK --> Auth[Verify identity using the selected method] Auth --> Derive[f5 derives MacKey/LTK] Derive --> Enc[LL starts AES-CCM encryption] Enc --> Dist[Optionally distribute IRK/CSRK]

The core takeaway is: LESC is not just “more SMP packets.” It separates identity verification from link-encryption key derivation. The first decides whether MITM is blocked; the second decides whether the link is actually encrypted afterward.

BLE security architecture

graph TB App[Application
GATT/GAP] --> SM[Security Manager] SM --> SMP[SMP protocol
L2CAP channel 0x0006] SMP --> HCI[HCI layer
Key-generation commands] HCI --> Controller[Controller
Link Layer encryption] SM --> Keys[Key management] Keys --> LTK[LTK
Link encryption] Keys --> IRK[IRK
Address resolution] Keys --> CSRK[CSRK
Data signing]

Protocol layers:

  • SMP (Security Manager Protocol): L2CAP channel 0x0006, responsible for pairing and key distribution
  • HCI (Host Controller Interface): provides ECDH P-256 key generation and link-layer encryption commands
  • Link Layer: uses LTK for AES-CCM encryption
  • GAP (Generic Access Profile): defines pairing modes and security levels

Design principles

ECDH P-256 key exchange:

  • Protocol mechanism: elliptic-curve Diffie-Hellman on the NIST P-256 curve
  • Key generation: DHKey = ECDH(SKa, PKb) or ECDH(SKb, PKa)
  • Temporary nature: a new ECDH key pair is generated for each pairing, which reduces the value of passive sniffing
  • HCI commands: LE_Read_Local_P256_Public_Key, LE_Generate_DHKey

Nonce-based anti-replay:

  • Protocol requirement: both sides generate 128-bit random numbers (Na/Nb)
  • Commitment scheme: the responder first sends Confirm = f4(PKb, PKa, Nb, Z) and reveals Nb only later
  • Protection effect: attackers cannot replay old Pairing Confirm or Random messages

Multiple authentication methods:

  • Depending on both sides’ IO capabilities, the stack chooses among:
    • Numeric Comparison: show a 6-digit code and let the user confirm it
    • Passkey Entry: one side enters the 6-digit code shown on the other side
    • Just Works: no user interaction, no MITM protection, but still protects against passive eavesdropping
    • OOB (Out of Band): exchange data over NFC, QR codes, or another side channel

Privacy:

  • Uses IRK to generate a Resolvable Private Address (RPA)
  • Addresses rotate every 15 minutes by default, with a configurable range of 1 to 3600 seconds
  • Resolution works by matching AES-128(IRK, prand) against the stored hash

LE Secure Connections Pairing Flow

sequenceDiagram participant I as Initiator participant R as Responder Note over I,R: Phase 1: establish LL connection opt optional R->>I: Security Request end I->>R: Pairing Request R->>I: Pairing Response Note over I,R: Phase 2: SMP authentication I<<->>R: LE Secure Connections Note over I,R: Phase 3: establish encrypted connection using the keys from Phase 2 I<<->>R: Key distribution (IRK/CSRK)

The pairing flow has three phases:

  • Phase 1 (Feature exchange): after the link-layer connection is up, Pairing Request/Response exchange IO capability, authentication requirements, and key-distribution policy
  • Phase 2 (Authentication and key generation): based on the negotiated method (Numeric Comparison, Passkey Entry, Just Works, or OOB), identity is verified and the LTK is generated
  • Phase 3 (Key distribution): the encrypted connection is established and other keys such as IRK and CSRK are distributed

LESC characteristic: Phase 2 uses ECDH P-256, and the LTK is derived by f5 instead of being distributed in Phase 3.

When pairing starts:

  1. Lazy Pairing: the most common mode. The device discovers services first, and pairing is triggered later by ATT Error: Insufficient Authentication (0x05) when a protected attribute is accessed.
  2. Proactive Pairing: pairing starts right after connection, without waiting for GATT discovery.
    • Central can send Pairing Request directly
    • Peripheral can send Security Request (0x0B) first
    • Common in HID or high-security devices

If the product requires “protected service immediately after the first connection,” proactive pairing is usually the better choice. If quick connection and low disturbance matter more, lazy pairing is usually the default.

Phase 1: Feature exchange

sequenceDiagram participant I as Initiator participant R as Responder Note over I,R: Phase 1: Feature Exchange I->>R: ① Pairing Request
[IO Cap, OOB, AuthReq, MaxKeySize, KeyDist] R->>I: ② Pairing Response
[IO Cap, OOB, AuthReq, MaxKeySize, KeyDist] Note over I,R: Negotiate pairing method
(Numeric Comparison / Passkey Entry / Just Works / OOB) I->>R: ③ Pairing Public Key
[PKa = (PKax, PKay)] (64 bytes) R->>I: ④ Pairing Public Key
[PKb = (PKbx, PKby)] (64 bytes) Note over I: Compute DHKey = ECDH(SKa, PKb) Note over R: Compute DHKey = ECDH(SKb, PKa)

Protocol message details

Pairing Request/Response (SMP opcode 0x01/0x02):

Byte layout:
┌──────────────────────────────────┐
│ Code (1 byte): 0x01/0x02         │  Opcode
├──────────────────────────────────┤
│ IO Capability (1 byte)           │  IO capability
├──────────────────────────────────┤
│ OOB Data Flag (1 byte)           │  OOB data flag
├──────────────────────────────────┤
│ AuthReq (1 byte)                 │  Authentication requirements
│  Bit 0: Bonding                  │  Whether bonding is enabled
│  Bit 2: MITM                     │  Whether MITM protection is required
│  Bit 3: SC (Secure Connections)  │  Whether LESC is used
│  Bit 4: Keypress                 │  Passkey input notification
│  Bit 5: CT2                      │  Cross-transport key derivation (ignored in pure BLE)
├──────────────────────────────────┤
│ Maximum Encryption Key Size (1)  │  7-16 (16 recommended)
├──────────────────────────────────┤
│ Initiator Key Distribution (1)   │  Initiator key distribution
│  Bit 0: EncKey (LTK)             │  LESC derives LTK via f5, so no distribution needed
│  Bit 1: IdKey (IRK)              │  Identity Information
│  Bit 2: Sign (CSRK)              │  Signing Information
│  Bit 3: Link                     │  BR/EDR Link Key (set to 0 in pure BLE)
├──────────────────────────────────┤
│ Responder Key Distribution (1)   │  Responder key distribution (same as above)
└──────────────────────────────────┘

Example Pairing Response for a pure BLE peripheral:

02 00 00 0d 10 06 06
Bluetooth Security Manager Protocol
    Opcode: Pairing Response (0x02)
    IO Capability: Display Only (0x00)
    OOB Data Flags: OOB Auth. Data Not Present (0x00)
    AuthReq: 0x0d, Secure Connection Flag, MITM Flag, Bonding Flags: Bonding
        ...0 .... = Keypress Flag: False
        .... 1... = Secure Connection Flag: True
        .... .1.. = MITM Flag: True
        .... ..01 = Bonding Flags: Bonding (0x1)
    Max Encryption Key Size: 16
    Initiator Key Distribution: 0x06, Signature Key (CSRK), Id Key (IRK)
        .... .1.. = Signature Key (CSRK): True
        .... ..1. = Id Key (IRK): True
    Responder Key Distribution: 0x06, Signature Key (CSRK), Id Key (IRK)
        .... .1.. = Signature Key (CSRK): True
        .... ..1. = Id Key (IRK): True

Protocol design points:

  • IO Capability: determines the authentication method
  • AuthReq.SC: must be 1 to use LESC
  • Maximum Encryption Key Size: negotiated key length, fixed at 16 for LESC
  • Key Distribution: decides which keys are distributed in Phase 3

If you only want to figure out why you did not get to LESC, look at these four fields first: AuthReq.SC, AuthReq.MITM, IO Capability, and OOB Data Flag. A lot of “security level is not what I expected” problems come from these fields.

What to check first during embedded bring-up

Symptom First judgment Fields or logs to check first
MITM was expected, but not achieved The authentication method degraded IO Capability, AuthReq.MITM, OOB Data Flag
LESC was expected, but did not happen One side did not enable SC AuthReq.SC, pairing request/response capability negotiation
It keeps not pairing after connection Protected access may not have been triggered yet ATT Error 0x05, Security Request
It keeps pairing again after bonding Keys were not stored correctly or identity was not restored bond storage, IRK/LTK recovery, Encryption Change

Pairing Public Key (SMP opcode 0x0C):

Byte layout:
┌──────────────────────────────────┐
│ Code (1 byte): 0x0C              │
├──────────────────────────────────┤
│ Public Key X (32 bytes)          │  X coordinate of P-256 public key
├──────────────────────────────────┤
│ Public Key Y (32 bytes)          │  Y coordinate of P-256 public key
└──────────────────────────────────┘

Protocol design: public key exchange happens in Phase 1, so later ECDH computation can run in parallel with user interaction.

Phase 2: Authentication and key generation

The core job of Phase 2 is to generate the LTK through the ECDH shared secret and the chosen authentication method. The timing differs significantly between methods:

  • Numeric Comparison / Just Works: one Confirm/Random exchange
  • Passkey Entry: 20 rounds of Confirm/Random exchange, one bit of the 6-digit code per round
  • OOB: one exchange, but the Confirm value includes OOB data

Numeric Comparison / Just Works

sequenceDiagram participant I as Initiator participant R as Responder Note over I,R: Phase 2, Stage 1: Authentication (Numeric Comparison) Note over I: Generate random number Na (128-bit) Note over R: Generate random number Nb (128-bit) R->>I: ⑤ Pairing Confirm
[Cb = f4(PKb, PKa, Nb, 0)] I->>R: ⑥ Pairing Random
[Na] R->>I: ⑦ Pairing Random
[Nb] Note over I: Verify Cb == f4(PKb, PKa, Nb, 0)
Compute Va = g2(PKa, PKb, Na, Nb)
Display 6-digit number = Va mod 1000000 Note over R: Compute Vb = g2(PKa, PKb, Na, Nb)
Display 6-digit number = Vb mod 1000000 Note over I,R: User confirms the numbers match
→ enter Stage 2

Three-layer security design:

1. Random (Nonce) - session freshness

  • Purpose: generate a unique session identifier for each pairing and prevent replay attacks
  • Mechanism: both sides generate 128-bit random numbers (Na/Nb)
  • Effect: even if public keys are reused, the LTK is different for each session because f5 binds the nonces

2. Confirmation - commitment scheme

  • Purpose: prevent the later sender from adjusting its nonce based on the earlier sender’s nonce
  • Mechanism: the responder first sends Cb = f4(PKb, PKa, Nb, 0) as a commitment, then reveals Nb
  • Effect: both nonces remain independently random, and the initiator can verify that the responder did not cheat

3. Va/Vb (6-digit code) - MITM protection

  • Purpose: prevent an attacker from replacing a public key, which is the core man-in-the-middle attack method
  • Mechanism: Va = Vb = g2(PKa, PKb, Na, Nb) mod 1000000, then the user compares the displayed values
  • Effect: if the public key was replaced, the computed codes differ and the user rejects pairing

Just Works difference:

  • The flow is exactly the same as Numeric Comparison
  • The difference is that it skips the code display and user confirmation step
  • ⚠️ No MITM protection, only passive eavesdropping protection (it cannot stop public-key replacement attacks)

If the device only has NoInputNoOutput, do not assume MITM protection is available. At that point the question becomes whether the business can tolerate that, or whether OOB is required.

Passkey Entry (20 iterations)

Protocol design: a 6-digit number (000000-999999) needs log2(1000000) ≈ 20 bits of entropy, so 20 rounds are used to verify each bit.

Purpose of the 20-iteration design - gradual disclosure

  • Verify the passkey bit by bit, and abort immediately if any bit fails, without revealing later bits
  • If MITM fails, the attacker gains at most 2 bits of information (success probability increases from 0.000001 to 0.000004, which is basically negligible)
sequenceDiagram participant I as Initiator participant R as Responder Note over I,R: Phase 2, Stage 1: Authentication (Passkey Entry) Note over I,R: Both sides enter or display the same 6-digit Passkey (P) opt User input stage (optional notification) Note over I,R: Pairing Keypress Notification
Notifies input progress end loop Iteration i (i = 1 to 20) Note over I: Generate Na[i] (128-bit)
ra[i] = (P >> (20-i)) & 0x01 Note over R: Generate Nb[i] (128-bit)
rb[i] = (P >> (20-i)) & 0x01 I->>R: ⑤ Pairing Confirm
[Ca[i] = f4(PKa, PKb, Na[i], ra[i])] R->>I: ⑥ Pairing Confirm
[Cb[i] = f4(PKb, PKa, Nb[i], rb[i])] I->>R: ⑦ Pairing Random
[Na[i]] Note over R: Verify Ca[i] == f4(PKa, PKb, Na[i], rb[i]) R->>I: ⑧ Pairing Random
[Nb[i]] Note over I: Verify Cb[i] == f4(PKb, PKa, Nb[i], ra[i]) end Note over I,R: 20 iterations complete
→ enter Stage 2

Iteration mechanism:

  • New nonce each round: Na[i] and Nb[i] are generated independently in each iteration
  • Bit extraction: ra[i] = rb[i] = (P >> (20-i)) & 0x01, extracting the i-th bit of the passkey from high bit to low bit
  • Commit-verify mechanism: first exchange Confirm commitments, then exchange Random disclosures, and the receiver verifies the peer’s Confirm value immediately
  • Pairing Keypress Notification: while the user is entering the passkey, input-progress notifications may optionally be sent (SMP 0x0E), but this does not affect the cryptographic computation

Difference from Numeric Comparison:

  • Passkey Entry: 20 iterations, new nonce each time, f4 uses ra[i]/rb[i] as the Z parameter (single bit)
  • Numeric Comparison: one exchange, f4 uses Z = 0

OOB authentication

Protocol design: authentication data is exchanged through an external channel (NFC, QR codes, BR/EDR, etc.), providing the highest security. OOB authentication has two independent stages:

Stage 1 (Authentication Stage 1): OOB data exchange, before BLE pairing
Stage 2 (SMP Pairing): Security Manager pairing flow

Key design differences:

  • The OOB stage uses ra/rb (128-bit random numbers)
  • The SMP stage uses Na/Nb (128-bit random numbers, same as Numeric Comparison)
  • In the OOB stage, the f4 function uses the same public key parameters: f4(PKa, PKa, ra, 0) instead of f4(PKa, PKb, Na, 0)
sequenceDiagram participant A as Device A (Initiating) participant B as Device B (Non-initiating) Note over A,B: Authentication Stage 1: Out of Band Note over A: Generate ra (128-bit)
Set peer rb = 0 (initial assumption) Note over B: Generate rb (128-bit)
Set peer ra = 0 (initial assumption) Note over A: Compute Ca = f4(PKa, PKa, ra, 0) Note over B: Compute Cb = f4(PKb, PKb, rb, 0) A->>B: ① OOB channel exchange
[Address A, ra, Ca] B->>A: ② OOB channel exchange
[Address B, rb, Cb] Note over A,B: ========== BLE connection established ========== Note over A,B: Security Manager Pairing begins Note over A: Step 5a: verify Cb
Adjust ra based on B's OOB data flag Note over B: Step 5b: verify Ca
Adjust rb based on A's OOB data flag Note over A: Generate Na (128-bit) Note over B: Generate Nb (128-bit) A->>B: ③ Pairing Random [Na] B->>A: ④ Pairing Random [Nb] Note over A,B: Enter Stage 2

Three OOB modes:

Mode ra value rb value Security Meaning
Bidirectional OOB != 0 != 0 ⭐⭐⭐ Both sides exchange data through OOB
Unidirectional OOB != 0 = 0 ⭐⭐ Only one side provides OOB data
No OOB = 0 = 0 Both sides have OOB data flag = not present, and the flow degrades to Just Works

Security impact:

  • The values of ra/rb directly affect the R parameter in DHKey Check
  • Unidirectional OOB still protects against passive eavesdropping, but it cannot prevent active MITM unless there is extra authentication
  • No OOB mode is equivalent to Just Works, with no MITM protection

If the product really needs “protection against active MITM” as a hard requirement and the device lacks suitable HMI, OOB is usually the only reliable option.

Comparison with other authentication methods

Feature Numeric Comparison Passkey Entry OOB
Phase 2 stages 1 stage 1 stage 2 stages (OOB + SMP)
Confirm/Random exchange 1 exchange 20 iterations 1 exchange in the OOB stage; no Confirm in SMP
f4 parameters f4(PKa, PKb, Na, 0) f4(PKa, PKb, Na[i], ra[i]) f4(PKa, PKa, ra, 0)
User interaction Confirm a 6-digit number Enter a 6-digit number Scan NFC / QR code
MITM protection ✅ (about 20-bit entropy) ✅ (20-bit entropy) ✅ (128-bit entropy)
Security level ⭐⭐ ⭐⭐ ⭐⭐⭐

Security advantage:

  • The attacker has to break both the BLE wireless channel and the OOB channel
  • ra/rb provide 128-bit entropy, which is much stronger than Numeric Comparison’s roughly 20-bit entropy
  • This is a better fit for high-security use cases such as payments, access control, and medical devices

Cryptographic functions (Stage 1)

f4 function (Confirm value calculation):

Ca/Cb = f4(U, V, X, Z) = AES-CMAC_X(U || V || Z)

Parameters:
- U: PKax (initiator public key X coordinate, 32 bytes)
- V: PKbx (responder public key X coordinate, 32 bytes)
- X: Na/Nb (16 bytes) — AES-CMAC key
- Z: 0x00 (Numeric Comparison) or ra/rb (Passkey Entry)

g2 function (6-digit code calculation):

Va/Vb = g2(U, V, X, Y) = AES-CMAC_X(U || V || Y) mod 2^32

6-digit code = Va mod 1000000

Parameters:
- U: PKax (initiator public key X coordinate, 32 bytes)
- V: PKbx (responder public key X coordinate, 32 bytes)
- X: Na — AES-CMAC key
- Y: Nb

Phase 2 Stage 2 and Phase 3: key derivation and distribution

Protocol design: Stage 2 (LTK computation and verification) and Phase 3 (key distribution) are independent of the authentication method. All authentication methods share the same flow.

Stage 2: LTK computation and DHKey Check

sequenceDiagram participant I as Initiator participant R as Responder Note over I,R: Phase 2, Stage 2: LTK computation and verification Note over I: Compute MacKey||LTK = f5(DHKey, Na, Nb, A, B) Note over R: Compute MacKey||LTK = f5(DHKey, Na, Nb, A, B) I->>R: ⑨ DHKey Check
[Ea = f6(MacKey, Na, Nb, rb, IOcapA, A, B)] Note over R: Verify Ea R->>I: ⑩ DHKey Check
[Eb = f6(MacKey, Nb, Na, ra, IOcapB, B, A)] Note over I: Verify Eb Note over I,R: Verification succeeds, both sides obtain the same LTK

f5 function (MacKey and LTK derivation):

T = AES-CMAC_SALT(DHKey)
    SALT = 0x6C888391AAF5A53860370BDB5A6083BE (fixed value)

MacKey = AES-CMAC_T(0x00 || "btle" || N1 || N2 || A1 || A2 || 0x00 0x01)
LTK    = AES-CMAC_T(0x01 || "btle" || N1 || N2 || A1 || A2 || 0x00 0x01)

Parameters:
- DHKey: ECDH shared key (X coordinate of the P-256 point, 32 bytes)
- N1: initiator nonce (Na, 16 bytes)
- N2: responder nonce (Nb, 16 bytes)
- A1: initiator Bluetooth address (7 bytes: 6-byte address + 1-byte type)
- A2: responder Bluetooth address (7 bytes)
- Counter: 0x00 (MacKey) or 0x01 (LTK)
- keyID: "btle" (4 ASCII bytes)
- Length: 0x0100 (256 bits, stored in little-endian as 0x00 0x01)

Output:
- MacKey: 128-bit (used for the f6 function)
- LTK: 128-bit (link encryption key)

f6 function (DHKey Check calculation):

Ea/Eb = f6(MacKey, N1, N2, R, IOcap, A1, A2)
      = AES-CMAC_MacKey(N1 || N2 || R || IOcap || A1 || A2)

Parameters:
- MacKey: 128-bit key derived by f5
- N1, N2: depend on the authentication method
  - Numeric Comparison / Just Works / OOB:
    - Initiator computes Ea: N1=Na, N2=Nb
    - Responder computes Eb: N1=Nb, N2=Na
  - Passkey Entry:
    - Initiator computes Ea: N1=Na20, N2=Nb20 (nonce from iteration 20)
    - Responder computes Eb: N1=Nb20, N2=Na20 (nonce from iteration 20)
- R: 3 bytes, depends on the authentication method:
  - Passkey Entry: 6-digit passkey
  - Numeric Comparison / Just Works: 0
  - OOB: combined value of ra (initiator) and rb (responder), depending on unidirectional/bidirectional OOB mode
- IOcap: 3 bytes (IO capabilities of both sides)
- A1, A2: Bluetooth addresses

**Notes**:

- The order of N1/N2 and A1/A2 is reversed between initiator and responder
- In OOB mode, the `R` value is determined by the adjusted `ra/rb` from Step 5a/5b

Phase 3: Key distribution (optional)

sequenceDiagram participant I as Initiator participant R as Responder Note over I,R: Phase 3: Key Distribution Note over I,R: LTK has already been derived by f5, so it does not need to be distributed alt Initiator distributes IRK I->>R: ⑪ Identity Information
[IRK] I->>R: ⑫ Identity Address Information
[Public address / static address] end alt Initiator distributes CSRK I->>R: ⑬ Signing Information
[CSRK] end alt Responder distributes IRK R->>I: ⑭ Identity Information
[IRK] R->>I: ⑮ Identity Address Information
[Public address / static address] end alt Responder distributes CSRK R->>I: ⑯ Signing Information
[CSRK] end Note over I,R: Pairing complete, enter bonded state

The easiest thing to confuse here is this: under LESC, LTK is not sent in Phase 3. Both sides derive it locally using f5. Phase 3 is mainly used to send IRK and CSRK, not the link-encryption key.


Key hierarchy and encryption mechanism

Key derivation flow

ECDH key exchange
DHKey (256-bit, X coordinate of P-256 point)
    ↓ [f5 function]
├─ MacKey (128-bit) → DHKey Check (f6)
└─ LTK (128-bit) → link-layer encryption
    ↓ [link layer established]
LTK-encrypted connection
IRK (128-bit, distributed in Phase 3) → RPA generation/resolution
CSRK (128-bit, distributed in Phase 3) → data signing

Protocol mechanism:

  • Encryption algorithm: AES-CCM (Counter with CBC-MAC, M=4)
  • Key: LTK (128-bit, used directly in LESC without extra derivation)
  • Nonce: Packet Counter (39-bit) || Direction (1-bit) || IV (64-bit)
  • MIC: 32-bit / 4 bytes (Link Layer MIC)

LESC encryption start flow:

sequenceDiagram participant C as Central participant P as Peripheral Note over C,P: Precondition: LTK has already been obtained through LESC pairing C->>P: LL_ENC_REQ
[Rand=0, EDIV=0, IV_C] Note over C: IV_C: 32-bit random number Note over P: Find LTK (by connection identity)
Generate IV_P (32-bit random number) P->>C: LL_ENC_RSP
[IV_P] Note over C,P: Build session IV together
IV = IV_C || IV_P (64-bit) P->>C: LL_START_ENC_REQ
(unencrypted) Note over P: Enable encryption mode
Wait for encryption response C->>P: LL_START_ENC_RSP
(encrypted) Note over C: Enable encryption before sending P->>C: LL_START_ENC_RSP
(encrypted) Note over P: Verify that decryption succeeded Note over C,P: 🔒 Bidirectional verification complete, encrypted connection established

LESC encryption mechanism:

  • Uses LTK directly as the AES-CCM key
  • ⚠️ LL_ENC_REQ uses Rand=0, EDIV=0 to mark an LTK derived by LESC
  • IV_C (Central’s IV) and IV_P (Peripheral’s IV) are each 32-bit and are concatenated into a 64-bit session IV
  • The IV changes on every connection, which provides session independence
  • LL_START_ENC handshake: Peripheral sends the request first (unencrypted), and both sides send a response once (encrypted) to verify each other
  • The nonce is built from Packet Counter + Direction + IV (see below)

AES-CCM encrypted format:

Before encryption:
┌──────────┬──────────────────┐
│ Header   │ Payload          │
└──────────┴──────────────────┘

After encryption:
┌──────────┬──────────────────┬─────────┐
│ Header   │ Encrypted Payload│ MIC(32) │
└──────────┴──────────────────┴─────────┘

CCM nonce (13 bytes, 104-bit):
┌────────────────┬───────────┬────────┐
│ Packet Counter │ Direction │   IV   │
│    (39-bit)    │  (1-bit)  │(64-bit)│
└────────────────┴───────────┴────────┘

Notes:
- Packet Counter: packet sequence number in the connection (increments, prevents replay)
- Direction: 0 = Central→Peripheral, 1 = Peripheral→Central
- IV: concatenation of IV_C || IV_P from LL_ENC_REQ/RSP (IV_C and IV_P are 32-bit each)

IRK and the privacy mechanism

Resolvable Private Address (RPA):

RPA (48-bit) = prand (24-bit) || hash (24-bit)
hash = AES-128(IRK, padding || prand)[0:23]

RPA update strategy:

  • RPA changes every 15 minutes (default 900 seconds, configurable range 1-3600 seconds) to prevent device tracking
  • prand is fully random, so it has no visible pattern
  • hash is computed with IRK, so only paired devices can resolve the RPA
  • An attacker who sees different RPAs at different times cannot tell whether they belong to the same device without the IRK

Privacy limits:

  • RPA only hides the device address: broadcast data (Complete Local Name, Service UUIDs) may still reveal the device identity
  • Broadcast fingerprinting: an attacker may correlate different RPAs over time by using broadcast interval, RSSI patterns, packet structure, and other features
  • Initial pairing problem: IRK can only be exchanged after pairing, so the first connection uses a public address or a static random address
  • Complete privacy solution: requires GAP Limited Discoverable Mode + dynamic broadcast data + hardware RNG support

For embedded developers, the most practical conclusion is:

  • RPA solves the problem of being tracked through a long-lived fixed address; it does not solve broadcast-content leakage
  • Once a device enables RPA, bring-up, allow-listing, background bonding, and packet-capture identification all become more complicated
  • If you do not have a clear privacy requirement, start by getting the bring-up path working with a stable address, and then decide whether to introduce RPA

CSRK data signing

ATT Signed Write: data-integrity verification without an encrypted connection (used less often in practice)

Authentication Signature = SignCounter (4 bytes) || MAC (8 bytes)
MAC = AES-CMAC(CSRK, Opcode || Handle || Value || SignCounter)[0:63]
  • ATT_SIGNED_WRITE_CMD (0xD2) carries a 12-byte signature
  • SignCounter increases monotonically (prevents replay)
  • ⚠️ It provides integrity only, not confidentiality (the payload is still sent in the clear)

Engineering decisions and debugging

IO Capabilities and authentication-method decisions

IO Capability Display capability Input capability Typical device
DisplayOnly Yes No Temperature sensor with screen
DisplayYesNo Yes Yes/No buttons Smart watch
KeyboardOnly No Keyboard Keyboard without a display
NoInputNoOutput No No Headphones, Beacon
KeyboardDisplay Yes Keyboard Smartphone, PC

Authentication-method decision order (from highest priority to lowest):

  1. OOB: if either side has OOB Data Flag = 0x01 (OOB data is provided), use OOB authentication (highest security)
  2. Numeric Comparison: use it when the two IO capability combinations satisfy the conditions for confirming a displayed number
  3. Passkey Entry: one side can type, and the other side can display the passkey
  4. Just Works: all other cases (⚠️ no MITM protection)

This is only a pragmatic engineering simplification. In a real implementation or validation pass, you should still revisit the complete decision table in the Core Spec based on the IO capability combination of both sides, so you do not lump DisplayOnly, DisplayYesNo, and KeyboardDisplay together.

Packet capture and debugging cheat sheet

These are the most common debugging points in real development:

Symptom Priority layer Common cause First place to check
Pairing does not happen for a long time after connection GATT / SMP The application has not accessed a protected attribute yet; the host strategy is Lazy Pairing Whether ATT Error: Insufficient Authentication (0x05) or Security Request appears
Sent Pairing Request but did not enter LESC SMP AuthReq.SC=0; one side does not support SC AuthReq in Pairing Request/Response
MITM was expected but the result is Just Works SMP The IO Capability combination does not qualify; OOB was not declared correctly IO Capability, OOB Data Flag, AuthReq.MITM
Pairing fails after public-key exchange SMP / HCI Random-number verification failed; DHKey Check failed; the user did not confirm the number Pairing Confirm, Pairing Random, DHKey Check, Pairing Failed
Pairing succeeds but business access still returns permission errors ATT / GAP A higher security level is actually required; the link was not truly encrypted; bonding information was not restored Encryption Change, attribute permissions, whether the link re-encrypts after reconnect
Peer address changes after reconnect GAP / Privacy The device enabled RPA Whether IRK has been distributed and whether the host successfully resolves RPA

When capturing packets, it is better to follow this order instead of staring at the cryptographic functions first:

  1. Confirm the trigger point first: is pairing triggered by access to a protected attribute, or does it start proactively after connection?
  2. Then check capability negotiation: SC, MITM, IO Capability, and Key Distribution in Pairing Request/Response
  3. Then check the authentication path: did it go through Public Key, Confirm/Random, and DHKey Check?
  4. Finally check the outcome: did you see Pairing Failed, Encryption Change, or Identity Information?

Common failure paths are worth remembering too:

  • You only see Security Request, but the Central never sends Pairing Request: usually the host policy did not respond, or the application disabled auto-pairing
  • It fails immediately after Pairing Random: first suspect Confirm verification failure, or a user-side Numeric Comparison / Passkey operation that was not completed
  • It fails after DHKey Check: first suspect inconsistent authentication data, such as Passkey/OOB data or address-parameter handling
  • It was already bonded, but it pairs again every time it reconnects: first check bond persistence, IRK/LTK storage, and identity restoration

Quick reference appendix

The main text is enough for most pairing, encryption, and reconnect debugging. The following sections only keep the commands and tools that are often checked during implementation.

Common HCI commands

Command Opcode Description Spec section
LE_Read_Local_P256_Public_Key 0x2025 Read the local P-256 public key Vol 4, Part E, § 7.8.36
LE_Generate_DHKey 0x2026 Generate the ECDH shared key Vol 4, Part E, § 7.8.37
LE_Enable_Encryption 0x2019 The master starts link-layer encryption (sends LL_ENC_REQ) Vol 4, Part E, § 7.8.24

Common tool entry points

  • Wireshark + BTVS: inspect HCI / L2CAP / SMP interactions
  • Android btsnoop_hci.log: capture HCI logs on the phone side
  • BlueZ btmon: inspect HCI events on Linux hosts
  • nRF Connect (Mobile): scan, connect, and pair for testing
  • Nordic nRF Sniffer: capture over-the-air packets

References

Standards

  • Bluetooth Core Specification

    • Volume 3, Part H: Security Manager Protocol (SMP)
    • Volume 6, Part B: Link Layer Specification (LL Encryption)
    • Volume 4, Part E: Host Controller Interface (HCI Commands)
  • Bluetooth SIG specification downloads

If you only want to get the main path straight first, start with these three spec entry points:

  • Vol 3, Part H: SMP message formats, authentication methods, f4/f5/f6/g2
  • Vol 6, Part B: link-layer encryption start and LL_ENC_REQ/LL_START_ENC_*
  • Vol 4, Part E: host-side HCI commands and events, useful for matching logs

Further reading

  • BLE Architecture Overview: return to the layering boundaries and a unified debugging entry point
  • BLE GAP Analysis: return to address types, security levels, and the Privacy entry point
  • BLE Link Layer / PHY: return to the connection events, retransmission, and air-interface stability after encrypted link setup
  • BLE GATT/ATT Analysis: continue with how permission errors map to concrete attribute access and error codes