Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 36a5b421 authored by uael's avatar uael
Browse files

lmp: refactor send/recv commitments

This change is motivated by the need to properly handle passkey error handling.

Bug: 290987423
Test: atest avatar
Change-Id: I54d424cfbbf28d89a5d04b69db6f5cb761a3ca01
parent 0a5c4b8a
Loading
Loading
Loading
Loading
+105 −64
Original line number Diff line number Diff line
@@ -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]
}

    if !skip_first {
        let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
async fn receive_commitment(ctx: &impl Context, confirm: Option<lmp::SimplePairingConfirm>) {
    let commitment_value = build_commitment(ctx);

    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,27 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
                        }
                        .build(),
                    );
                    ctx.send_lmp_packet(
                        lmp::SimplePairingConfirmBuilder {
                            transaction_id: 0,
                            commitment_value: build_commitment(ctx),
                        }
                        .build(),
                    );
                    // TODO: handle error
                    ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await
                };
                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),
                        }
                for _ in 0..PASSKEY_ENTRY_REPEAT_NUMBER {
                    send_commitment(ctx, false).await;
                        .build(),
                    );
                    let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
                    send_commitment(ctx, confirm).await;
                }
                Ok(())
            }
@@ -474,7 +496,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,48 +683,42 @@ 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 {
    let result: Result<bool, ()> = async {
        match auth_method {
            AuthenticationMethod::NumericComparisonJustWork
            | AuthenticationMethod::NumericComparisonUserConfirm => {
            receive_commitment(ctx, true).await;
                receive_commitment(ctx, None).await;

                let user_confirmation = user_confirmation_request(ctx).await;
            user_confirmation.is_err()
                Ok(user_confirmation.is_err())
            }
            AuthenticationMethod::PasskeyEntry => {
            let skip_first_commitment = if responder.io_capability
                == hci::IoCapability::KeyboardOnly
            {
                let confirm = if responder.io_capability == hci::IoCapability::KeyboardOnly {
                    // TODO: handle error
                    let _user_passkey = user_passkey_request(ctx).await;
                false
                    ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await
                } else {
                    ctx.send_hci_event(
                    hci::UserPasskeyNotificationBuilder { bd_addr: ctx.peer_address(), passkey: 0 }
                        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(),
                        );
                        return Err(());
                    }
                        Either::Left(confirm) => confirm,
                        Either::Right(_) => Err(())?,
                    }
                };
            receive_commitment(ctx, skip_first_commitment).await;
                receive_commitment(ctx, Some(confirm)).await;
                for _ in 1..PASSKEY_ENTRY_REPEAT_NUMBER {
                receive_commitment(ctx, false).await;
                    let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
                    receive_commitment(ctx, Some(confirm)).await;
                }
            false
                Ok(false)
            }
            AuthenticationMethod::OutOfBand => {
                if responder.oob_data_present != hci::OobDataPresent::NotPresent {
@@ -702,8 +726,25 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReq) -> Resul
                    let _remote_oob_data = remote_oob_data_request(ctx).await;
                }

            receive_commitment(ctx, false).await;
            false
                let confirm = ctx.receive_lmp_packet::<lmp::SimplePairingConfirm>().await;
                receive_commitment(ctx, Some(confirm)).await;
                Ok(false)
            }
        }
    }
    .await;

    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(());
        }
    };