From 291eefefbf453123cda2d6a490699d09ab7937ed Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 22 Nov 2024 18:15:03 +0800 Subject: [PATCH 1/6] feat(security_manager): add security types Signed-off-by: Haobo Gu --- host/src/types/mod.rs | 1 + host/src/types/security.rs | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 host/src/types/security.rs diff --git a/host/src/types/mod.rs b/host/src/types/mod.rs index e8b746ae..d7cabcae 100644 --- a/host/src/types/mod.rs +++ b/host/src/types/mod.rs @@ -4,5 +4,6 @@ pub mod gatt_traits; pub(crate) mod l2cap; pub(crate) mod primitives; +pub(crate) mod security; pub mod uuid; diff --git a/host/src/types/security.rs b/host/src/types/security.rs new file mode 100644 index 00000000..d3effae5 --- /dev/null +++ b/host/src/types/security.rs @@ -0,0 +1,85 @@ +use bt_hci::param::AddrKind; + +use crate::Address; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SecurityMode { + NoAccess, + Open, + JustWorks, + Mitm, + LescMitm, + Signed, + SignedMitm, +} + +impl Default for SecurityMode { + fn default() -> Self { + Self::Open + } +} + +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct MasterId { + /// Encrypted diversifier + pub ediv: u16, + /// Random number + pub rand: [u8; 8], +} + +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EncryptionInfo { + /// Long term key + pub ltk: [u8; 16], + pub flags: u8, +} + +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IdentityResolutionKey { + irk: [u8; 16], +} + +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IdentityKey { + /// Identity resolution key + pub irk: IdentityResolutionKey, + /// Address + pub addr: Address, +} + + +impl IdentityKey { + pub fn is_match(&self, addr: Address) -> bool { + match addr.kind { + AddrKind::PUBLIC | AddrKind::RANDOM => { + if self.addr.kind == addr.kind && self.addr.addr == addr.addr { + true + } else { + false + } + }, + AddrKind::RESOLVABLE_PRIVATE_OR_RANDOM => { + todo!("Random address resolution") + } + AddrKind::RESOLVABLE_PRIVATE_OR_PUBLIC | AddrKind::ANONYMOUS_ADV => false, + _ => panic!("Invalid address kind"), + } + } + + pub fn from_addr(addr: Address) -> Self { + Self { + irk: Default::default(), + addr, + } + } +} + +fn random_address_hash(key: IdentityResolutionKey) -> [u8; 6] { + todo!() +} + From e1e804cf94807a9f7136eb77ce39de1fc47df744 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Mon, 25 Nov 2024 16:20:04 +0800 Subject: [PATCH 2/6] wip(sm): add struct security manager Signed-off-by: Haobo Gu --- host/Cargo.toml | 1 + host/src/lib.rs | 4 ++ host/src/security_manager.rs | 97 ++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 host/src/security_manager.rs diff --git a/host/Cargo.toml b/host/Cargo.toml index 5c0738cf..81b30db9 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -9,6 +9,7 @@ categories = ["embedded", "hardware-support", "no-std"] resolver = "2" [dependencies] +bitfield-struct = "0.9.2" bt-hci = { version = "0.1.1", features = ["embassy-time"] } embedded-io-async = { version = "0.6" } embedded-io = { version = "0.6" } diff --git a/host/src/lib.rs b/host/src/lib.rs index 26c43ed1..a93a80bd 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -22,6 +22,7 @@ use crate::channel_manager::{ChannelStorage, PacketChannel}; use crate::connection_manager::{ConnectionStorage, EventChannel}; use crate::l2cap::sar::SarType; use crate::packet_pool::{PacketPool, Qos}; +use crate::security_manager::SecurityManagerError; mod fmt; @@ -39,6 +40,7 @@ mod cursor; pub mod packet_pool; mod pdu; pub mod peripheral; +mod security_manager; pub mod types; pub use packet_pool::Qos as PacketQos; @@ -135,6 +137,8 @@ pub enum Error { HciDecode(FromHciBytesError), /// Error from the Attribute Protocol. Att(AttErrorCode), + /// Error from the security manager + Security(SecurityManagerError), /// Insufficient space in the buffer. InsufficientSpace, /// Invalid value. diff --git a/host/src/security_manager.rs b/host/src/security_manager.rs new file mode 100644 index 00000000..0b1561fb --- /dev/null +++ b/host/src/security_manager.rs @@ -0,0 +1,97 @@ +use bitfield_struct::bitfield; +use bt_hci::controller::Controller; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum IoCapabilities { + DisplayOnly = 0, + DisplayYesNo = 1, + KeyboardOnly = 2, + NoInputNoOutput = 3, + KeyboardDisplay = 4, +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum SecurityManagerError { + PasskeyEntryFailed = 1, + OobNotAvailable, + AuthenticationRequirements, + ConfirmValueFailed, + PairingNotSupported, + EncryptionKeySize, + CommandNotSupported, + UnspecifiedReason, + RepeatedAttempts, + InvalidParameters, + DHKeyCheckFailed, + NumericComparisonFailed, + BrEdrPairingInProgress, + GenerationNotAllowed, + KeyRejected, +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum OobDataFlag { + NotPresent = 0, + Present = 1, +} + +#[bitfield(u8)] +pub struct AuthReq { + #[bits(2)] + bonding_flags: u8, + #[bits(1)] + mitm: bool, + #[bits(1)] + sc: bool, + #[bits(1)] + keypress: bool, + #[bits(1)] + ct2: bool, + #[bits(2)] + rfu: u8, +} + +const SM_PAIRING_REQUEST: u8 = 0x01; +const SM_PAIRING_RESPONSE: u8 = 0x02; +const SM_PAIRING_CONFIRM: u8 = 0x03; +const SM_PAIRING_RANDOM: u8 = 0x04; +const SM_PAIRING_FAILED: u8 = 0x05; +const SM_PAIRING_PUBLIC_KEY: u8 = 0x0c; +const SM_PAIRING_DHKEY_CHECK: u8 = 0x0d; + +/// Security manager that handles SM packet +pub struct SecurityManager<'d, C: Controller> { + controller: &'d C, +} + +impl SecurityManager<'_, C> { + /// Handle packet + pub(crate) async fn handle(&mut self, payload: &[u8]) { + let data = &payload[1..]; + let command = payload[0]; + + match command { + SM_PAIRING_REQUEST => { + todo!() + } + SM_PAIRING_PUBLIC_KEY => { + todo!() + } + SM_PAIRING_RANDOM => { + todo!() + } + SM_PAIRING_DHKEY_CHECK => { + todo!() + } + _ => { + // handle FAILURE + error!("Unknown SM command {}", command); + todo!() + } + } + } +} From 7b6430f8ce8fc91fb48e25ac45fba52dd6eef439 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 6 Dec 2024 18:13:16 +0800 Subject: [PATCH 3/6] feat(sm): add handle_pairing_request Signed-off-by: Haobo Gu --- host/src/security_manager.rs | 69 ++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/host/src/security_manager.rs b/host/src/security_manager.rs index 0b1561fb..6ccdbb04 100644 --- a/host/src/security_manager.rs +++ b/host/src/security_manager.rs @@ -1,5 +1,9 @@ use bitfield_struct::bitfield; -use bt_hci::controller::Controller; +use bt_hci::{ + controller::Controller, + data::{AclBroadcastFlag, AclPacket, AclPacketBoundary}, + param::ConnHandle, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -55,6 +59,15 @@ pub struct AuthReq { rfu: u8, } +fn make_auth_req() -> AuthReq { + AuthReq::new() + .with_bonding_flags(1) + .with_mitm(true) + .with_sc(true) + .with_keypress(false) + .with_ct2(true) +} + const SM_PAIRING_REQUEST: u8 = 0x01; const SM_PAIRING_RESPONSE: u8 = 0x02; const SM_PAIRING_CONFIRM: u8 = 0x03; @@ -70,13 +83,13 @@ pub struct SecurityManager<'d, C: Controller> { impl SecurityManager<'_, C> { /// Handle packet - pub(crate) async fn handle(&mut self, payload: &[u8]) { + pub(crate) async fn handle(&mut self, src_handle: u16, payload: &[u8]) { let data = &payload[1..]; let command = payload[0]; match command { SM_PAIRING_REQUEST => { - todo!() + self.handle_pairing_request(src_handle, data).await.unwrap(); } SM_PAIRING_PUBLIC_KEY => { todo!() @@ -94,4 +107,54 @@ impl SecurityManager<'_, C> { } } } + + fn encode_sm_data<'a>(&self, data: &[u8], target: &'a mut [u8]) -> &'a [u8] { + target.copy_from_slice(&[ + 0x0, 0x0, // len set later + 0x6, 0x0, // channel + ]); + target[4..data.len() + 4].copy_from_slice(data); + let len = data.len() - 4; + target[0] = (len & 0xff) as u8; + target[1] = ((len >> 8) & 0xff) as u8; + &target[..data.len() + 4] + } + + async fn write_sm_data(&self, handle: u16, data: &[u8]) -> Result<(), C::Error> { + let mut sm_data_buf = [0u8; 256]; + let encoded_data = self.encode_sm_data(data, &mut sm_data_buf); + + let packet = AclPacket::new( + ConnHandle::new(handle), + AclPacketBoundary::FirstNonFlushable, + AclBroadcastFlag::PointToPoint, + encoded_data, + ); + self.controller.write_acl_data(&packet).await + } + + async fn handle_pairing_request(&mut self, src_handle: u16, data: &[u8]) -> Result<(), C::Error> { + // TODO: save A/O/I + debug!("[security manager] Handle pairing request"); + let auth_req = data[2]; + let oob_data = data[1] != 0; + let io_cap = data[0]; + + let req_data = [ + SM_PAIRING_RESPONSE, + IoCapabilities::DisplayYesNo as u8, + OobDataFlag::NotPresent as u8, + make_auth_req().0, + 0x10, + 0, + 0, + ]; + self.write_sm_data(src_handle, &req_data).await + } + + async fn handle_pairing_public_key(&mut self, src_handle: u16, pka: &[u8]) -> Result<(), C::Error> { + debug!("[security manager] Handle pairing public key"); + debug!("[security manager] key len = {} {:02x?}", pka.len(), pka); + todo!() + } } From 16ef3e5c8c3b960a3d59d252f884753caac4a9bd Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 13 Dec 2024 14:13:51 +0800 Subject: [PATCH 4/6] feat(sm): add crypto mod Signed-off-by: Haobo Gu --- host/Cargo.toml | 5 + host/src/crypto.rs | 655 +++++++++++++++++++++++++++++++++++++++++++++ host/src/lib.rs | 9 + 3 files changed, 669 insertions(+) create mode 100644 host/src/crypto.rs diff --git a/host/Cargo.toml b/host/Cargo.toml index 36b786e8..4b7026d3 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -40,6 +40,11 @@ tokio-serial = "5.4" env_logger = "0.11" critical-section = { version = "1", features = ["std"] } rand = "0.8.5" +rand_core = "0.6.4" + +p256 = { version = "0.13.2", default-features = false, features = ["ecdh","arithmetic"] } +aes = { version = "0.8.2" } +cmac = { version = "0.7.2" } [features] diff --git a/host/src/crypto.rs b/host/src/crypto.rs new file mode 100644 index 00000000..99fedaa1 --- /dev/null +++ b/host/src/crypto.rs @@ -0,0 +1,655 @@ +// This file contains code from Blackrock User-Mode Bluetooth LE Library (https://github.com/mxk/burble) + +use cmac::digest; +use p256::ecdh; +use rand_core::CryptoRng; +use rand_core::RngCore; + +use crate::Address; + +/// LE Secure Connections Long Term Key. +#[derive(Eq, PartialEq)] +#[must_use] +#[repr(transparent)] +pub struct LTK(pub u128); + +impl LTK { + /// Creates a Long Term Key from a `u128` value. + #[inline(always)] + pub const fn new(k: u128) -> Self { + Self(k) + } +} + +impl From<<K> for u128 { + #[inline(always)] + fn from(k: <K) -> Self { + k.0 + } +} + +/// RFC-4493 AES-CMAC ([Vol 3] Part H, Section 2.2.5). +#[derive(Debug)] +#[repr(transparent)] +pub struct AesCmac(cmac::Cmac); + +impl AesCmac { + /// Creates new AES-CMAC state using key `k`. + #[inline(always)] + #[must_use] + pub(super) fn new(k: &Key) -> Self { + Self(digest::KeyInit::new(&k.0)) + } + + /// Creates new AES-CMAC state using an all-zero key for GAP database hash + /// calculation ([Vol 3] Part G, Section 7.3.1). + #[inline(always)] + #[must_use] + pub fn db_hash() -> Self { + Self::new(&Key::new(0)) + } + + /// Updates CMAC state. + #[inline(always)] + pub fn update(&mut self, b: impl AsRef<[u8]>) -> &mut Self { + digest::Update::update(&mut self.0, b.as_ref()); + self + } + + /// Computes the final MAC value. + #[inline(always)] + #[must_use] + pub fn finalize(self) -> u128 { + u128::from_be_bytes(*digest::FixedOutput::finalize_fixed(self.0).as_ref()) + } + + /// Computes the final MAC value for use as a future key and resets the + /// state. + #[inline(always)] + pub(super) fn finalize_key(&mut self) -> Key { + // Best effort to avoid leaving copies + let mut k = Key::new(0); + digest::FixedOutputReset::finalize_into_reset(&mut self.0, &mut k.0); + k + } +} + +/// LE Secure Connections check value generated by [`MacKey::f6`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[must_use] +#[repr(transparent)] +pub struct Check(pub u128); + +#[repr(transparent)] +pub(super) struct Key(aes::cipher::Key); + +impl Key { + /// Creates a key from a `u128` value. + #[inline(always)] + pub fn new(k: u128) -> Self { + Self(k.to_be_bytes().into()) + } +} + +impl From<&Key> for u128 { + #[inline(always)] + fn from(k: &Key) -> Self { + Self::from_be_bytes(k.0.into()) + } +} + +/// Concatenated `AuthReq`, OOB data flag, and IO capability parameters used by +/// [`MacKey::f6`] function ([Vol 3] Part H, Section 2.2.8). +#[repr(transparent)] +#[derive(Clone, Copy, Debug)] +pub struct IoCap([u8; 3]); + +impl IoCap { + /// Creates new `IoCap` parameter. + #[inline(always)] + pub fn new(auth_req: u8, oob_data: bool, io_cap: u8) -> Self { + Self([auth_req, u8::from(oob_data), io_cap]) + } +} + +/// 128-bit key used to compute LE Secure Connections check value +/// ([Vol 3] Part H, Section 2.2.8). +#[must_use] +#[repr(transparent)] +pub struct MacKey(Key); + +impl MacKey { + /// Generates LE Secure Connections check value + /// ([Vol 3] Part H, Section 2.2.8). + #[inline] + pub fn f6(&self, n1: Nonce, n2: Nonce, r: u128, io_cap: IoCap, a1: Address, a2: Address) -> Check { + let mut m = AesCmac::new(&self.0); + m.update(n1.0.to_be_bytes()) + .update(n2.0.to_be_bytes()) + .update(r.to_be_bytes()) + .update(io_cap.0) + .update(a1.to_bytes()) + .update(a2.to_bytes()); + Check(m.finalize()) + } +} + +/// 128-bit random nonce value ([Vol 3] Part H, Section 2.3.5.6). +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(transparent)] +pub struct Nonce(pub u128); + +impl Nonce { + /// Generates a new non-zero random nonce value from the OS CSPRNG. + /// + /// # Panics + /// + /// Panics if the OS CSPRNG is broken. + #[allow(clippy::new_without_default)] + #[inline] + pub fn new(rng: &mut T) -> Self { + let mut b = [0; core::mem::size_of::()]; + rng.fill_bytes(b.as_mut_slice()); + let n = u128::from_ne_bytes(b); + assert_ne!(n, 0); + Self(n) + } + + /// Generates LE Secure Connections confirm value + /// ([Vol 3] Part H, Section 2.2.6). + #[inline] + pub fn f4(&self, u: &PublicKeyX, v: &PublicKeyX, z: u8) -> Confirm { + let mut m = AesCmac::new(&Key::new(self.0)); + m.update(u.as_be_bytes()).update(v.as_be_bytes()).update([z]); + Confirm(m.finalize()) + } + + /// Generates LE Secure Connections numeric comparison value + /// ([Vol 3] Part H, Section 2.2.9). + #[inline] + pub fn g2(&self, pkax: &PublicKeyX, pkbx: &PublicKeyX, nb: &Self) -> NumCompare { + let mut m = AesCmac::new(&Key::new(self.0)); + m.update(pkax.as_be_bytes()) + .update(pkbx.as_be_bytes()) + .update(nb.0.to_be_bytes()); + #[allow(clippy::cast_possible_truncation)] + NumCompare(m.finalize() as u32 % 1_000_000) + } +} + +/// LE Secure Connections confirm value generated by [`Nonce::f4`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[must_use] +#[repr(transparent)] +pub struct Confirm(pub u128); + +/// 6-digit LE Secure Connections numeric comparison value generated by +/// [`Nonce::g2`]. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[must_use] +#[repr(transparent)] +pub struct NumCompare(pub u32); + +/// P-256 elliptic curve secret key. +#[must_use] +#[repr(transparent)] +pub struct SecretKey(p256::NonZeroScalar); + +impl SecretKey { + /// Generates a new random secret key. + #[allow(clippy::new_without_default)] + #[inline(always)] + pub fn new(rng: &mut T) -> Self { + Self(p256::NonZeroScalar::random(rng)) + } + + /// Computes the associated public key. + pub fn public_key(&self) -> PublicKey { + use p256::elliptic_curve::sec1::{Coordinates::Uncompressed, ToEncodedPoint}; + let p = p256::PublicKey::from_secret_scalar(&self.0).to_encoded_point(false); + match p.coordinates() { + Uncompressed { x, y } => PublicKey { + x: PublicKeyX(Coord(*x.as_ref())), + y: Coord(*y.as_ref()), + }, + _ => unreachable!("invalid secret key"), + } + } + + /// Computes a shared secret from the local secret key and remote public + /// key. Returns [`None`] if the public key is either invalid or derived + /// from the same secret key ([Vol 3] Part H, Section 2.3.5.6.1). + #[must_use] + pub fn dh_key(&self, pk: PublicKey) -> Option { + use p256::elliptic_curve::sec1::FromEncodedPoint; + if pk.is_debug() { + return None; // TODO: Compile-time option for debug-only mode + } + + let (x, y) = (&pk.x.0 .0.into(), &pk.y.0.into()); + let rep = p256::EncodedPoint::from_affine_coordinates(x, y, false); + let lpk = p256::PublicKey::from_secret_scalar(&self.0); + // Constant-time ops not required: + // https://github.com/RustCrypto/traits/issues/1227 + let rpk = Option::from(p256::PublicKey::from_encoded_point(&rep)).unwrap_or(lpk); + (rpk != lpk).then(|| DHKey(ecdh::diffie_hellman(&self.0, rpk.as_affine()))) + } +} + +/// P-256 elliptic curve public key ([Vol 3] Part H, Section 3.5.6). +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[must_use] +pub struct PublicKey { + pub x: PublicKeyX, + pub y: Coord, +} + +impl PublicKey { + pub fn from_bytes(bytes: &[u8]) -> Self { + let mut x = [0u8; 32]; + let mut y = [0u8; 32]; + + x.copy_from_slice(&bytes[..32]); + y.copy_from_slice(&bytes[32..]); + + x.reverse(); + y.reverse(); + + Self { + x: PublicKeyX(Coord(x)), + y: Coord(y), + } + } + + /// Returns the public key X coordinate. + #[inline(always)] + pub const fn x(&self) -> &PublicKeyX { + &self.x + } + + /// Returns whether `self` is the debug public key + /// ([Vol 3] Part H, Section 2.3.5.6.1). + #[allow(clippy::unreadable_literal)] + #[allow(clippy::unusual_byte_groupings)] + fn is_debug(&self) -> bool { + let (x, y) = (&self.x.0 .0, &self.y.0); + x[..16] == u128::to_be_bytes(0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9) + && x[16..] == u128::to_be_bytes(0xeff49111_acf4fddb_cc030148_0e359de6) + && y[..16] == u128::to_be_bytes(0xdc809c49_652aeb6d_63329abf_5a52155c) + && y[16..] == u128::to_be_bytes(0x766345c2_8fed3024_741c8ed0_1589d28b) + } +} + +// impl Codec for PublicKey { +// #[inline] +// fn pack(&self, p: &mut Packer) { +// let (mut x, mut y) = (self.x.0 .0, self.y.0); +// x.reverse(); +// y.reverse(); +// p.put(x).put(y); +// } + +// #[inline] +// fn unpack(p: &mut Unpacker) -> Option { +// let (mut x, mut y) = (PublicKeyX(Coord(p.bytes())), Coord(p.bytes())); +// x.0 .0.reverse(); +// y.0.reverse(); +// Some(Self { x, y }) +// } +// } + +/// 256-bit elliptic curve coordinate in big-endian byte order. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(transparent)] +pub struct Coord([u8; 256 / u8::BITS as usize]); + +impl Coord { + /// Returns the coordinate in big-endian byte order. + #[inline(always)] + pub(super) const fn as_be_bytes(&self) -> &[u8; core::mem::size_of::()] { + &self.0 + } +} + +/// P-256 elliptic curve public key affine X coordinate. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[must_use] +#[repr(transparent)] +pub struct PublicKeyX(Coord); + +impl PublicKeyX { + /// Creates the coordinate from a big-endian encoded byte array. + #[cfg(test)] + #[inline] + pub(super) const fn from_be_bytes(x: [u8; core::mem::size_of::()]) -> Self { + Self(Coord(x)) + } + + /// Returns the coordinate in big-endian byte order. + #[inline(always)] + pub(super) const fn as_be_bytes(&self) -> &[u8; core::mem::size_of::()] { + &self.0 .0 + } +} + +/// P-256 elliptic curve shared secret ([Vol 3] Part H, Section 2.3.5.6.1). +#[must_use] +#[repr(transparent)] +pub struct DHKey(ecdh::SharedSecret); + +impl DHKey { + /// Generates LE Secure Connections `MacKey` and `LTK` + /// ([Vol 3] Part H, Section 2.2.7). + #[inline] + pub fn f5(&self, n1: Nonce, n2: Nonce, a1: Addr, a2: Addr) -> (MacKey, LTK) { + let n1 = n1.0.to_be_bytes(); + let n2 = n2.0.to_be_bytes(); + let half = |m: &mut AesCmac, counter: u8| { + m.update([counter]) + .update(b"btle") + .update(n1) + .update(n2) + .update(a1.0) + .update(a2.0) + .update(256_u16.to_be_bytes()) + .finalize_key() + }; + let mut m = AesCmac::new(&Key::new(0x6C88_8391_AAF5_A538_6037_0BDB_5A60_83BE)); + m.update(self.0.raw_secret_bytes()); + let mut m = AesCmac::new(&m.finalize_key()); + (MacKey(half(&mut m, 0)), LTK(u128::from(&half(&mut m, 1)))) + } +} + +/// Combines `hi` and `lo` values into a big-endian byte array. +#[allow(clippy::redundant_pub_crate)] +#[cfg(test)] +pub(super) fn u256>(hi: u128, lo: u128) -> T { + let mut b = [0; 32]; + b[..16].copy_from_slice(&hi.to_be_bytes()); + b[16..].copy_from_slice(&lo.to_be_bytes()); + T::from(b) +} + +#[allow(clippy::unreadable_literal)] +#[allow(clippy::unusual_byte_groupings)] +#[cfg(test)] +mod tests { + use p256::elliptic_curve::rand_core::OsRng; + + use super::*; + extern crate std; + + #[test] + fn sizes() { + assert_eq!(core::mem::size_of::(), 32); + assert_eq!(core::mem::size_of::(), 64); + assert_eq!(core::mem::size_of::(), 32); + assert_eq!(core::mem::size_of::(), 32); + } + + /// Debug mode key ([Vol 3] Part H, Section 2.3.5.6.1). + #[test] + fn debug_key() { + let sk = secret_key( + 0x3f49f6d4_a3c55f38_74c9b3e3_d2103f50, + 0x4aff607b_eb40b799_5899b8a6_cd3c1abd, + ); + let pk = PublicKey { + x: PublicKeyX(Coord(u256( + 0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9, + 0xeff49111_acf4fddb_cc030148_0e359de6, + ))), + y: Coord(u256( + 0xdc809c49_652aeb6d_63329abf_5a52155c, + 0x766345c2_8fed3024_741c8ed0_1589d28b, + )), + }; + assert_eq!(sk.public_key(), pk); + assert!(pk.is_debug()); + } + + /// P-256 data set 1 ([Vol 2] Part G, Section 7.1.2.1). + #[test] + fn p256_1() { + let (ska, skb) = ( + secret_key( + 0x3f49f6d4_a3c55f38_74c9b3e3_d2103f50, + 0x4aff607b_eb40b799_5899b8a6_cd3c1abd, + ), + secret_key( + 0x55188b3d_32f6bb9a_900afcfb_eed4e72a, + 0x59cb9ac2_f19d7cfb_6b4fdd49_f47fc5fd, + ), + ); + let (pka, pkb) = ( + PublicKey { + x: PublicKeyX(Coord(u256( + 0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9, + 0xeff49111_acf4fddb_cc030148_0e359de6, + ))), + y: Coord(u256( + 0xdc809c49_652aeb6d_63329abf_5a52155c, + 0x766345c2_8fed3024_741c8ed0_1589d28b, + )), + }, + PublicKey { + x: PublicKeyX(Coord(u256( + 0x1ea1f0f0_1faf1d96_09592284_f19e4c00, + 0x47b58afd_8615a69f_559077b2_2faaa190, + ))), + y: Coord(u256( + 0x4c55f33e_429dad37_7356703a_9ab85160, + 0x472d1130_e28e3676_5f89aff9_15b1214a, + )), + }, + ); + let dh_key = shared_secret( + 0xec0234a3_57c8ad05_341010a6_0a397d9b, + 0x99796b13_b4f866f1_868d34f3_73bfa698, + ); + assert_eq!(ska.public_key(), pka); + assert_eq!(skb.public_key(), pkb); + assert_eq!( + ska.dh_key(pkb).unwrap().0.raw_secret_bytes(), + dh_key.0.raw_secret_bytes() + ); + + assert!(!pkb.is_debug()); + assert!(skb.dh_key(pkb).is_none()); + } + + /// P-256 data set 2 ([Vol 2] Part G, Section 7.1.2.2). + #[test] + fn p256_2() { + let (ska, skb) = ( + secret_key( + 0x06a51669_3c9aa31a_6084545d_0c5db641, + 0xb48572b9_7203ddff_b7ac73f7_d0457663, + ), + secret_key( + 0x529aa067_0d72cd64_97502ed4_73502b03, + 0x7e8803b5_c60829a5_a3caa219_505530ba, + ), + ); + let (pka, pkb) = ( + PublicKey { + x: PublicKeyX(Coord(u256( + 0x2c31a47b_5779809e_f44cb5ea_af5c3e43, + 0xd5f8faad_4a8794cb_987e9b03_745c78dd, + ))), + y: Coord(u256( + 0x91951218_3898dfbe_cd52e240_8e43871f, + 0xd0211091_17bd3ed4_eaf84377_43715d4f, + )), + }, + PublicKey { + x: PublicKeyX(Coord(u256( + 0xf465e43f_f23d3f1b_9dc7dfc0_4da87581, + 0x84dbc966_204796ec_cf0d6cf5_e16500cc, + ))), + y: Coord(u256( + 0x0201d048_bcbbd899_eeefc424_164e33c2, + 0x01c2b010_ca6b4d43_a8a155ca_d8ecb279, + )), + }, + ); + let dh_key = shared_secret( + 0xab85843a_2f6d883f_62e5684b_38e30733, + 0x5fe6e194_5ecd1960_4105c6f2_3221eb69, + ); + assert_eq!(ska.public_key(), pka); + assert_eq!(skb.public_key(), pkb); + assert_eq!( + ska.dh_key(pkb).unwrap().0.raw_secret_bytes(), + dh_key.0.raw_secret_bytes() + ); + } + + /// Key generation function ([Vol 3] Part H, Section D.3). + #[test] + fn dh_key_f5() { + let w = shared_secret( + 0xec0234a3_57c8ad05_341010a6_0a397d9b, + 0x99796b13_b4f866f1_868d34f3_73bfa698, + ); + let n1 = Nonce(0xd5cb8454_d177733e_ffffb2ec_712baeab); + let n2 = Nonce(0xa6e8e7cc_25a75f6e_216583f7_ff3dc4cf); + let a1 = Addr([0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce]); + let a2 = Addr([0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1]); + let (mk, ltk) = w.f5(n1, n2, a1, a2); + assert_eq!(ltk.0, 0x69867911_69d7cd23_980522b5_94750a38); + assert_eq!(u128::from(&mk.0), 0x2965f176_a1084a02_fd3f6a20_ce636e20); + } + + #[inline] + fn secret_key(hi: u128, lo: u128) -> SecretKey { + SecretKey(p256::NonZeroScalar::from_repr(u256(hi, lo)).unwrap()) + } + + #[inline] + fn shared_secret(hi: u128, lo: u128) -> DHKey { + DHKey(ecdh::SharedSecret::from(u256::(hi, lo))) + } + + #[test] + fn testtest() { + let skb = SecretKey::new(&mut OsRng::default()); + let _pkb = skb.public_key(); + + let ska = SecretKey::new(&mut OsRng::default()); + let pka = ska.public_key(); + + let _dh_key = skb.dh_key(pka).unwrap(); + } + + #[test] + fn testtest2() { + let bytes = [ + 0x1eu8, 0x3b, 0x26, 0x40, 0x0e, 0xba, 0x72, 0x51, 0x81, 0xf9, 0x3d, 0x16, 0xb3, 0xc4, 0x11, 0x55, 0x3f, + 0xa8, 0x88, 0x47, 0x08, 0x1c, 0x4a, 0x42, 0x88, 0xbb, 0x68, 0x1d, 0x93, 0xe5, 0xab, 0xb3, 0x72, 0xfa, 0x93, + 0xb4, 0xa0, 0xfe, 0x3f, 0x83, 0x9c, 0x85, 0x5b, 0x5f, 0xb6, 0x30, 0x09, 0x85, 0x47, 0xfd, 0xa8, 0xfa, 0x11, + 0x71, 0xe4, 0x95, 0x17, 0x71, 0x98, 0x82, 0x8f, 0xf8, 0x79, 0x94, + ]; + + let skb = SecretKey::new(&mut OsRng::default()); + let _pkb = skb.public_key(); + + let pka = PublicKey::from_bytes(&bytes); + + let _dh_key = skb.dh_key(pka).unwrap(); + } + + #[test] + fn nonce() { + // No fair dice rolls for us! + assert_ne!(Nonce::new(&mut OsRng::default()), Nonce::new(&mut OsRng::default())); + } + + /// Confirm value generation function ([Vol 3] Part H, Section D.2). + #[test] + fn nonce_f4() { + let u = PublicKeyX::from_be_bytes(u256( + 0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9, + 0xeff49111_acf4fddb_cc030148_0e359de6, + )); + let v = PublicKeyX::from_be_bytes(u256( + 0x55188b3d_32f6bb9a_900afcfb_eed4e72a, + 0x59cb9ac2_f19d7cfb_6b4fdd49_f47fc5fd, + )); + let x = Nonce(0xd5cb8454_d177733e_ffffb2ec_712baeab); + assert_eq!(x.f4(&u, &v, 0).0, 0xf2c916f1_07a9bd1c_f1eda1be_a974872d); + } + + /// Numeric comparison generation function ([Vol 3] Part H, Section D.5). + #[allow(clippy::unreadable_literal)] + #[test] + fn nonce_g2() { + let u = PublicKeyX::from_be_bytes(u256( + 0x20b003d2_f297be2c_5e2c83a7_e9f9a5b9, + 0xeff49111_acf4fddb_cc030148_0e359de6, + )); + let v = PublicKeyX::from_be_bytes(u256( + 0x55188b3d_32f6bb9a_900afcfb_eed4e72a, + 0x59cb9ac2_f19d7cfb_6b4fdd49_f47fc5fd, + )); + let x = Nonce(0xd5cb8454_d177733e_ffffb2ec_712baeab); + let y = Nonce(0xa6e8e7cc_25a75f6e_216583f7_ff3dc4cf); + assert_eq!(x.g2(&u, &v, &y), NumCompare(0x2f9ed5ba % 1_000_000)); + } + + /// Check value generation function ([Vol 3] Part H, Section D.4). + #[test] + fn mac_key_f6() { + let k = MacKey(Key::new(0x2965f176_a1084a02_fd3f6a20_ce636e20)); + let n1 = Nonce(0xd5cb8454_d177733e_ffffb2ec_712baeab); + let n2 = Nonce(0xa6e8e7cc_25a75f6e_216583f7_ff3dc4cf); + let r = 0x12a3343b_b453bb54_08da42d2_0c2d0fc8; + let io_cap = IoCap([0x01, 0x01, 0x02]); + let a1 = Addr([0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce]); + let a2 = Addr([0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1]); + let c = k.f6(n1, n2, r, io_cap, a1, a2); + assert_eq!(c.0, 0xe3c47398_9cd0e8c5_d26c0b09_da958f61); + } + + #[test] + fn nonce_f4_test() { + let ra = [ + 0x11u8, 0x3a, 0x7a, 0x69, 0x11, 0xcd, 0x44, 0x15, 0x52, 0xf7, 0x47, 0xe8, 0x26, 0x67, 0x72, 0xca, + ]; + + let rb = [ + 0xa5u8, 0x9e, 0x9a, 0x32, 0xc0, 0x97, 0x1c, 0xf7, 0x72, 0x1c, 0x29, 0xa7, 0x8c, 0x1e, 0xfd, 0x18, + ]; + + let pkb = [ + 0xd, 0x80, 0x33, 0x93, 0xad, 0x1f, 0x7e, 0x9a, 0x30, 0xc9, 0x6e, 0x1, 0x78, 0xf3, 0x43, 0x14, 0xa0, 0x57, + 0xae, 0xa5, 0xa8, 0xee, 0x75, 0x51, 0x3f, 0xaa, 0xb1, 0x80, 0x75, 0xc7, 0x14, 0x50, 0x73, 0x9a, 0x98, 0x95, + 0x36, 0x2e, 0xe6, 0x81, 0x5f, 0xbf, 0x16, 0xa2, 0x8c, 0xf6, 0x9d, 0xdc, 0x1f, 0xb8, 0x84, 0x8c, 0x7d, 0x37, + 0x36, 0xe4, 0x36, 0x3c, 0xb3, 0xe8, 0xfe, 0x4a, 0x73, 0xc6, + ]; + + let pka = [ + 0x97, 0x20, 0x0f, 0xfe, 0xf0, 0xec, 0xdd, 0x11, 0xda, 0xa8, 0xa8, 0x07, 0x3e, 0xd7, 0xc6, 0xf2, 0x68, 0x5d, + 0xc2, 0x58, 0x71, 0x1e, 0x34, 0x4f, 0xa1, 0xc4, 0x44, 0xa9, 0x7c, 0x71, 0xee, 0x54, 0x0d, 0xad, 0xb7, 0x69, + 0x89, 0x9d, 0x4f, 0x83, 0x37, 0xcd, 0x43, 0xd3, 0x9f, 0x05, 0x13, 0x99, 0x6f, 0xbc, 0x1a, 0x89, 0xed, 0xb4, + 0x7f, 0x80, 0x98, 0xcf, 0xad, 0x7c, 0x4c, 0x57, 0xbf, 0xe1, + ]; + + let mut pkb_x = [0u8; 32]; + pkb_x.copy_from_slice(&pkb[..32]); + pkb_x.reverse(); + let mut pka_x = [0u8; 32]; + pka_x.copy_from_slice(&pka[..32]); + pka_x.reverse(); + + let pkbx = PublicKeyX::from_be_bytes(pkb_x); + let pkax = PublicKeyX::from_be_bytes(pka_x); + extern crate std; + + let x = Nonce(u128::from_le_bytes(ra)); + let y = Nonce(u128::from_le_bytes(rb)); + + assert_eq!(x.g2(&pkax, &pkbx, &y).0, 991180); + } +} diff --git a/host/src/lib.rs b/host/src/lib.rs index 051a743b..dbb851cd 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -36,6 +36,7 @@ mod codec; mod command; pub mod config; mod connection_manager; +mod crypto; mod cursor; pub mod packet_pool; mod pdu; @@ -118,6 +119,14 @@ impl Address { addr: BdAddr::new(val), } } + + /// To bytes + pub fn to_bytes(&self) -> [u8; 7] { + let mut bytes = [0; 7]; + bytes[0] = self.kind.into_inner(); + bytes[1..].copy_from_slice(&self.addr.into_inner()); + bytes + } } /// Errors returned by the host. From 7dedaee19c0e18145231b8877117b81f52f6b78b Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 13 Dec 2024 15:10:54 +0800 Subject: [PATCH 5/6] feat(sm): add security check handlers Signed-off-by: Haobo Gu --- host/src/security_manager.rs | 173 +++++++++++++++++++++++++++++++---- 1 file changed, 156 insertions(+), 17 deletions(-) diff --git a/host/src/security_manager.rs b/host/src/security_manager.rs index 6ccdbb04..6920541a 100644 --- a/host/src/security_manager.rs +++ b/host/src/security_manager.rs @@ -4,6 +4,12 @@ use bt_hci::{ data::{AclBroadcastFlag, AclPacket, AclPacketBoundary}, param::ConnHandle, }; +use rand_core::{CryptoRng, RngCore}; + +use crate::{ + crypto::{Nonce, PublicKey, SecretKey}, + BleHostError, Error, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -77,26 +83,21 @@ const SM_PAIRING_PUBLIC_KEY: u8 = 0x0c; const SM_PAIRING_DHKEY_CHECK: u8 = 0x0d; /// Security manager that handles SM packet -pub struct SecurityManager<'d, C: Controller> { +pub struct SecurityManager<'a, 'd, C: Controller, R: CryptoRng + RngCore> { controller: &'d C, + rng: &'a mut R, } -impl SecurityManager<'_, C> { +impl SecurityManager<'_, '_, C, R> { /// Handle packet - pub(crate) async fn handle(&mut self, src_handle: u16, payload: &[u8]) { + pub(crate) async fn handle(&mut self, src_handle: u16, payload: &[u8]) -> Result<(), BleHostError> { let data = &payload[1..]; let command = payload[0]; match command { - SM_PAIRING_REQUEST => { - self.handle_pairing_request(src_handle, data).await.unwrap(); - } - SM_PAIRING_PUBLIC_KEY => { - todo!() - } - SM_PAIRING_RANDOM => { - todo!() - } + SM_PAIRING_REQUEST => self.handle_pairing_request(src_handle, data).await, + SM_PAIRING_PUBLIC_KEY => self.handle_pairing_public_key(src_handle, data).await, + SM_PAIRING_RANDOM => self.handle_pairing_random(src_handle, data).await, SM_PAIRING_DHKEY_CHECK => { todo!() } @@ -120,7 +121,7 @@ impl SecurityManager<'_, C> { &target[..data.len() + 4] } - async fn write_sm_data(&self, handle: u16, data: &[u8]) -> Result<(), C::Error> { + async fn write_sm_data(&self, handle: u16, data: &[u8]) -> Result<(), BleHostError> { let mut sm_data_buf = [0u8; 256]; let encoded_data = self.encode_sm_data(data, &mut sm_data_buf); @@ -130,10 +131,13 @@ impl SecurityManager<'_, C> { AclBroadcastFlag::PointToPoint, encoded_data, ); - self.controller.write_acl_data(&packet).await + self.controller + .write_acl_data(&packet) + .await + .map_err(|e| BleHostError::Controller(e)) } - async fn handle_pairing_request(&mut self, src_handle: u16, data: &[u8]) -> Result<(), C::Error> { + async fn handle_pairing_request(&mut self, src_handle: u16, data: &[u8]) -> Result<(), BleHostError> { // TODO: save A/O/I debug!("[security manager] Handle pairing request"); let auth_req = data[2]; @@ -152,9 +156,144 @@ impl SecurityManager<'_, C> { self.write_sm_data(src_handle, &req_data).await } - async fn handle_pairing_public_key(&mut self, src_handle: u16, pka: &[u8]) -> Result<(), C::Error> { + async fn handle_pairing_public_key(&mut self, src_handle: u16, pka: &[u8]) -> Result<(), BleHostError> { debug!("[security manager] Handle pairing public key"); debug!("[security manager] key len = {} {:02x?}", pka.len(), pka); - todo!() + let pka = PublicKey::from_bytes(pka); + + // Send the local public key before validating the remote key to allow + // parallel computation of DHKey. No security risk in doing so. + + let skb = SecretKey::new(self.rng); + let pkb = skb.public_key(); + + let mut x = [0u8; 32]; + let mut y = [0u8; 32]; + x.copy_from_slice(pkb.x.as_be_bytes()); + y.copy_from_slice(pkb.y.as_be_bytes()); + x.reverse(); + y.reverse(); + + let mut data = [0u8; 65]; + data[0] = SM_PAIRING_PUBLIC_KEY; + data[1..33].copy_from_slice(&x); + data[33..65].copy_from_slice(&y); + + self.write_sm_data(src_handle, &data).await?; + + let dh_key = match skb.dh_key(pka) { + Some(dh_key) => Ok(dh_key), + None => Err(BleHostError::BleHost(Error::Security( + SecurityManagerError::DHKeyCheckFailed, + ))), + }?; + + // SUBTLE: The order of these send/recv ops is important. See last + // paragraph of Section 2.3.5.6.2. + let nb = Nonce::new(self.rng); + let cb = nb.f4(pkb.x(), pka.x(), 0); + + let mut data = [0u8; 17]; + data[0] = SM_PAIRING_CONFIRM; + data[1..17].copy_from_slice(&cb.0.to_le_bytes()); + + self.write_sm_data(src_handle, &data).await?; + + // TODO: update keys + // self.pka = Some(pka); + // self.pkb = Some(pkb); + // self.skb = Some(skb); + // self.confirm = Some(cb); + // self.nb = Some(nb); + // self.dh_key = Some(dh_key); + + Ok(()) + } + + async fn handle_pairing_random(&mut self, src_handle: u16, random: &[u8]) -> Result<(), BleHostError> { + debug!("[security manager] Handle pairing random"); + debug!("[security manager] Got pairing random: {:02x?}", random); + + // TODO: Do checking + + // Write nb data + let mut data = [0u8; 17]; + data[0] = SM_PAIRING_RANDOM; + // TODO: add nb + // data[1..17].copy_from_slice(self.nb.unwrap().0.to_le_bytes()); + self.write_sm_data(src_handle, &data).await?; + + let na = Nonce(u128::from_le_bytes(random.try_into().unwrap())); + // TODO: calculation + // self.na = Some(na); + // let nb = self.nb.unwrap(); + // let vb = na.g2(self.pka.as_ref().unwrap().x(), self.pkb.as_ref().unwrap().x(), &nb); + + // should display the code and get confirmation from user (pin ok or not) - if not okay send a pairing-failed + // assume it's correct or the user will cancel on central + // TODO: What is pin_callback used for? + // info!("Display code is {}", vb.0); + // if let Some(pin_callback) = pin_callback { + // pin_callback(vb.0); + // } + + // Authentication stage 2 and long term key calculation + // ([Vol 3] Part H, Section 2.3.5.6.5 and C.2.2.4). + + // let a = self.peer_address.unwrap(); + // let b = self.local_address.unwrap(); + let ra = 0; + // trace!("peer_address = {:02x?}", a.0); + // trace!("local_address = {:02x?}", b.0); + + // TODO: more calculations! + // let iob = IoCap::new(make_auth_req().0, false, io_cap); + let auth_req = make_auth_req(); + let oob_data = false; + let io_cap = IoCapabilities::DisplayYesNo as u8; + // let dh_key = self.dh_key.as_ref().unwrap(); + + // let (mac_key, ltk) = dh_key.f5(na, nb, a, b); + // let eb = mac_key.f6(nb, na, ra, iob, b, a); + + // self.mac_key = Some(mac_key); + // self.ltk = Some(ltk.0); + // self.eb = Some(eb); + + Ok(()) + } + + async fn handle_pairing_dhkey_check(&mut self, src_handle: u16, ea: &[u8]) -> Result<(), BleHostError> { + debug!("[security manager] Handle pairing dhkey check"); + debug!("[security manager] Got ea: {:02x?}", ea); + + // TODO: Do checking + + // TODO: Check dhkey + // let expected_ea = self + // .mac_key + // .as_ref() + // .unwrap() + // .f6( + // self.na.unwrap(), + // self.nb.unwrap(), + // 0, + // self.ioa.unwrap(), + // self.peer_address.unwrap(), + // self.local_address.unwrap(), + // ) + // .0 + // .to_le_bytes(); + + // if ea != expected { + // warn!("DH check failed"); + // } + + let mut data = [0u8; 17]; + data[0] = SM_PAIRING_DHKEY_CHECK; + // data[1..17].copy_from_slice(self.eb.as_ref().unwrap().0.to_le_bytes()); + + self.write_sm_data(src_handle, &data).await?; + Ok(()) } } From 365679e03e5de2cfd91dca46f7cc028acd91bb62 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 13 Dec 2024 15:35:39 +0800 Subject: [PATCH 6/6] refactor(sm): clean security types Signed-off-by: Haobo Gu --- host/src/types/mod.rs | 1 - host/src/types/security.rs | 85 -------------------------------------- 2 files changed, 86 deletions(-) delete mode 100644 host/src/types/security.rs diff --git a/host/src/types/mod.rs b/host/src/types/mod.rs index d7cabcae..e8b746ae 100644 --- a/host/src/types/mod.rs +++ b/host/src/types/mod.rs @@ -4,6 +4,5 @@ pub mod gatt_traits; pub(crate) mod l2cap; pub(crate) mod primitives; -pub(crate) mod security; pub mod uuid; diff --git a/host/src/types/security.rs b/host/src/types/security.rs deleted file mode 100644 index d3effae5..00000000 --- a/host/src/types/security.rs +++ /dev/null @@ -1,85 +0,0 @@ -use bt_hci::param::AddrKind; - -use crate::Address; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum SecurityMode { - NoAccess, - Open, - JustWorks, - Mitm, - LescMitm, - Signed, - SignedMitm, -} - -impl Default for SecurityMode { - fn default() -> Self { - Self::Open - } -} - -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct MasterId { - /// Encrypted diversifier - pub ediv: u16, - /// Random number - pub rand: [u8; 8], -} - -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EncryptionInfo { - /// Long term key - pub ltk: [u8; 16], - pub flags: u8, -} - -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IdentityResolutionKey { - irk: [u8; 16], -} - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct IdentityKey { - /// Identity resolution key - pub irk: IdentityResolutionKey, - /// Address - pub addr: Address, -} - - -impl IdentityKey { - pub fn is_match(&self, addr: Address) -> bool { - match addr.kind { - AddrKind::PUBLIC | AddrKind::RANDOM => { - if self.addr.kind == addr.kind && self.addr.addr == addr.addr { - true - } else { - false - } - }, - AddrKind::RESOLVABLE_PRIVATE_OR_RANDOM => { - todo!("Random address resolution") - } - AddrKind::RESOLVABLE_PRIVATE_OR_PUBLIC | AddrKind::ANONYMOUS_ADV => false, - _ => panic!("Invalid address kind"), - } - } - - pub fn from_addr(addr: Address) -> Self { - Self { - irk: Default::default(), - addr, - } - } -} - -fn random_address_hash(key: IdentityResolutionKey) -> [u8; 6] { - todo!() -} -