summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Abel Lucas <uael@google.com> 2023-08-02 16:46:03 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2023-08-02 16:46:03 +0000
commit73d060e749da8ea454598f0c483e896cef88f1e1 (patch)
tree07e184fda4d740999df060cb5137ff0a8533db1b
parent549850b3774d243dcc8d3c9184a4301082d1d641 (diff)
parentabc35c269b8c5b5ee17712401429ac6d7de3c015 (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.rs202
-rw-r--r--tools/rootcanal/rust/test/SP/BV-14bis-C.in106
-rw-r--r--tools/rootcanal/rust/test/SP/BV-15bis-C.in118
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(),
+ }
+}