@@ -16,8 +16,7 @@ use super::Error;
1616pub ( crate ) enum BondingState {
1717 Bonded ,
1818 NotBonded ,
19- /// Bonding state could not be determined (non-bluez backend or DBus
20- /// unreachable); the caller decides whether to proceed.
19+ /// State could not be determined; treated as not bonded.
2120 Unknown ,
2221}
2322
@@ -48,27 +47,22 @@ pub(crate) async fn check_bonded(peripheral: &Peripheral) -> BondingState {
4847 }
4948}
5049
51- /// Returns `Err(ConnectionFailed)` when the device is reachable but
52- /// explicitly not bonded; falls through on `Unknown`.
50+ /// Refuses the link unless bluez confirms it is bonded.
5351pub ( crate ) async fn enforce_bonded ( peripheral : & Peripheral ) -> Result < ( ) , Error > {
54- match check_bonded ( peripheral) . await {
55- BondingState :: Bonded => Ok ( ( ) ) ,
56- BondingState :: Unknown => {
57- warn ! (
58- "Could not verify LE Secure Connections bonding via bluez; \
59- proceeding under OS pairing enforcement"
60- ) ;
61- Ok ( ( ) )
62- }
63- BondingState :: NotBonded => {
64- warn ! (
65- "BLE FIDO authenticator is not bonded with LE Secure Connections; \
66- CTAP 2.2 §11.4 requires bonding. Pair the device via the OS \
67- (e.g. `bluetoothctl pair <ADDR>`) before retrying."
68- ) ;
69- Err ( Error :: ConnectionFailed )
70- }
52+ bonded_or_refused ( check_bonded ( peripheral) . await )
53+ }
54+
55+ fn bonded_or_refused ( state : BondingState ) -> Result < ( ) , Error > {
56+ if state == BondingState :: Bonded {
57+ return Ok ( ( ) ) ;
7158 }
59+ warn ! (
60+ ?state,
61+ "BLE FIDO authenticator link is not confirmed bonded with LE Secure \
62+ Connections; CTAP 2.2 §11.4 requires bonding. Pair the device via the OS \
63+ (e.g. `bluetoothctl pair <ADDR>`) before retrying."
64+ ) ;
65+ Err ( Error :: ConnectionFailed )
7266}
7367
7468/// btleplug doesn't expose the adapter index, so we walk the bluez
@@ -109,3 +103,15 @@ fn query_bluez_bonded(address: BDAddr) -> Result<(bool, bool), String> {
109103
110104 Err ( format ! ( "device {address} not found in bluez ObjectManager" ) )
111105}
106+
107+ #[ cfg( test) ]
108+ mod tests {
109+ use super :: * ;
110+
111+ #[ test]
112+ fn only_confirmed_bonded_proceeds ( ) {
113+ assert ! ( bonded_or_refused( BondingState :: Bonded ) . is_ok( ) ) ;
114+ assert ! ( bonded_or_refused( BondingState :: NotBonded ) . is_err( ) ) ;
115+ assert ! ( bonded_or_refused( BondingState :: Unknown ) . is_err( ) ) ;
116+ }
117+ }
0 commit comments