rust/bt-gatt: Add module for PII-sensitive ops Adds a trait for obtaining the bluetooth address of a peerid into a new module for operations that involve privacy-sensitive information. Also makes that trait an argument for the broadcast assistant's `add_broadcast_source` method, so that the address can be filled in. Fixed: 416319216 Change-Id: I81da037194a04536be2dc7dfdb98ce0bee100fbb Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/2440 Reviewed-by: Marie Janssen <jamuraa@google.com>
diff --git a/rust/bt-broadcast-assistant/src/assistant/peer.rs b/rust/bt-broadcast-assistant/src/assistant/peer.rs index ad101cf..5131604 100644 --- a/rust/bt-broadcast-assistant/src/assistant/peer.rs +++ b/rust/bt-broadcast-assistant/src/assistant/peer.rs
@@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use futures::stream::FusedStream; +use bt_gatt::pii::GetPeerAddr; use futures::Stream; +use futures::stream::FusedStream; use std::sync::Arc; use thiserror::Error; @@ -32,6 +33,9 @@ #[error("Broadcast source with peer id ({0}) does not exist")] DoesNotExist(PeerId), + #[error("Failed to lookup address for peer id ({0}): {1:?}")] + AddressLookupError(PeerId, bt_gatt::types::Error), + #[error("Packet error: {0}")] PacketError(#[from] PacketError), } @@ -86,19 +90,29 @@ /// /// * `broadcast_source_pid` - peer id of the braodcast source that's to be /// added to this scan delegator peer + /// * `address_lookup` - An implementation of [`GetPeerAddr`] that will + /// be used to look up the peer's address. /// * `pa_sync` - pa sync mode the peer should attempt to be in /// * `bis_sync` - desired BIG to BIS synchronization information. If the /// set is empty, no preference value is used for all the BIGs pub async fn add_broadcast_source( &mut self, source_peer_id: PeerId, + address_lookup: &impl GetPeerAddr, pa_sync: PaSync, bis_sync: BigToBisSync, ) -> Result<(), Error> { - let broadcast_source = self + let mut broadcast_source = self .broadcast_sources .get_by_peer_id(&source_peer_id) .ok_or(Error::DoesNotExist(source_peer_id))?; + + let (broadcast_addr, broadcast_addr_type) = address_lookup + .get_peer_address(source_peer_id) + .await + .map_err(|err| Error::AddressLookupError(source_peer_id, err))?; + broadcast_source.with_address(broadcast_addr).with_address_type(broadcast_addr_type); + if !broadcast_source.into_add_source() { return Err(Error::NotEnoughInfo(source_peer_id)); } @@ -175,12 +189,13 @@ use super::*; use assert_matches::assert_matches; - use futures::{pin_mut, FutureExt}; + use bt_gatt::pii::StaticPeerAddr; + use futures::{FutureExt, pin_mut}; use std::collections::HashSet; use std::task::Poll; use bt_common::core::{AddressType, AdvertisingSetId}; - use bt_gatt::test_utils::{FakeClient, FakePeerService, FakeTypes}; + use bt_gatt::test_utils::{FakeClient, FakeGetPeerAddr, FakePeerService, FakeTypes}; use bt_gatt::types::{ AttributePermissions, CharacteristicProperties, CharacteristicProperty, Handle, }; @@ -260,33 +275,50 @@ { let fut = peer.add_broadcast_source( PeerId(1001), + &FakeGetPeerAddr, PaSync::SyncPastUnavailable, HashSet::new(), ); pin_mut!(fut); let polled = fut.poll_unpin(&mut noop_cx); - assert_matches!(polled, Poll::Ready(Err(_))); + assert_matches!(polled, Poll::Ready(Err(Error::DoesNotExist(_)))); } let _ = broadcast_source.merge_broadcast_source_data( &PeerId(1001), &BroadcastSource::default() - .with_address([1, 2, 3, 4, 5, 6]) - .with_address_type(AddressType::Public) .with_advertising_sid(AdvertisingSetId(1)) .with_broadcast_id(BroadcastId::try_from(1001).unwrap()), ); - // Should fail because not enough information. + // Should fail because peer address couldn't be looked up. { + let address_lookup = + StaticPeerAddr::new_for_peer(PeerId(1002), [1, 2, 3, 4, 5, 6], AddressType::Public); let fut = peer.add_broadcast_source( PeerId(1001), + &address_lookup, PaSync::SyncPastUnavailable, HashSet::new(), ); pin_mut!(fut); let polled = fut.poll_unpin(&mut noop_cx); - assert_matches!(polled, Poll::Ready(Err(_))); + assert_matches!(polled, Poll::Ready(Err(Error::AddressLookupError(_, _)))); + } + + // Should fail because not enough information. + { + let address_lookup = + StaticPeerAddr::new_for_peer(PeerId(1001), [1, 2, 3, 4, 5, 6], AddressType::Public); + let fut = peer.add_broadcast_source( + PeerId(1001), + &address_lookup, + PaSync::SyncPastUnavailable, + HashSet::new(), + ); + pin_mut!(fut); + let polled = fut.poll_unpin(&mut noop_cx); + assert_matches!(polled, Poll::Ready(Err(Error::NotEnoughInfo(_)))); } } }
diff --git a/rust/bt-gatt/src/lib.rs b/rust/bt-gatt/src/lib.rs index f5adefd..74c7f3b 100644 --- a/rust/bt-gatt/src/lib.rs +++ b/rust/bt-gatt/src/lib.rs
@@ -14,6 +14,8 @@ pub mod central; pub use central::Central; +pub mod pii; + #[cfg(any(test, feature = "test-utils"))] pub mod test_utils;
diff --git a/rust/bt-gatt/src/pii.rs b/rust/bt-gatt/src/pii.rs new file mode 100644 index 0000000..f165db6 --- /dev/null +++ b/rust/bt-gatt/src/pii.rs
@@ -0,0 +1,55 @@ +// Copyright 2025 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::future::Future; + +use bt_common::{ + PeerId, + core::{Address, AddressType}, +}; + +use crate::types::*; + +/// Takes a peer address and queries an underlying service for its actual +/// bluetooth address and address type. Used for adding broadcast sources. +pub trait GetPeerAddr { + /// Resolve peer ID to peer address and address type. + fn get_peer_address( + &self, + peer_id: PeerId, + ) -> impl Future<Output = Result<(Address, AddressType)>>; +} + +/// Helper for when the peer's address is known. Always returns the given +/// address, if any. +pub struct StaticPeerAddr { + peer_id: Option<PeerId>, + address: Address, + address_type: AddressType, +} + +impl StaticPeerAddr { + /// Returns a [`StaticPeerAddr`] that always returns the given address, + /// regardless of the `peer_id` being looked up. + pub fn new(address: Address, address_type: AddressType) -> Self { + Self { peer_id: None, address, address_type } + } + + /// Returns a [`StaticPeerAddr`] that will only return successfully if the + /// `peer_id` matches. + pub fn new_for_peer(peer_id: PeerId, address: Address, address_type: AddressType) -> Self { + Self { peer_id: Some(peer_id), address, address_type } + } +} + +impl GetPeerAddr for StaticPeerAddr { + async fn get_peer_address(&self, peer_id: PeerId) -> Result<(Address, AddressType)> { + if let Some(validated_peer_id) = self.peer_id { + if peer_id != validated_peer_id { + return Err(Error::PeerNotRecognized(peer_id)); + } + } + return Ok((self.address, self.address_type)); + } +}
diff --git a/rust/bt-gatt/src/test_utils.rs b/rust/bt-gatt/src/test_utils.rs index bc3d88f..15ad2fa 100644 --- a/rust/bt-gatt/src/test_utils.rs +++ b/rust/bt-gatt/src/test_utils.rs
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use bt_common::core::{Address, AddressType}; use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use futures::future::{ready, Ready}; use futures::Stream; @@ -14,8 +15,9 @@ use crate::central::ScanResult; use crate::client::CharacteristicNotification; +use crate::pii::GetPeerAddr; use crate::server::{self, LocalService, ReadResponder, ServiceDefinition, WriteResponder}; -use crate::{types::*, GattTypes, ServerTypes}; +use crate::{GattTypes, ServerTypes, types::*}; #[derive(Default)] struct FakePeerServiceInner { @@ -256,6 +258,26 @@ } } +/// Implements a fake [`GetPeerAddr`] that just converts the peer_id into a +/// public [`Address`] based on the given peer_id. +pub struct FakeGetPeerAddr; + +impl GetPeerAddr for FakeGetPeerAddr { + async fn get_peer_address(&self, peer_id: PeerId) -> Result<(Address, AddressType)> { + Ok(( + [ + peer_id.0 as u8, + ((peer_id.0 >> 8) & 0xff) as u8, + ((peer_id.0 >> 16) & 0xff) as u8, + ((peer_id.0 >> 24) & 0xff) as u8, + ((peer_id.0 >> 32) & 0xff) as u8, + ((peer_id.0 >> 48) & 0xff) as u8, + ], + AddressType::Public, + )) + } +} + pub struct FakeTypes {} impl GattTypes for FakeTypes {