Skip to content

Phase 7: Bluetooth offline pairing (v0.2.0) - #19

Merged
xuandung38 merged 17 commits into
mainfrom
feat/p07-ghep-bluetooth-offline
Oct 30, 2026
Merged

xuandung38 merged 17 commits into
mainfrom
feat/p07-ghep-bluetooth-offline

Conversation

@xuandung38

Copy link
Copy Markdown
Contributor

Ghep doi Bluetooth lan dau khong can mang (QR luong B, PIN luong C dung CPace-X25519-SHA512), nhan 'Connected via Bluetooth'. Bao cao bao mat PASS o hub reports/security-scan-0.2.0-2026-10-30.md. Merge bang 'Create a merge commit'.

Draft Appendix B.1 vectors (generator/message), HandLive's PIN-pairing
vectors with their HKDF-derived Bluetooth keys, and all four negative
cases (low-order points, sid mismatch, wrong PIN).
Field25519 is a self-written, constant-time GF(2^255-19): 8 limbs of
32 bits (not BigInteger, not Tink, no branch or table index on a
secret field value) so every limb*limb product fits a ULong without
needing a 64x64->128 multiply (JVM has none below API 31). On top of
it, CPaceX25519SHA512 implements calculate_generator (Elligator2 per
RFC 9380) and ISK per draft-irtf-cfrg-cpace-21 Appendix B.1/A.5;
scalar_mult_vfy reuses Tink's X25519 (already rejects low-order
points), matching the existing primitives' use of Tink for point
multiplication.
…etekt limits

Field25519.kt: wraps the q-constant uintArrayOf one element per line
(ktlint argument-list-wrapping) and suppresses TooManyFunctions at
file scope — the constant-time helpers below the class (addRaw,
subRaw, reduceBounded, addMod, subMod, addAt, mulRaw,
mulBySmallConstant, mulMod) are split one step per function on
purpose, so merging them to dodge the count would make the
constant-time review harder, not easier.

CPaceX25519SHA512.kt: names the LEB128 constants (group bits,
continuation bit, payload mask, capacity hint) and the RFC 7748
top-bit-clear mask instead of inline magic numbers, and suppresses
LongParameterList on isk() — its 6 parameters are exactly the draft's
ISK signature (sid, K, Ya, ADa, Yb, ADb), not something to shrink.

No behavior change; all 8 CPaceX25519SHA512VectorTest cases (cpace.json,
including the negative vectors) still pass.

Signed-off-by: Hồ Xuân Dũng <me@hxd.vn>
Pre-auth records, transport frames and the record codec of the Bluetooth link (0.4.5), the direct and bulk envelope types and DIRECT_WIFI_UNAVAILABLE (P7).
K_bt_disc/K_bt_link (0.4.5 Keys), Noise_NNpsk0_25519_ChaChaPoly_SHA256 on the S side and the ChaCha20-Poly1305 AEAD it uses, with bt-link.json tests.
Admission, link-security handshake (steps 1-5), transport records with lanes and rekey, the advertisement data and serveBluetoothPeer on ControlServer (0.4.5, CONN-05).
Peripheral (advertising set and L2CAP server, hint rotation), adapter lifecycle, Nearby devices permissions and the Bluetooth session channel in ConnectionRuntime (CONN-05).
Braces on every when entry, one call per line in chained test calls, blank lines between multiline when entries; no behaviour change.
bt-pairing.json keys, pr_bt and first frame; a single pairing slot (wrong or missing MAC, second claimant, hold timeout); pr_bt first in the advertising while a QR window is open; a K_bt_plink link running hello, offer, confirm and done through PairingExchange, and a pin hello refused on a QR link.
While a QR window is open the phone advertises pr_bt first, tries K_bt_plink before every pair key through one slot per listener, and runs PAIR-01 on the confirmed pairing link through PairingCoordinator, storing the pair as on the LAN. A noise1 of the wrong size is rejected instead of throwing, and the last pair envelope reaches the link before it closes.
CPace then noise1 keyed by K_bt_pinlink reaching the PIN pairing link with K_bt_pin; cpace1 with no PIN window and a low-order Ya closed without an answer; a wrong PIN rejected and counted once; PAIR-01 over the PIN link and a qr hello refused on it; three wrong PINs over Bluetooth, and wrong PINs mixed across Bluetooth and the LAN, closing the window at the third for both transports with BT_PIN_LOCKOUT. cpace.json and bt-pairing.json point at their tests.
…flow C)

While a PIN window is open the phone advertises BT_PIN_HINT, answers cpace1 with cpace2 and accepts only the noise1 keyed by K_bt_pinlink from ISK; the PIN link runs PAIR-01 with K_bt_pin in place of K_pin. A channel that fails after cpace1 counts on the open PIN window, the same counter as the LAN offers, so the two transports share PIN_MAX_ATTEMPTS; the third failure closes the window and starts BT_PIN_LOCKOUT on both.
DeviceListModelTest and HLComponentSemanticsTest expect a dedicated
DeviceLink.BLUETOOTH / HLConnectionStatus.ConnectedBluetooth instead of
the Wi-Fi placeholder; red until the next commit adds them.
DeviceLink gets its own BLUETOOTH case and HLConnectionStatus gets
ConnectedBluetooth (status_connected_bluetooth/_to, same green tint as
Wi-Fi), so DeviceListModel stops mapping PeerSession.Channel.BLUETOOTH
to the Wi-Fi placeholder.
Drop the per-connection bulk stream keys (K_bulk) and Wi-Fi Direct
Location-prompt claims into a Specs section: core/transport reserves
the bulk channel and scheduling, but nothing calls enqueueBulk or
encrypts bulk content yet, and there is no Wi-Fi Direct code at all.
Credit the UI status label and permission-prompt logic that are
actually wired up.
@xuandung38
xuandung38 merged commit cd4527a into main Oct 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant