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

Commit 5876607c authored by Josh Wu's avatar Josh Wu
Browse files

RootCanal: Authentication challenge

Note that this is not totally matches spec. It only makes
sure that link key must be matched and a random number is
used for verification.

Tag: #feature
Test: gd/cert/run ---host
Bug: 235777894
Change-Id: I1e5c13e2e28a79b2ccbf08feb306a15d6f57a0ec
parent 08efb17b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ cc_binary {
        "liblog",
        "libutils",
        "libprotobuf-cpp-lite",
        "libcrypto",
        "libssl",
    ],
    cflags: [
        "-fvisibility=hidden",
@@ -96,6 +98,8 @@ cc_library_shared {
        "liblog",
        "libutils",
        "libprotobuf-cpp-lite",
        "libcrypto",
        "libssl",
    ],
    cflags: [
        "-Wall",
+2 −0
Original line number Diff line number Diff line
@@ -176,6 +176,8 @@ cc_binary_host {
    shared_libs: [
        "liblog",
        "libunwindstack",
        "libcrypto",
        "libssl",
    ],
    whole_static_libs: [
        "libbt-rootcanal",
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ rust_ffi {
        "libbt_packets",
        "libbytes",
        "libnum_traits",
        "libopenssl",
        "libthiserror",
        "libpin_utils",
    ],
@@ -56,6 +57,7 @@ rust_test_host {
        "libbt_packets",
        "libbytes",
        "libnum_traits",
        "libopenssl",
        "libthiserror",
        "libpin_utils",
    ],
+98 −47
Original line number Diff line number Diff line
// Bluetooth Core, Vol 2, Part C, 4.2.1

use num_traits::ToPrimitive;
use openssl::rand::rand_bytes;
use openssl::symm::{encrypt, Cipher};
use std::convert::TryInto;

use crate::either::Either;
use crate::num_hci_command_packets;
@@ -10,6 +13,11 @@ use crate::procedure::legacy_pairing;
use crate::procedure::secure_simple_pairing;
use crate::procedure::Context;

fn generate_sres(random_number: &[u8; 16], key: &[u8; 16]) -> [u8; 4] {
    let cipher = Cipher::aes_128_cbc();
    encrypt(cipher, key, None, random_number).unwrap()[0..4].try_into().unwrap()
}

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;
@@ -25,28 +33,45 @@ async fn secure_simple_pairing_supported(ctx: &impl Context) -> bool {
    local_supported && peer_supported.await
}

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 send_authentication_challenge(
    ctx: &impl Context,
    transaction_id: u8,
    link_key: [u8; 16],
) -> Result<(), ()> {
    let mut random_number = [0; 16];
    rand_bytes(&mut random_number).unwrap();
    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) => {
            if *response.get_authentication_rsp() == generate_sres(&random_number, &link_key) {
                Ok(())
            } else {
                Err(())
            }
        }
        Either::Right(_) => Err(()),
    }
}

pub async fn initiate(ctx: &impl Context) {
    let _ = ctx.receive_hci_command::<hci::AuthenticationRequestedPacket>().await;
    ctx.send_hci_event(
        hci::AuthenticationRequestedStatusBuilder {
            num_hci_command_packets,
            status: hci::ErrorCode::Success,
pub async fn receive_authentication_challenge(ctx: &impl Context, link_key: [u8; 16]) {
    let au_rand = ctx.receive_lmp_packet::<lmp::AuRandPacket>().await;
    ctx.send_lmp_packet(
        lmp::SresBuilder {
            transaction_id: 0,
            authentication_rsp: generate_sres(au_rand.get_random_number(), &link_key),
        }
        .build(),
    );
}

pub async fn request_link_key_from_host(ctx: &impl Context) -> Option<[u8; 16]> {
    ctx.send_hci_event(hci::LinkKeyRequestBuilder { bd_addr: ctx.peer_address() }.build());

    let pairing = match ctx.receive_hci_command::<Either<
    match ctx.receive_hci_command::<Either<
        hci::LinkKeyRequestReplyPacket,
        hci::LinkKeyRequestNegativeReplyPacket,
    >>().await {
        Either::Left(_reply) => {
        Either::Left(reply) => {
            ctx.send_hci_event(
                hci::LinkKeyRequestReplyCompleteBuilder {
                    num_hci_command_packets,
@@ -55,7 +80,7 @@ pub async fn initiate(ctx: &impl Context) {
                }
                .build(),
            );
            false
            Some(*reply.get_link_key())
        },
        Either::Right(_) => {
            ctx.send_hci_event(
@@ -66,14 +91,34 @@ pub async fn initiate(ctx: &impl Context) {
                }
                .build(),
            );
            None
        }
    }
}

pub async fn initiate(ctx: &impl Context) {
    let _ = ctx.receive_hci_command::<hci::AuthenticationRequestedPacket>().await;
    ctx.send_hci_event(
        hci::AuthenticationRequestedStatusBuilder {
            num_hci_command_packets,
            status: hci::ErrorCode::Success,
        }
        .build(),
    );

    let (pairing, link_key) = match request_link_key_from_host(ctx).await {
        Some(host_link_key) => (false, host_link_key),
        None => {
            // No link key, start pairing
            let result = if secure_simple_pairing_supported(ctx).await {
                secure_simple_pairing::initiate(ctx).await
            } else {
                legacy_pairing::initiate(ctx).await
            };

            if result.is_err() {
            if let Ok(pairing_link_key) = result {
                (true, pairing_link_key)
            } else {
                ctx.send_hci_event(
                    hci::AuthenticationCompleteBuilder {
                        status: hci::ErrorCode::AuthenticationFailure,
@@ -83,37 +128,33 @@ pub async fn initiate(ctx: &impl Context) {
                );
                return;
            }
            true
        }
    };

    send_authentication_challenge(ctx, 0).await;
    let auth_response = send_authentication_challenge(ctx, 0, link_key).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(),
        );
    if pairing && auth_response.is_ok() {
        // Bluetooth Core, Vol 2, Part C, 4.2.19
        receive_authentication_challenge(ctx, link_key).await;

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

    let status = if auth_response.is_ok() {
        hci::ErrorCode::Success
    } else {
        hci::ErrorCode::AuthenticationFailure
    };
    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(),
    );
}

@@ -124,10 +165,16 @@ pub async fn respond(ctx: &impl Context) {
    >>()
    .await
    {
        Either::Left(_random_number) => {
            // TODO: Resolve authentication challenge
            // TODO: Ask for link key
            ctx.send_lmp_packet(lmp::SresBuilder { transaction_id: 0, authentication_rsp: [0; 4] }.build());
        Either::Left(au_rand) => {
            match request_link_key_from_host(ctx).await {
                Some(link_key) => {
                    let remote_random_number = au_rand.get_random_number();
                    ctx.send_lmp_packet(lmp::SresBuilder { transaction_id: 0, authentication_rsp: generate_sres(remote_random_number, &link_key) }.build());
                },
                None => {
                    ctx.send_lmp_packet(lmp::NotAcceptedBuilder { transaction_id: 0, not_accepted_opcode: lmp::Opcode::AuRand, error_code: hci::ErrorCode::PinOrKeyMissing.to_u8().unwrap() }.build());
                }
            }
        },
        Either::Right(pairing) => {
            let result = match pairing {
@@ -142,21 +189,25 @@ pub async fn respond(ctx: &impl Context) {
            }

            // Link Key Calculation
            let link_key = if let Ok(link_key) = result {
                link_key
             } else {
                return;
             };
            receive_authentication_challenge(ctx, link_key).await;
            let auth_result = send_authentication_challenge(ctx, 0, link_key).await;

            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;

            if auth_result.is_ok() {
                ctx.send_hci_event(
                    hci::LinkKeyNotificationBuilder {
                        bd_addr: ctx.peer_address(),
                        key_type: hci::KeyType::AuthenticatedP192,
                    link_key: [0; 16],
                        link_key,
                    }
                    .build(),
                );
            }
            // TODO: Handle error
        }
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ use crate::procedure::Context;

use crate::num_hci_command_packets;

pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {
pub async fn initiate(ctx: &impl Context) -> Result<([u8; 16]), ()> {
    ctx.send_hci_event(hci::PinCodeRequestBuilder { bd_addr: ctx.peer_address() }.build());

    let _pin_code = ctx.receive_hci_command::<hci::PinCodeRequestReplyPacket>().await;
@@ -30,10 +30,10 @@ pub async fn initiate(ctx: &impl Context) -> Result<(), ()> {

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

    Ok(())
    Ok([0; 16])
}

pub async fn respond(ctx: &impl Context, _request: lmp::InRandPacket) -> Result<(), ()> {
pub async fn respond(ctx: &impl Context, _request: lmp::InRandPacket) -> Result<([u8; 16]), ()> {
    ctx.send_hci_event(hci::PinCodeRequestBuilder { bd_addr: ctx.peer_address() }.build());

    let _pin_code = ctx.receive_hci_command::<hci::PinCodeRequestReplyPacket>().await;
@@ -55,5 +55,5 @@ 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());

    Ok(())
    Ok([0; 16])
}
Loading