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

Commit 97e63ca1 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes I00673f01,Iab535d7c,Iaee68ebe am: 84f442b7

parents 358a415b 84f442b7
Loading
Loading
Loading
Loading
+23 −79
Original line number Diff line number Diff line
// Bluetooth Core, Vol 2, Part C, 4.2.1

use num_traits::ToPrimitive;

use crate::either::Either;
use crate::num_hci_command_packets;
use crate::packets::{hci, lmp};
@@ -10,24 +8,23 @@ use crate::procedure::legacy_pairing;
use crate::procedure::secure_simple_pairing;
use crate::procedure::Context;

async fn secure_simple_pairing_supported(ctx: &impl Context) -> bool {
    let ssp_bit = hci::LMPFeaturesPage1Bits::SecureSimplePairingHostSupport.to_u64().unwrap();
    let local_supported = ctx.extended_features(1) & ssp_bit != 0;
    // Lazy peer features
    let peer_supported = async move {
        let page = if let Some(page) = ctx.peer_extended_features(1) {
            page
        } else {
            features::initiate(ctx, 1).await
        };
        page & ssp_bit != 0
    };
    local_supported && peer_supported.await
pub async fn send_challenge(
    ctx: &impl Context,
    transaction_id: u8,
    _link_key: [u8; 16],
) -> Result<(), ()> {
    let random_number = [0; 16];
    ctx.send_lmp_packet(lmp::AuRandBuilder { transaction_id, random_number }.build());

    match ctx.receive_lmp_packet::<Either<lmp::SresPacket, lmp::NotAcceptedPacket>>().await {
        Either::Left(_response) => Ok(()),
        Either::Right(_) => Err(()),
    }
}

pub async fn send_authentication_challenge(ctx: &impl Context, transaction_id: u8) {
    ctx.send_lmp_packet(lmp::AuRandBuilder { transaction_id, random_number: [0; 16] }.build());
    let _ = ctx.receive_lmp_packet::<lmp::SresPacket>().await;
pub async fn receive_challenge(ctx: &impl Context, _link_key: [u8; 16]) {
    let _random_number = *ctx.receive_lmp_packet::<lmp::AuRandPacket>().await.get_random_number();
    ctx.send_lmp_packet(lmp::SresBuilder { transaction_id: 0, authentication_rsp: [0; 4] }.build());
}

pub async fn initiate(ctx: &impl Context) {
@@ -42,7 +39,7 @@ pub async fn initiate(ctx: &impl Context) {

    ctx.send_hci_event(hci::LinkKeyRequestBuilder { bd_addr: ctx.peer_address() }.build());

    let pairing = match ctx.receive_hci_command::<Either<
    let status = match ctx.receive_hci_command::<Either<
        hci::LinkKeyRequestReplyPacket,
        hci::LinkKeyRequestNegativeReplyPacket,
    >>().await {
@@ -55,7 +52,7 @@ pub async fn initiate(ctx: &impl Context) {
                }
                .build(),
            );
            false
            hci::ErrorCode::Success
        },
        Either::Right(_) => {
            ctx.send_hci_event(
@@ -67,53 +64,21 @@ pub async fn initiate(ctx: &impl Context) {
                .build(),
            );

            let result = if secure_simple_pairing_supported(ctx).await {
            let result = if features::supported_on_both_page1(ctx, hci::LMPFeaturesPage1Bits::SecureSimplePairingHostSupport).await {
                secure_simple_pairing::initiate(ctx).await
            } else {
                legacy_pairing::initiate(ctx).await
            };

            if result.is_err() {
                ctx.send_hci_event(
                    hci::AuthenticationCompleteBuilder {
                        status: hci::ErrorCode::AuthenticationFailure,
                        connection_handle: ctx.peer_handle(),
                    }
                    .build(),
                );
                return;
            match result {
                Ok(_) => hci::ErrorCode::Success,
                Err(_) => hci::ErrorCode::AuthenticationFailure
            }
            true
        }
    };

    send_authentication_challenge(ctx, 0).await;

    // Link Key Calculation
    if pairing {
        let _random_number = ctx.receive_lmp_packet::<lmp::AuRandPacket>().await;

        // TODO: Resolve authentication challenge
        ctx.send_lmp_packet(
            lmp::SresBuilder { transaction_id: 0, authentication_rsp: [0; 4] }.build(),
        );

    ctx.send_hci_event(
            hci::LinkKeyNotificationBuilder {
                bd_addr: ctx.peer_address(),
                key_type: hci::KeyType::AuthenticatedP192,
                link_key: [0; 16],
            }
            .build(),
        );
    }

    ctx.send_hci_event(
        hci::AuthenticationCompleteBuilder {
            status: hci::ErrorCode::Success,
            connection_handle: ctx.peer_handle(),
        }
        .build(),
        hci::AuthenticationCompleteBuilder { status, connection_handle: ctx.peer_handle() }.build(),
    );
}

@@ -130,33 +95,12 @@ pub async fn respond(ctx: &impl Context) {
            ctx.send_lmp_packet(lmp::SresBuilder { transaction_id: 0, authentication_rsp: [0; 4] }.build());
        },
        Either::Right(pairing) => {
            let result = match pairing {
            let _result = match pairing {
                Either::Left(io_capability_request) =>
                    secure_simple_pairing::respond(ctx, io_capability_request).await,
                Either::Right(in_rand) =>
                    legacy_pairing::respond(ctx, in_rand).await,
            };

            if result.is_err() {
                return;
            }

            // Link Key Calculation

            let _random_number = ctx.receive_lmp_packet::<lmp::AuRandPacket>().await;
            // TODO: Resolve authentication challenge
            ctx.send_lmp_packet(lmp::SresBuilder { transaction_id: 0, authentication_rsp: [0; 4] }.build());

            send_authentication_challenge(ctx, 0).await;

            ctx.send_hci_event(
                hci::LinkKeyNotificationBuilder {
                    bd_addr: ctx.peer_address(),
                    key_type: hci::KeyType::AuthenticatedP192,
                    link_key: [0; 16],
                }
                .build(),
            );
        }
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -33,3 +33,22 @@ pub async fn respond(ctx: &impl Context) {
        .build(),
    );
}

pub async fn supported_on_both_page1(
    ctx: &impl Context,
    feature: crate::packets::hci::LMPFeaturesPage1Bits,
) -> bool {
    use num_traits::ToPrimitive;
    let feature_mask = feature.to_u64().unwrap();
    let local_supported = ctx.extended_features(1) & feature_mask != 0;
    // Lazy peer features
    let peer_supported = async move {
        let page = if let Some(page) = ctx.peer_extended_features(1) {
            page
        } else {
            crate::procedure::features::initiate(ctx, 1).await
        };
        page & feature_mask != 0
    };
    local_supported && peer_supported.await
}
+35 −1
Original line number Diff line number Diff line
// Bluetooth Core, Vol 2, Part C, 4.2.2

use crate::packets::{hci, lmp};
use crate::procedure::Context;
use crate::procedure::{authentication, Context};

use crate::num_hci_command_packets;

@@ -30,6 +30,23 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {

    let _ = ctx.receive_lmp_packet::<lmp::CombKeyPacket>().await;

    // Post pairing authentication
    let link_key = [0; 16];
    let auth_result = authentication::send_challenge(ctx, 0, link_key).await;
    authentication::receive_challenge(ctx, link_key).await;

    if auth_result.is_err() {
        return Err(());
    }
    ctx.send_hci_event(
        hci::LinkKeyNotificationBuilder {
            bd_addr: ctx.peer_address(),
            key_type: hci::KeyType::Combination,
            link_key,
        }
        .build(),
    );

    Ok(())
}

@@ -55,5 +72,22 @@ pub async fn respond(ctx: &impl Context, _request: lmp::InRandPacket) -> Result<

    ctx.send_lmp_packet(lmp::CombKeyBuilder { transaction_id: 0, random_number: [0; 16] }.build());

    // Post pairing authentication
    let link_key = [0; 16];
    authentication::receive_challenge(ctx, link_key).await;
    let auth_result = authentication::send_challenge(ctx, 0, link_key).await;

    if auth_result.is_err() {
        return Err(());
    }
    ctx.send_hci_event(
        hci::LinkKeyNotificationBuilder {
            bd_addr: ctx.peer_address(),
            key_type: hci::KeyType::Combination,
            link_key,
        }
        .build(),
    );

    Ok(())
}
+132 −30
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ use num_traits::{FromPrimitive, ToPrimitive};

use crate::either::Either;
use crate::packets::{hci, lmp};
use crate::procedure::Context;
use crate::procedure::{authentication, features, Context};

use crate::num_hci_command_packets;

@@ -23,10 +23,50 @@ fn has_mitm(requirements: hci::AuthenticationRequirements) -> bool {

enum AuthenticationMethod {
    OutOfBand,
    NumericComparaison,
    NumericComparaisonJustWork,
    NumericComparaisonUserConfirm,
    PasskeyEntry,
}

const P192_PUBLIC_KEY_SIZE: usize = 48;
const P256_PUBLIC_KEY_SIZE: usize = 64;

enum PublicKey {
    P192([u8; P192_PUBLIC_KEY_SIZE]),
    P256([u8; P256_PUBLIC_KEY_SIZE]),
}

impl PublicKey {
    fn generate(key_size: usize) -> Option<PublicKey> {
        match key_size {
            P192_PUBLIC_KEY_SIZE => Some(PublicKey::P192([0; P192_PUBLIC_KEY_SIZE])),
            P256_PUBLIC_KEY_SIZE => Some(PublicKey::P256([0; P256_PUBLIC_KEY_SIZE])),
            _ => None,
        }
    }

    fn as_slice(&self) -> &[u8] {
        match self {
            PublicKey::P192(inner) => inner,
            PublicKey::P256(inner) => inner,
        }
    }

    fn as_mut_slice(&mut self) -> &mut [u8] {
        match self {
            PublicKey::P192(inner) => inner,
            PublicKey::P256(inner) => inner,
        }
    }

    fn get_size(&self) -> usize {
        match self {
            PublicKey::P192(_) => P192_PUBLIC_KEY_SIZE,
            PublicKey::P256(_) => P256_PUBLIC_KEY_SIZE,
        }
    }
}

#[derive(Clone, Copy)]
struct AuthenticationParams {
    io_capability: hci::IoCapability,
@@ -47,20 +87,37 @@ fn authentication_method(
    } else if !has_mitm(initiator.authentication_requirements)
        && !has_mitm(responder.authentication_requirements)
    {
        AuthenticationMethod::NumericComparaison
        AuthenticationMethod::NumericComparaisonJustWork
    } else if (initiator.io_capability == KeyboardOnly
        && responder.io_capability != NoInputNoOutput)
        || (responder.io_capability == KeyboardOnly && initiator.io_capability != NoInputNoOutput)
    {
        AuthenticationMethod::PasskeyEntry
    } else if initiator.io_capability == DisplayYesNo && responder.io_capability == DisplayYesNo {
        AuthenticationMethod::NumericComparaisonUserConfirm
    } else {
        AuthenticationMethod::NumericComparaison
        AuthenticationMethod::NumericComparaisonJustWork
    }
}

const P192_PUBLIC_KEY_SIZE: usize = 48;
// Bluetooth Core, Vol 3, Part C, 5.2.2.6
fn link_key_type(auth_method: AuthenticationMethod, public_key: PublicKey) -> hci::KeyType {
    use hci::KeyType::*;
    use AuthenticationMethod::*;

    match (public_key, auth_method) {
        (PublicKey::P256(_), OutOfBand | PasskeyEntry | NumericComparaisonUserConfirm) => {
            AuthenticatedP256
        }
        (PublicKey::P192(_), OutOfBand | PasskeyEntry | NumericComparaisonUserConfirm) => {
            AuthenticatedP192
        }
        (PublicKey::P256(_), NumericComparaisonJustWork) => UnauthenticatedP256,
        (PublicKey::P192(_), NumericComparaisonJustWork) => UnauthenticatedP192,
    }
}

async fn send_public_key(ctx: &impl Context, transaction_id: u8, key: &[u8; P192_PUBLIC_KEY_SIZE]) {
async fn send_public_key(ctx: &impl Context, transaction_id: u8, public_key: PublicKey) {
    // TODO: handle error
    let _ = ctx
        .send_accepted_lmp_packet(
@@ -68,13 +125,13 @@ async fn send_public_key(ctx: &impl Context, transaction_id: u8, key: &[u8; P192
                transaction_id,
                major_type: 1,
                minor_type: 1,
                payload_length: P192_PUBLIC_KEY_SIZE as u8,
                payload_length: public_key.get_size() as u8,
            }
            .build(),
        )
        .await;

    for chunk in key.chunks(16) {
    for chunk in public_key.as_slice().chunks(16) {
        // TODO: handle error
        let _ = ctx
            .send_accepted_lmp_packet(
@@ -85,16 +142,16 @@ async fn send_public_key(ctx: &impl Context, transaction_id: u8, key: &[u8; P192
    }
}

async fn receive_public_key(ctx: &impl Context, transaction_id: u8) -> [u8; P192_PUBLIC_KEY_SIZE] {
    let _ = ctx.receive_lmp_packet::<lmp::EncapsulatedHeaderPacket>().await;
async fn receive_public_key(ctx: &impl Context, transaction_id: u8) -> PublicKey {
    let key_size: usize =
        ctx.receive_lmp_packet::<lmp::EncapsulatedHeaderPacket>().await.get_payload_length().into();
    let mut key = PublicKey::generate(key_size).unwrap();

    ctx.send_lmp_packet(
        lmp::AcceptedBuilder { transaction_id, accepted_opcode: lmp::Opcode::EncapsulatedHeader }
            .build(),
    );

    let mut key = [0; P192_PUBLIC_KEY_SIZE];

    for chunk in key.chunks_mut(16) {
    for chunk in key.as_mut_slice().chunks_mut(16) {
        let payload = ctx.receive_lmp_packet::<lmp::EncapsulatedPayloadPacket>().await;
        chunk.copy_from_slice(payload.get_data().as_slice());
        ctx.send_lmp_packet(
@@ -363,16 +420,23 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
    };

    // Public Key Exchange
    {
        let public_key = [0; P192_PUBLIC_KEY_SIZE];
        send_public_key(ctx, 0, &public_key).await;
        let _key = receive_public_key(ctx, 0).await;
    }
    let peer_public_key = {
        use hci::LMPFeaturesPage1Bits::SecureConnectionsHostSupport;
        let key = if features::supported_on_both_page1(ctx, SecureConnectionsHostSupport).await {
            PublicKey::generate(P256_PUBLIC_KEY_SIZE).unwrap()
        } else {
            PublicKey::generate(P192_PUBLIC_KEY_SIZE).unwrap()
        };
        send_public_key(ctx, 0, key).await;
        receive_public_key(ctx, 0).await
    };

    // Authentication Stage 1
    let auth_method = authentication_method(initiator, responder);
    let result: Result<(), ()> = async {
        match authentication_method(initiator, responder) {
            AuthenticationMethod::NumericComparaison => {
        match auth_method {
            AuthenticationMethod::NumericComparaisonJustWork
            | AuthenticationMethod::NumericComparaisonUserConfirm => {
                send_commitment(ctx, true).await;

                user_confirmation_request(ctx).await?;
@@ -458,6 +522,24 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
        .build(),
    );

    // Link Key Calculation
    let link_key = [0; 16];
    let auth_result = authentication::send_challenge(ctx, 0, link_key).await;
    authentication::receive_challenge(ctx, link_key).await;

    if auth_result.is_err() {
        return Err(());
    }

    ctx.send_hci_event(
        hci::LinkKeyNotificationBuilder {
            bd_addr: ctx.peer_address(),
            key_type: link_key_type(auth_method, peer_public_key),
            link_key,
        }
        .build(),
    );

    Ok(())
}

@@ -515,16 +597,18 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReqPacket) ->
    };

    // Public Key Exchange
    {
        let public_key = [0; P192_PUBLIC_KEY_SIZE];
        let _key = receive_public_key(ctx, 0).await;
        send_public_key(ctx, 0, &public_key).await;
    }
    let peer_public_key = {
        let peer_public_key = receive_public_key(ctx, 0).await;
        let public_key = PublicKey::generate(peer_public_key.get_size()).unwrap();
        send_public_key(ctx, 0, public_key).await;
        peer_public_key
    };

    // Authentication Stage 1

    let negative_user_confirmation = match authentication_method(initiator, responder) {
        AuthenticationMethod::NumericComparaison => {
    let auth_method = authentication_method(initiator, responder);
    let negative_user_confirmation = match auth_method {
        AuthenticationMethod::NumericComparaisonJustWork
        | AuthenticationMethod::NumericComparaisonUserConfirm => {
            receive_commitment(ctx, true).await;

            let user_confirmation = user_confirmation_request(ctx).await;
@@ -616,6 +700,24 @@ pub async fn respond(ctx: &impl Context, request: lmp::IoCapabilityReqPacket) ->
        .build(),
    );

    // Link Key Calculation
    let link_key = [0; 16];
    authentication::receive_challenge(ctx, link_key).await;
    let auth_result = authentication::send_challenge(ctx, 0, link_key).await;

    if auth_result.is_err() {
        return Err(());
    }

    ctx.send_hci_event(
        hci::LinkKeyNotificationBuilder {
            bd_addr: ctx.peer_address(),
            key_type: link_key_type(auth_method, peer_public_key),
            link_key,
        }
        .build(),
    );

    Ok(())
}

@@ -639,7 +741,7 @@ mod tests {
            assert!(size < limit)
        }

        assert_max_size(procedure, 250);
        assert_max_size(procedure, 512);
    }

    #[test]