Status: v0, reviewed by the PMC. Drafted by the ASF Security team from public artefacts (the in-repo source layout under
nimble/host,nimble/controller,nimble/host/services, andsyscfg.yml) against the Scovetta rubric, plus general Bluetooth-LE domain knowledge. Szymon Janc (Mynewt PMC) reviewed it on 2026-07-24 and answered §14 Q1–Q14; those answers are folded into the body. Provenance tags: (documented) / (maintainer) / (inferred), with every load-bearing (inferred) claim routed to a numbered §14 question. What remains (inferred) is noted as still-open in §14.
- Project: Apache NimBLE — a full Bluetooth Low Energy (BLE) host + controller stack.
- Repository / commit:
apache/mynewt-nimble,master@793cb1612088(2026-06-09). - Drafted: 2026-06-13, ASF Security team (v0 draft from public artefacts).
- Companion repos this round:
apache/mynewt-core(the RTOS NimBLE runs on, and which provides the management transport that may sit on top of a BLE L2CAP channel) andapache/mynewt-mcumgr(uses a BLE connection as one SMP transport — so NimBLE is the carrier, mcumgr the payload). - What triggers a revision: a new Bluetooth spec feature (new PDU types, a new pairing method, a new LL control procedure); a change to the Security Manager's supported association models; a change to the HCI host/controller split; or a new GATT service shipped in-tree. Internal refactors that do not change a wire-facing parser or the SM cryptography do not.
NimBLE is the BLE protocol stack a firmware author links into a
device. It is not an application; it is the machinery that turns radio
PDUs into connection/GATT events and back. It can be built as a
combined host+controller, host-only (talking to an external
controller over HCI), or controller-only (exposing HCI to an external
host). (documented: nimble/{host,controller,transport} layout; §14 Q1)
| Family | Source | What it parses / does | Radio-trust tier |
|---|---|---|---|
| Controller / Link Layer (LL) | nimble/controller/src/ble_ll_* |
Lowest level: advertising, scanning, connection establishment, LL control PDUs, channel selection, encryption (ble_ll_crypto), RPA resolution, whitelist, DTM. |
Tier 0 — raw radio. Processes PDUs from anyone in range before any connection or authentication exists. |
| HCI transport | nimble/transport, ble_hs_hci*, ble_ll_hci* |
The command/event boundary between host and controller. | Boundary only when split; in a combined build it is internal. |
| Host — L2CAP | ble_l2cap* (+ _coc, _sig) |
Channel multiplexing, fragmentation/reassembly, connection-oriented channels, signalling. | Tier 1 — connected peer, pre- or post-encryption. |
| Host — ATT / GATT | ble_att*, ble_gattc, ble_gatts |
The attribute protocol server/client and GATT layer above it. | Tier 1; the server parses every ATT request from the peer. |
| Host — GAP + advertising data | ble_gap, ble_hs_adv |
Connection/advertisement state machine; parses advertising/scan-response data. | Tier 0/1. |
| Security Manager (SM) | ble_sm, ble_sm_sc, ble_sm_lgcy, ble_sm_alg, ble_sm_cmd |
Pairing, key generation, MITM protection, LE Secure Connections (ECDH P-256) and legacy pairing. This is where NimBLE's actual security properties live. | Tier 1; pairing PDUs are attacker-reachable; the output is the encryption/authentication of the link. |
| Privacy | ble_hs_pvcy, ble_ll_resolv |
Resolvable Private Address (RPA) generation/resolution. | Tier 0. |
| In-tree GATT services | nimble/host/services/{ans,bas,bleuart,dis,gap,ias,ipss,lls,tps} |
Example/standard GATT services. | Application-tier; mostly demo/standard profiles (§3). |
- Not the application's GATT logic. NimBLE provides the protocol and the mechanism to require encryption/authentication on a characteristic; which characteristics require it is the application's choice (§10).
- Not the radio PHY or the RF hardware (
drivers/are thin HW shims). - Not key storage at rest — bond/key persistence uses the host OS's store
(
apache/mynewt-coreor the porting layer). (inferred — §14 Q2)
- Application GATT services and their access-control choices. If an
app exposes a sensitive characteristic with no encryption/auth
requirement, that is the app's decision, not a NimBLE defect (§10).
The in-tree
host/services/*are standard/demo profiles in this same category. - The Bluetooth specification itself. Where the spec mandates an unauthenticated or weak-by-modern-standards behaviour (open advertising, passive-eavesdroppable pre-encryption traffic, "Just Works" pairing with no MITM protection, legacy pairing), NimBLE conforming to the spec is not a NimBLE vulnerability (§11a).
- The radio PHY / RF front-end / hardware and the timing of the link — jamming, RF interference, and physical-layer attacks are out.
- The host RTOS, its scheduler, and key-at-rest storage — modelled
in
apache/mynewt-core/ the porting layer. - mcumgr / newtmgr management payloads that ride on top of a BLE
L2CAP channel — NimBLE is the carrier; the SMP server is modelled in
apache/mynewt-mcumgrandapache/mynewt-core'smgmt/. - Physical / invasive and side-channel attacks on the device.
- Supply-chain / build hygiene.
The trust boundary is the antenna. What differs from a wired stack is that there are graduated radio-trust tiers, and the same code base processes input at every tier:
Tier 0 RAW RADIO (anyone in range, NO connection, NO auth)
adv / scan-req / scan-rsp / connect-ind / LL-control PDUs
| ble_ll_* (controller) ble_hs_adv (host, adv-data parse)
v
Tier 1 CONNECTED PEER (a link exists; may be UNENCRYPTED)
L2CAP (+reassembly) -> ATT requests -> GATT ; SM pairing PDUs
| ble_l2cap* / ble_att_svr / ble_sm*
v
[ Security Manager pairing ] --(if an *authenticated* method used)-->
v
Tier 2 ENCRYPTED + (optionally) AUTHENTICATED LINK
AES-CCM link encryption (ble_ll_crypto) ; bonded keys
|
v
application GATT characteristics (permissions are the APP's choice, §10)
HCI boundary (only in split host/controller builds):
host <== HCI events/ACL ==> controller (trusted in combined builds)
Two consequences: (1) most NimBLE code runs at Tier 0/1 against fully untrusted input — memory safety there is the core obligation; (2) the transition from Tier 1 to Tier 2 is NimBLE's security product — the correctness of the Security Manager's pairing crypto is what authenticates and encrypts the link.
- Embedded, single address space, no MMU/process isolation. A memory-safety bug in any PDU parser is a whole-device compromise. (inferred — §14 Q3)
- In a combined build, host and controller trust each other over the internal HCI; in a split build, the HCI transport is itself a trust boundary and each side must tolerate a malformed/ hostile peer across it. (inferred — §14 Q1)
- The RNG is the Bluetooth controller's, not NimBLE's. On the host
side randomness comes from the HCI
LE Randcommand (ble_hs_hci_rand(),nimble/host/src/ble_hs_hci_util.c), which the Bluetooth specification requires to be FIPS-compliant; parts of the LE SC algorithm code may use a platform TRNG directly where configured to. On the controller side this typically sources the SoC's hardware RNG. Its quality is therefore the controller/SoC's property, backed by the spec — not something NimBLE implements. (maintainer — §14 Q4) - Bond/key storage is the integrator's, not NimBLE's. Persistence
happens through callbacks registered in
ble_hs_cfg, so the backend may keep keys in RAM, flash, PSA, or anywhere else. NimBLE ships one implementation (nimble/host/store/config, on Mynewtsys/config); applications may substitute their own. Confidentiality and integrity of that store are assumed, and are outside core-host. (maintainer — §14 Q2)
- Role build (combined / host-only / controller-only) changes which parsers are present and whether HCI is a boundary. (documented: layout)
- Supported pairing methods / SM features are syscfg-selectable
(LE Secure Connections vs legacy, bonding, MITM, OOB, key-distribution
set). The security posture of the link depends on which are enabled
and which the application requests. By default only legacy pairing
is enabled (
nimble/host/syscfg.yml) — so a build that wants LE Secure Connections must turn it on deliberately. (maintainer — §14 Q5) - Crypto backend. Tinycrypt support was removed; the only supported backend is mbedTLS 3.6. A move to the PSA crypto API is planned. NimBLE claims no constant-time / side-channel posture of its own — that rests on the crypto library. (maintainer — §14 Q13)
- Privacy (RPA) on/off and the resolving-list size. (documented:
ble_hs_pvcy,ble_ll_resolv) - Maximum MTU / number of connections / L2CAP COC buffers bound reassembly and allocation. (inferred — §14 Q6)
- Direct Test Mode (
ble_ll_dtm) is a radio-hardware test feature, typically used in test labs for emission approval. It is a controller feature with a thin host layer for application access. It can be compiled out, and it can also be reachable in production if the application allows it — so its presence is a per-build integrator decision rather than something the library forces either way. (maintainer — §14 Q7)
Every radio PDU is attacker-controllable to anyone within range. The question for each surface is only which tier it is reachable at.
| Surface | Source code | Reachable by | Must enforce |
|---|---|---|---|
| Advertising / scan-response data | ble_hs_adv, ble_ll_adv, ble_ll_scan |
anyone in range, no connection | memory-safe parse of arbitrary/truncated/over-length AD structures; no OOB on malformed length fields. NimBLE ignores/rejects malformed PDUs, but passes advertising data up to the application WITHOUT LTV inspection — parsing helpers exist, the application is not obliged to use them. So AD-parsing safety is shared with the integrator (maintainer — §14 Q8) |
connect-ind / connection setup |
ble_ll_conn, ble_gap |
anyone in range | bounded state allocation; safe handling of malformed connection parameters (inferred — §14 Q8) |
| LL control PDUs | ble_ll_ctrl |
a connected peer (Tier 1, pre-encryption) | safe handling of every control opcode incl. length/feature/PHY-update, version, and encryption-start procedures (inferred — §14 Q9) |
| HCI commands/events | ble_ll_hci*, ble_hs_hci* |
the HCI peer (split build) or internal | length/parameter validation on every event/command (inferred — §14 Q1) |
| L2CAP packets + reassembly | ble_l2cap, _coc, _sig |
connected peer | reassembly bounds (no overflow on a lying L2CAP length); COC credit/flow safety (inferred — §14 Q10) |
| ATT requests | ble_att_svr |
connected peer (pre/post-encryption per char permission) | bounds on every ATT opcode (READ/WRITE/PREPARE-WRITE queue, FIND, READ-BLOB offset); the prepared-write queue is a classic resource sink (inferred — §14 Q11) |
| SM pairing PDUs | ble_sm* |
connected peer, pre-encryption | correct pairing state machine; validate the peer's ECDH public key (reject invalid-curve / zero / debug keys); no key/nonce reuse; constant-time where it matters (inferred — §14 Q12, the highest-value crypto question) |
- Reassembly and allocation are bounded by the configured memory pools
—
msysor a dedicated pool (§5a). Regardless of configuration, a peer must not be able to write past the provided buffer. But note the maintainer's caveat: depending on the layer or protocol, length fields are not guaranteed to stay within bounds and must be checked. Missing length validation on a radio-reachable path is therefore squarely in-model. (maintainer — §14 Q6) - L2CAP COC: if a peer sends more packets than its credits allow, the channel is terminated. (maintainer — §14 Q10)
- ATT prepared-write queue: bounded by a configurable limit and a
timeout, after which the connection is terminated and resources freed
(
BLE_ATT_SVR_MAX_PREP_ENTRIES,BLE_ATT_SVR_QUEUED_WRITE_TMO). Permissions are enforced per attribute. (maintainer — §14 Q11) - No rate guarantee against a flooding/jamming peer (§9). Connection-flood and resource-exhaustion DoS is out of scope unless it is an actual resource leak — it depends on system configuration and application policy. (maintainer — §14 Q14)
| Actor | In scope? | Capabilities |
|---|---|---|
| Anyone in radio range | yes — primary | passively sniff all pre-encryption traffic; inject/spoof advertising; initiate connections; send malformed PDUs at every layer; attempt/abort pairing; replay. |
| Active MITM during pairing | yes | sit between two pairing devices. NimBLE's defence is an authenticated SM association model (Passkey / Numeric Comparison / OOB); "Just Works" provides none by spec (§9, §11a). |
| A bonded/paired peer turned malicious | conditionally | has a valid LTK; can send well-formed encrypted requests. In-model only for memory safety / bounds in the request parsers — not for "a bonded peer can use the services it was granted." |
| HCI peer (split builds) | yes | a malicious external controller/host across the HCI transport. |
| Application firmware author | out of scope | trusted; chooses pairing requirements and characteristic permissions. |
| Physical / RF-layer attacker (jam, glitch, decap) | out of scope | §3.3, §3.6. |
| Side-channel observer of SM crypto | partially — §14 Q13 | timing/DPA on pairing; NimBLE's constant-time posture is unconfirmed. |
Unlike a wired protocol, most BLE traffic is unauthenticated by spec up to the moment pairing completes. The adversary's reach is therefore huge at Tiers 0/1 and is reduced only by (a) NimBLE's parser robustness and (b) the application requiring an authenticated pairing before exposing anything sensitive. A finding that "an unpaired peer can read characteristic X" is the application's permission choice (§10), not a NimBLE bug — unless NimBLE failed to enforce a permission the app did set, which is VALID.
For each: condition, violation symptom, severity, provenance.
- Memory-safe parsing of malformed PDUs across LL / HCI / L2CAP /
ATT / SM, given a correct platform. (inferred — §14 Q8–Q12; the
foundational property)
- Violation: a crafted PDU (bad length, malformed AD, oversized ATT write, lying L2CAP reassembly length) → OOB read/write or overflow.
- Severity: high (single address space; reachable by anyone in range, pre-authentication).
- Correct LE Secure Connections pairing — ECDH P-256 with peer
public-key validation, MITM protection under authenticated association
models, fresh nonces, correct key derivation. (inferred — §14 Q12)
- Violation: accepting an invalid/zero/debug ECDH key, nonce reuse, or a downgrade that silently drops MITM protection the app required.
- Severity: high (breaks the security NimBLE exists to provide).
- Correct AES-CCM link encryption once a key is established
(
ble_ll_crypto). (inferred — §14 Q9)- Violation: keystream/nonce reuse, encryption not actually applied after the start-encryption procedure.
- Severity: high.
- Enforcement of the ATT permission the application set on a
characteristic (require-encryption / require-authentication). (inferred
— §14 Q11)
- Violation: serving a read/write on an encryption-required attribute over an unencrypted link.
- Severity: high.
- Address privacy via RPA when enabled. (documented:
ble_hs_pvcy)- Violation: a static identifier leaking despite RPA being on.
- Severity: medium.
- No security for unauthenticated association ("Just Works") pairing. By spec, Just Works has no MITM protection; an active MITM can pair with both sides. The application must choose an authenticated method (Passkey / Numeric Comparison / OOB) for sensitive use. (spec-defined; §11a)
- No confidentiality before encryption is established. All Tier 0/1 traffic is sniffable. (spec-defined)
- No protection against jamming / RF DoS / connection flooding. (inferred — §14 Q14)
- No app-layer access control. NimBLE enforces the char permission the app set; it does not decide policy. A characteristic the app left open is open by the app's choice. (inferred — §14 Q11)
- No defence against a legitimately-bonded peer doing what its bond permits. (inferred)
- No guarantee about legacy pairing strength — legacy pairing is weak by modern standards; use LE Secure Connections. (spec-defined; §11a)
- No constant-time / side-channel guarantee on SM crypto unless confirmed. (inferred — §14 Q13)
- "Paired" ≠ "authenticated." A Just-Works bond is paired but not
MITM-protected. The
authenticatedflag on the bond is the property that matters. - "Connected" ≠ "authorized." Anyone in range can connect; access to data is gated by ATT permissions, not by the existence of a connection.
- A resolvable private address still resolves for a peer holding the IRK — RPA defeats tracking by third parties, not identification by a bonded peer.
- Require an authenticated pairing method (Passkey / Numeric Comparison / OOB) before exposing anything sensitive — do not rely on Just Works for confidential or safety-relevant data.
- Set ATT characteristic permissions correctly — mark sensitive characteristics require-encryption and/or require-authentication.
- Prefer LE Secure Connections; disable legacy pairing where the threat model needs modern guarantees.
- Enable privacy (RPA) if device tracking is a concern.
- Provision a strong platform RNG and confidential bond storage.
- Keep test features (DTM) out of production builds.
- Using Just Works pairing for sensitive data and assuming it protects against MITM.
- Exposing sensitive GATT characteristics with no encryption/auth permission and assuming "you need our app to connect."
- Assuming advertising is private or that a static address is not trackable (use RPA).
- Relying on legacy pairing for modern confidentiality.
- Shipping with Direct Test Mode reachable.
- Treating a connection as if it were authorization.
| Reported as | Why it is a non-finding | Cite |
|---|---|---|
| "Device advertises / is connectable without authentication" | BLE advertising and connection establishment are unauthenticated by spec; data access is gated by ATT permissions, not by connecting. | §3.2, §9 |
| "Just Works pairing has no MITM protection" | True and spec-defined; the application must select an authenticated association model. Not a NimBLE defect. | §9, §10.1 |
| "Traffic is sniffable before encryption" | Spec-defined; confidentiality begins after the encryption-start procedure. | §9 |
| "Legacy pairing is cryptographically weak" | Spec property of legacy pairing; remediation is to require LE Secure Connections (which NimBLE supports). | §9, §10.3 |
| "A peer can reset/flood/jam the link (DoS)" | No availability guarantee against an in-range RF adversary is claimed. | §9 |
| "Static/public BD address is trackable" | RPA exists for exactly this; if disabled, that is the integrator's choice. | §9, §10.4 |
| "Characteristic X is readable by an unpaired peer" | That is the application's ATT-permission choice, not a NimBLE bug — unless NimBLE failed to enforce a permission that was set (then VALID). | §9, §10.2 |
| "ECDH debug keys accepted in test build" | Debug keys are a spec test feature; relevant only if reachable in a production build (→ §14 Q12). | §5a, §14 Q12 |
The discriminator: anything resting on "BLE/this pairing mode is unauthenticated" is spec, not bug. A memory-safety/bounds failure in a parser, or a crypto/state-machine error that breaks a guarantee NimBLE does claim (SM key validation, encryption actually applied, enforcing an app-set permission), is VALID.
- A new pairing/association model or SM feature.
- A change to host/controller split or HCI handling.
- A new LL control procedure or PDU type from a spec update.
- Moving key storage in-tree, or taking ownership of the platform RNG.
- A new in-tree service that is more than a standard/demo profile.
| Disposition | Use when |
|---|---|
| VALID | Memory-unsafety/bounds failure in any LL/HCI/L2CAP/ATT/SM parser reachable from the radio; SM cryptographic/state-machine error (bad ECDH key accepted, nonce/key reuse, silent MITM-downgrade); encryption not applied after start; failure to enforce an ATT permission the app set. |
| OUT-OF-MODEL | The report depends on a spec-defined unauthenticated/weak behaviour (open advertising, Just Works, pre-encryption sniffing, legacy pairing) — §9. |
| DOWNSTREAM | Fix is the integrator's: require authenticated pairing, set char permissions, enable RPA, disable legacy/DTM (§10). |
| NON-FINDING | Matches a §11a row. |
| MODEL-GAP | In-scope in spirit but no §8/§9 item covers it → §14. |
Answered by Szymon Janc (Mynewt PMC) on 2026-07-24, in review of the PR that introduced this document. Q1–Q14 are answered below and folded into the body above.
Build / architecture — ANSWERED
- Q1. Is the HCI transport a trust boundary in split host/controller builds? → Yes, treat it as a boundary — a bug on one side should not affect the other. Szymon rates such issues low severity in practice, with the notable exception of a controller bug that lets a remote peer crash the host (e.g. during an LL procedure).
- Q2. Where do bonds/keys persist? → Outside "core-host."
Persistence is done by callbacks registered in
ble_hs_cfg(the host's main configuration struct), so the backend may store keys anywhere — RAM, flash, PSA. NimBLE ships one implementation innimble/host/store/config(built on Mynewtsys/config, RAM or flash); an application may supply its own platform-specific one. Key-store confidentiality is therefore the integrator's property, not NimBLE's. - Q3. Single-address-space / no-isolation amplification. → Confirmed in general; it depends on platform configuration.
Crypto / Security Manager — ANSWERED
- Q4. What RNG do SM/ECDH/RPA consume? → The Bluetooth
controller's, on the host side via the HCI
LE Randcommand (ble_hs_hci_rand()innimble/host/src/ble_hs_hci_util.c). The Bluetooth specification requires that command be FIPS-compliant. Parts of the LE SC algorithm code may use a platform TRNG directly where so configured. On the controller side this typically sources the SoC's hardware RNG peripheral, which is spec-compliant. So RNG quality rests on the controller/SoC and the spec, not on NimBLE code. - Q5. Which SM association models are enabled by default, and is
silent downgrade prevented? → Only legacy pairing is enabled by
default (
nimble/host/syscfg.yml). Szymon was not certain about silent security-level-downgrade prevention; he notes the application always receives a proper event when security changes. (the downgrade question therefore stays partly open — see below) - Q12. Does
ble_sm_scvalidate the peer's ECDH public key and are nonces fresh? → Yes. - Q13. Constant-time / side-channel posture for SM operations? → None claimed by NimBLE — it relies on the crypto library. Tinycrypt support was recently removed; the only supported backend now is mbedTLS 3.6. There are plans to move to the PSA crypto API.
Parser robustness — ANSWERED
- Q6. What bounds reassembly/allocation? → Configured memory pools
(
msysor a dedicated pool). Regardless of configuration, a peer must not be able to write past the provided buffer; but depending on the layer or protocol, length fields are not guaranteed to stay within bounds and must be checked. That makes length-field validation a first-order review target. - Q8. Robustness target for malformed advertising data / connection PDUs? → Ignore or reject them. Note: advertising data is passed to the application without LTV inspection. Parsing helpers are provided, but the application is not obliged to use them and may parse on its own — so malformed-AD handling is partly the integrator's.
- Q9. Handling of LL control opcodes? → Ignore or reject invalid PDUs, likewise unexpected or out-of-order ones; depending on the spec-defined procedure this may involve link termination.
- Q10. L2CAP reassembly and COC credit/flow bounds? → If a peer overflows — sends more packets than its credits allow — the COC is terminated.
- Q11. Does
ble_att_svrenforce per-attribute permissions, and what bounds the prepared-write queue? → Permissions are per attribute. The prepared-write queue has a configurable limit and a timeout, after which the connection is terminated and resources freed (BLE_ATT_SVR_MAX_PREP_ENTRIES,BLE_ATT_SVR_QUEUED_WRITE_TMOinnimble/host/syscfg.yml).
Features — ANSWERED
- Q7. Is
ble_ll_dtm(Direct Test Mode) reachable in production? → It can be. It is a radio-HW test mode (used in test labs for emission approvals), a controller feature with a thin host layer for application access. It may or may not be enabled in production and can be compiled out — reachable in production if the application allows it. - Q14. Posture on connection-flood / resource-exhaustion DoS? → Out of scope, unless there is an actual resource leak. It depends wholly on system configuration and application policy.
Still open
- Q5a. Whether NimBLE prevents a silent security-level downgrade below what the application requested. Szymon was not certain; the application does get an event on any security change.
- Q15 / Q16 (meta /
host/services/*scoping) — not yet answered.
At master @ 793cb1612088, apache/mynewt-nimble contains no
SECURITY.md, AGENTS.md, or prior threat-model document. The
nimble/doc and qualification/ trees are Bluetooth-qualification and
API documentation, not a security policy. This is a greenfield v0; there
is nothing to supersede or back-map. Source-layout facts are cited inline
as (documented).