use crate::accessory::BleAccessory;
use crate::broadcast_state::BleBroadcastState;
use crate::discovery::DiscoveredBleAccessory;
use crate::error::Result;
use crate::pairing;
use hap_crypto::AccessoryPairing;
use hap_crypto::ControllerKeypair;
use std::sync::Arc;
const PAIR_SETUP_CHAR: &str = "0000004c-0000-1000-8000-0026bb765291";
const PAIR_VERIFY_CHAR: &str = "0000004e-0000-1000-8000-0026bb765291";
const PAIRINGS_CHAR: &str = "00000050-0000-1000-8000-0026bb765291";
const SERVICE_SIGNATURE_CHAR: &str = "000000a5-0000-1000-8000-0026bb765291";
const PROTOCOL_INFO_SERVICE: &str = "000000a2-0000-1000-8000-0026bb765291";
const GENERATE_BROADCAST_KEY_BODY: [u8; 2] = [0x01, 0x00];
pub struct Paired {
pub accessory: BleAccessory,
pub pairing: AccessoryPairing,
pub broadcast: BleBroadcastState,
}
pub struct BleController {
keypair: ControllerKeypair,
}
impl BleController {
pub fn new(keypair: ControllerKeypair) -> Self {
Self { keypair }
}
pub fn generate(id: String) -> Self {
Self {
keypair: ControllerKeypair::generate(id),
}
}
pub fn keypair(&self) -> &ControllerKeypair {
&self.keypair
}
pub async fn pair(
&self,
gatt: Arc<dyn crate::gatt::GattConnection>,
_accessory: &DiscoveredBleAccessory,
setup_code: &str,
) -> Result<Paired> {
let frag = gatt.max_write().await;
let setup_iid = gatt.instance_id(PAIR_SETUP_CHAR).await?;
let pairing = pairing::pair_setup(
gatt.as_ref(),
PAIR_SETUP_CHAR,
setup_iid,
setup_code,
self.keypair.clone(),
frag,
)
.await?;
let accessory = self.verify_and_build(gatt, &pairing, 0).await?;
let broadcast = accessory.broadcast_state().await;
Ok(Paired {
accessory,
pairing,
broadcast,
})
}
pub async fn connect(
&self,
gatt: Arc<dyn crate::gatt::GattConnection>,
pairing: &AccessoryPairing,
broadcast: Option<BleBroadcastState>,
) -> Result<BleAccessory> {
let initial_gsn = broadcast.as_ref().map_or(0, |b| b.gsn);
self.verify_and_build(gatt, pairing, initial_gsn).await
}
async fn verify_and_build(
&self,
gatt: Arc<dyn crate::gatt::GattConnection>,
pairing: &AccessoryPairing,
initial_gsn: u16,
) -> Result<BleAccessory> {
let frag = gatt.max_write().await;
let services = gatt.enumerate().await?;
let accessories = crate::db::build_db(gatt.as_ref(), &services, frag).await?;
let verify_iid = iid_of(&services, PAIR_VERIFY_CHAR)?;
let (mut session, broadcast_key) = pairing::pair_verify(
gatt.as_ref(),
PAIR_VERIFY_CHAR,
verify_iid,
&self.keypair,
pairing,
frag,
)
.await?;
if let Some(sig_iid) = protocol_info_signature_iid(&services) {
let _ = crate::pdu::request_secure(
gatt.as_ref(),
&mut session,
SERVICE_SIGNATURE_CHAR,
crate::pdu::OpCode::ProtocolConfig,
1,
sig_iid,
&GENERATE_BROADCAST_KEY_BODY,
frag,
)
.await;
}
let session_generation = gatt.generation().await;
let pairings_iid = iid_of(&services, PAIRINGS_CHAR)?;
let ctx = crate::accessory::SecureContext {
session,
session_generation,
keypair: self.keypair.clone(),
pairing: pairing.clone(),
verify_char: PAIR_VERIFY_CHAR.to_string(),
verify_iid,
pairings_char: PAIRINGS_CHAR.to_string(),
pairings_iid,
broadcast_key,
initial_gsn,
};
Ok(BleAccessory::new(gatt, ctx, frag, &services, accessories))
}
}
fn protocol_info_signature_iid(services: &[crate::gatt::GattService]) -> Option<u16> {
let svc = services
.iter()
.find(|s| s.uuid.eq_ignore_ascii_case(PROTOCOL_INFO_SERVICE))?;
svc.characteristics
.iter()
.find(|c| c.uuid.eq_ignore_ascii_case(SERVICE_SIGNATURE_CHAR))
.map(|c| c.iid)
}
fn iid_of(services: &[crate::gatt::GattService], char_uuid: &str) -> Result<u16> {
services
.iter()
.flat_map(|s| &s.characteristics)
.find(|c| c.uuid.eq_ignore_ascii_case(char_uuid))
.map(|c| c.iid)
.ok_or(crate::error::BleError::CharacteristicNotFound { aid: 0, iid: 0 })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_sets_identity() {
let c = BleController::generate("11:22:33:44:55:66".into());
assert_eq!(c.keypair().id, "11:22:33:44:55:66");
}
}