diff options
author | 2023-08-02 16:46:03 +0000 | |
---|---|---|
committer | 2023-08-02 16:46:03 +0000 | |
commit | 73d060e749da8ea454598f0c483e896cef88f1e1 (patch) | |
tree | 07e184fda4d740999df060cb5137ff0a8533db1b | |
parent | 549850b3774d243dcc8d3c9184a4301082d1d641 (diff) | |
parent | abc35c269b8c5b5ee17712401429ac6d7de3c015 (diff) |
Merge changes from topic "bug_290987423" into main
* changes:
lmp: handle passkey entry failure on responding side
lmp: refactor send/recv commitments
-rw-r--r-- | tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs | 202 | ||||
-rw-r--r-- | tools/rootcanal/rust/test/SP/BV-14bis-C.in | 106 | ||||
-rw-r--r-- | tools/rootcanal/rust/test/SP/BV-15bis-C.in | 118 |
3 files changed, 361 insertions, 65 deletions
diff --git a/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs b/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs index 5a397ed232..543219691f 100644 --- a/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs +++ b/tools/rootcanal/rust/src/lmp/procedure/secure_simple_pairing.rs @@ -143,11 +143,14 @@ async fn receive_public_key(ctx: &impl Context, transaction_id: u8) -> PublicKey const COMMITMENT_VALUE_SIZE: usize = 16; const NONCE_SIZE: usize = 16; -async fn receive_commitment(ctx: &impl Context, skip_first: bool) { - let commitment_value = [0; COMMITMENT_VALUE_SIZE]; +fn build_commitment(_ctx: &impl Context) -> [u8; COMMITMENT_VALUE_SIZE] { + [0; COMMITMENT_VALUE_SIZE] +} + +async fn receive_commitment(ctx: &impl Context, confirm: Option<lmp::SimplePairingConfirm>) { + let commitment_value = build_commitment(ctx); - if !skip_first { - let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + if let Some(confirm) = confirm { if confirm.get_commitment_value() != &commitment_value { todo!(); } @@ -177,16 +180,8 @@ async fn receive_commitment(ctx: &impl Context, skip_first: bool) { .await; } -async fn send_commitment(ctx: &impl Context, skip_first: bool) { - let commitment_value = [0; COMMITMENT_VALUE_SIZE]; - - if !skip_first { - ctx.send_lmp_packet( - lmp::SimplePairingConfirmBuilder { transaction_id: 0, commitment_value }.build(), - ); - } - - let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; +async fn send_commitment(ctx: &impl Context, confirm: lmp::SimplePairingConfirm) { + let commitment_value = build_commitment(ctx); if confirm.get_commitment_value() != &commitment_value { todo!(); @@ -437,7 +432,8 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> { match auth_method { AuthenticationMethod::NumericComparisonJustWork | AuthenticationMethod::NumericComparisonUserConfirm => { - send_commitment(ctx, true).await; + let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + send_commitment(ctx, confirm).await; if user_confirmation_request(ctx).await.is_err() { ctx.send_lmp_packet( @@ -448,13 +444,21 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> { Ok(()) } AuthenticationMethod::PasskeyEntry => { - if initiator.io_capability == hci::IoCapability::KeyboardOnly { + let confirm = if initiator.io_capability == hci::IoCapability::KeyboardOnly { if user_passkey_request(ctx).await.is_err() { ctx.send_lmp_packet( lmp::PasskeyFailedBuilder { transaction_id: 0 }.build(), ); Err(())?; } + ctx.send_lmp_packet( + lmp::SimplePairingConfirmBuilder { + transaction_id: 0, + commitment_value: build_commitment(ctx), + } + .build(), + ); + ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await } else { ctx.send_hci_event( hci::UserPasskeyNotificationBuilder { @@ -463,9 +467,32 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> { } .build(), ); - } - for _ in 0..PASSKEY_ENTRY_REPEAT_NUMBER { - send_commitment(ctx, false).await; + ctx.send_lmp_packet( + lmp::SimplePairingConfirmBuilder { + transaction_id: 0, + commitment_value: build_commitment(ctx), + } + .build(), + ); + match ctx + .receive_lmp_packet::<Either<lmp::SimplePairingConfirm, lmp::NotAccepted>>() + .await + { + Either::Left(confirm) => confirm, + Either::Right(_) => Err(())?, + } + }; + send_commitment(ctx, confirm).await; + for _ in 1..PASSKEY_ENTRY_REPEAT_NUMBER { + ctx.send_lmp_packet( + lmp::SimplePairingConfirmBuilder { + transaction_id: 0, + commitment_value: build_commitment(ctx), + } + .build(), + ); + let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + send_commitment(ctx, confirm).await; } Ok(()) } @@ -474,7 +501,15 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> { remote_oob_data_request(ctx).await?; } - send_commitment(ctx, false).await; + ctx.send_lmp_packet( + lmp::SimplePairingConfirmBuilder { + transaction_id: 0, + commitment_value: build_commitment(ctx), + } + .build(), + ); + let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + send_commitment(ctx, confirm).await; Ok(()) } } @@ -653,57 +688,78 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReq) -> Resul // Authentication Stage 1 let auth_method = authentication_method(initiator, responder); - let negative_user_confirmation = match auth_method { - AuthenticationMethod::NumericComparisonJustWork - | AuthenticationMethod::NumericComparisonUserConfirm => { - receive_commitment(ctx, true).await; + let result: Result<bool, ()> = async { + match auth_method { + AuthenticationMethod::NumericComparisonJustWork + | AuthenticationMethod::NumericComparisonUserConfirm => { + receive_commitment(ctx, None).await; - let user_confirmation = user_confirmation_request(ctx).await; - user_confirmation.is_err() - } - AuthenticationMethod::PasskeyEntry => { - let skip_first_commitment = if responder.io_capability - == hci::IoCapability::KeyboardOnly - { - // TODO: handle error - let _user_passkey = user_passkey_request(ctx).await; - false - } else { - ctx.send_hci_event( - hci::UserPasskeyNotificationBuilder { bd_addr: ctx.peer_address(), passkey: 0 } - .build(), - ); - match ctx - .receive_lmp_packet::<Either<lmp::SimplePairingConfirm, lmp::PasskeyFailed>>() - .await - { - Either::Left(_) => true, // TODO: check for `confirm.get_commitment_value()` - Either::Right(_) => { - ctx.send_hci_event( - hci::SimplePairingCompleteBuilder { - status: hci::ErrorCode::AuthenticationFailure, - bd_addr: ctx.peer_address(), - } - .build(), + let user_confirmation = user_confirmation_request(ctx).await; + Ok(user_confirmation.is_err()) + } + AuthenticationMethod::PasskeyEntry => { + let confirm = if responder.io_capability == hci::IoCapability::KeyboardOnly { + let user_passkey = user_passkey_request(ctx).await; + let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + if user_passkey.is_err() { + ctx.send_lmp_packet( + lmp::NotAcceptedBuilder { + transaction_id: 0, + not_accepted_opcode: lmp::Opcode::SimplePairingConfirm, + error_code: hci::ErrorCode::AuthenticationFailure.into(), + }.build(), ); return Err(()); } + confirm + } else { + ctx.send_hci_event( + hci::UserPasskeyNotificationBuilder { + bd_addr: ctx.peer_address(), + passkey: 0, + } + .build(), + ); + match ctx + .receive_lmp_packet::<Either<lmp::SimplePairingConfirm, lmp::PasskeyFailed>>() + .await + { + Either::Left(confirm) => confirm, + Either::Right(_) => Err(())?, + } + }; + receive_commitment(ctx, Some(confirm)).await; + for _ in 1..PASSKEY_ENTRY_REPEAT_NUMBER { + let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + receive_commitment(ctx, Some(confirm)).await; } - }; - receive_commitment(ctx, skip_first_commitment).await; - for _ in 1..PASSKEY_ENTRY_REPEAT_NUMBER { - receive_commitment(ctx, false).await; + Ok(false) } - false - } - AuthenticationMethod::OutOfBand => { - if responder.oob_data_present != hci::OobDataPresent::NotPresent { - // TODO: handle error - let _remote_oob_data = remote_oob_data_request(ctx).await; + AuthenticationMethod::OutOfBand => { + if responder.oob_data_present != hci::OobDataPresent::NotPresent { + // TODO: handle error + let _remote_oob_data = remote_oob_data_request(ctx).await; + } + + let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await; + receive_commitment(ctx, Some(confirm)).await; + Ok(false) } + } + } + .await; - receive_commitment(ctx, false).await; - false + let negative_user_confirmation = match result { + Ok(negative_user_confirmation) => negative_user_confirmation, + Err(_) => { + ctx.send_hci_event( + hci::SimplePairingCompleteBuilder { + status: hci::ErrorCode::AuthenticationFailure, + bd_addr: ctx.peer_address(), + } + .build(), + ); + return Err(()); } }; @@ -896,7 +952,7 @@ mod tests { } #[test] - fn passkey_entry_initiator_failure_on_initiating_side() { + fn passkey_entry_initiator_negative_reply_on_initiating_side() { let context = TestContext::new(); let procedure = initiate; @@ -904,7 +960,15 @@ mod tests { } #[test] - fn passkey_entry_responder_failure_on_initiating_side() { + fn passkey_entry_responder_negative_reply_on_responding_side() { + let context = TestContext::new(); + let procedure = respond; + + include!("../../../test/SP/BV-14bis-C.in"); + } + + #[test] + fn passkey_entry_responder_negative_reply_on_initiating_side() { let context = TestContext::new(); let procedure = respond; @@ -912,6 +976,14 @@ mod tests { } #[test] + fn passkey_entry_initiator_negative_reply_on_responding_side() { + let context = TestContext::new(); + let procedure = initiate; + + include!("../../../test/SP/BV-15bis-C.in"); + } + + #[test] #[should_panic] // TODO: make the test pass fn passkey_entry_initiator_failure_on_responding_side() { let context = TestContext::new(); diff --git a/tools/rootcanal/rust/test/SP/BV-14bis-C.in b/tools/rootcanal/rust/test/SP/BV-14bis-C.in new file mode 100644 index 0000000000..b98f45096d --- /dev/null +++ b/tools/rootcanal/rust/test/SP/BV-14bis-C.in @@ -0,0 +1,106 @@ +// Passkey entry responder, negative reply on responding side: +// - Test case not present in LMP.TS, but other permutations are described in SP/BV-14-C, SP/BV-15-C +// - IUT is KeyboardOnly, responder +// - Lower Tester is Display, initiator +// - IUT fails passkey entry with User_Passkey_Request_NegativeReply, responds Not Accepted to the SimplePairingConfirm +sequence! { procedure, context, + // ACL Connection Established + Lower Tester -> IUT: IoCapabilityReq { + transaction_id: 0, + io_capabilities: 0x00, + oob_authentication_data: 0x00, + authentication_requirement: 0x01, + } + IUT -> Upper Tester: IoCapabilityResponse { + bd_addr: context.peer_address(), + io_capability: IoCapability::DisplayOnly, + oob_data_present: OobDataPresent::NotPresent, + authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection, + } + IUT -> Upper Tester: IoCapabilityRequest { + bd_addr: context.peer_address(), + } + Upper Tester -> IUT: IoCapabilityRequestReply { + bd_addr: context.peer_address(), + io_capability: IoCapability::KeyboardOnly, + oob_present: OobDataPresent::NotPresent, + authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection, + } + IUT -> Upper Tester: IoCapabilityRequestReplyComplete { + num_hci_command_packets: 1, + status: ErrorCode::Success, + bd_addr: context.peer_address(), + } + IUT -> Lower Tester: IoCapabilityRes { + transaction_id: 0, + io_capabilities: 0x02, + oob_authentication_data: 0x00, + authentication_requirement: 0x01, + } + // Public Key Exchange + Lower Tester -> IUT: EncapsulatedHeader { + transaction_id: 0, + major_type: 1, + minor_type: 1, + payload_length: 48, + } + IUT -> Lower Tester: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedHeader, + } + repeat 3 times with (part in peer_p192_public_key()) { + Lower Tester -> IUT: EncapsulatedPayload { + transaction_id: 0, + data: part, + } + IUT -> Lower Tester: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedPayload, + } + } + IUT -> Lower Tester: EncapsulatedHeader { + transaction_id: 0, + major_type: 1, + minor_type: 1, + payload_length: 48, + } + Lower Tester -> IUT: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedHeader, + } + repeat 3 times with (part in local_p192_public_key(&context)) { + IUT -> Lower Tester: EncapsulatedPayload { + transaction_id: 0, + data: part, + } + Lower Tester -> IUT: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedPayload, + } + } + // Authentication Stage 1: Passkey Entry Protocol + IUT -> Upper Tester: UserPasskeyRequest { + bd_addr: context.peer_address(), + } + Upper Tester -> IUT: UserPasskeyRequestNegativeReply { + bd_addr: context.peer_address(), + } + IUT -> Upper Tester: UserPasskeyRequestNegativeReplyComplete { + num_hci_command_packets: 1, + status: ErrorCode::Success, + bd_addr: context.peer_address(), + } + Lower Tester -> IUT: SimplePairingConfirm { + transaction_id: 0, + commitment_value: [0; 16], + } + IUT -> Lower Tester: NotAccepted { + transaction_id: 0, + not_accepted_opcode: Opcode::SimplePairingConfirm, + error_code: ErrorCode::AuthenticationFailure.into(), + } + IUT -> Upper Tester: SimplePairingComplete { + status: ErrorCode::AuthenticationFailure, + bd_addr: context.peer_address(), + } +} diff --git a/tools/rootcanal/rust/test/SP/BV-15bis-C.in b/tools/rootcanal/rust/test/SP/BV-15bis-C.in new file mode 100644 index 0000000000..f9fda299c8 --- /dev/null +++ b/tools/rootcanal/rust/test/SP/BV-15bis-C.in @@ -0,0 +1,118 @@ +// Passkey entry initiator, negative reply on responding side: +// - Test case not present in LMP.TS, but other permutations are described in SP/BV-14-C, SP/BV-15-C +// - IUT is DisplayOnly, initiator +// - Lower Tester is KeyboardOnly, responder +// - Lower Tester fails passkey entry with User_Passkey_Request_NegativeReply, responds Not Accepted to the SimplePairingConfirm +sequence! { procedure, context, + // ACL Connection Established + Upper Tester -> IUT: AuthenticationRequested { + connection_handle: context.peer_handle() + } + IUT -> Upper Tester: AuthenticationRequestedStatus { + num_hci_command_packets: 1, + status: ErrorCode::Success, + } + IUT -> Upper Tester: LinkKeyRequest { + bd_addr: context.peer_address(), + } + Upper Tester -> IUT: LinkKeyRequestNegativeReply { + bd_addr: context.peer_address(), + } + IUT -> Upper Tester: LinkKeyRequestNegativeReplyComplete { + num_hci_command_packets: 1, + status: ErrorCode::Success, + bd_addr: context.peer_address(), + } + IUT -> Upper Tester: IoCapabilityRequest { + bd_addr: context.peer_address(), + } + Upper Tester -> IUT: IoCapabilityRequestReply { + bd_addr: context.peer_address(), + io_capability: IoCapability::DisplayOnly, + oob_present: OobDataPresent::NotPresent, + authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection, + } + IUT -> Upper Tester: IoCapabilityRequestReplyComplete { + num_hci_command_packets: 1, + status: ErrorCode::Success, + bd_addr: context.peer_address(), + } + IUT -> Lower Tester: IoCapabilityReq { + transaction_id: 0, + io_capabilities: 0x00, + oob_authentication_data: 0x00, + authentication_requirement: 0x01, + } + Lower Tester -> IUT: IoCapabilityRes { + transaction_id: 0, + io_capabilities: 0x02, + oob_authentication_data: 0x00, + authentication_requirement: 0x01, + } + IUT -> Upper Tester: IoCapabilityResponse { + bd_addr: context.peer_address(), + io_capability: IoCapability::KeyboardOnly, + oob_data_present: OobDataPresent::NotPresent, + authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection, + } + // Public Key Exchange + IUT -> Lower Tester: EncapsulatedHeader { + transaction_id: 0, + major_type: 1, + minor_type: 1, + payload_length: 48, + } + Lower Tester -> IUT: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedHeader, + } + repeat 3 times with (part in local_p192_public_key(&context)) { + IUT -> Lower Tester: EncapsulatedPayload { + transaction_id: 0, + data: part, + } + Lower Tester -> IUT: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedPayload, + } + } + Lower Tester -> IUT: EncapsulatedHeader { + transaction_id: 0, + major_type: 1, + minor_type: 1, + payload_length: 48, + } + IUT -> Lower Tester: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedHeader, + } + repeat 3 times with (part in peer_p192_public_key()) { + Lower Tester -> IUT: EncapsulatedPayload { + transaction_id: 0, + data: part, + } + IUT -> Lower Tester: Accepted { + transaction_id: 0, + accepted_opcode: Opcode::EncapsulatedPayload, + } + } + // Authentication Stage 1: Passkey Entry Protocol + IUT -> Upper Tester: UserPasskeyNotification { bd_addr: context.peer_address(), passkey: 0 } + IUT -> Lower Tester: SimplePairingConfirm { + transaction_id: 0, + commitment_value: [0; 16], + } + Lower Tester -> IUT: NotAccepted { + transaction_id: 0, + not_accepted_opcode: Opcode::SimplePairingConfirm, + error_code: ErrorCode::AuthenticationFailure.into(), + } + IUT -> Upper Tester: SimplePairingComplete { + status: ErrorCode::AuthenticationFailure, + bd_addr: context.peer_address(), + } + IUT -> Upper Tester: AuthenticationComplete { + status: ErrorCode::AuthenticationFailure, + connection_handle: context.peer_handle(), + } +} |