use crate::l2cap::{Protocol, ProtocolObj, Sender};
use crate::{bytes::*, utils::HexSlice, Error};
use bitflags::bitflags;
use core::fmt;
pub trait SecurityLevel {
const MTU: u8;
}
#[derive(Debug)]
pub struct NoSecurity;
impl SecurityLevel for NoSecurity {
const MTU: u8 = 23;
}
#[derive(Debug)]
pub struct SecureConnections;
impl SecurityLevel for SecureConnections {
const MTU: u8 = 65;
}
#[derive(Debug)]
pub struct SecurityManager<S: SecurityLevel> {
_security: S,
}
impl SecurityManager<NoSecurity> {
pub fn no_security() -> Self {
Self {
_security: NoSecurity,
}
}
}
impl<S: SecurityLevel> ProtocolObj for SecurityManager<S> {
fn process_message(&mut self, message: &[u8], _responder: Sender<'_>) -> Result<(), Error> {
let cmd = Command::from_bytes(&mut ByteReader::new(message))?;
trace!("SMP cmd {:?}, {:?}", cmd, HexSlice(message));
match cmd {
Command::PairingRequest { .. } => {
warn!("pairing request NYI");
}
Command::Unknown {
code: CommandCode::Unknown(code),
data,
} => warn!(
"unknown security manager cmd: 0x{:02X} {:?}",
code,
HexSlice(data)
),
Command::Unknown { code, data } => {
warn!("[NYI] SMP cmd {:?}: {:?}", code, HexSlice(data));
}
}
Ok(())
}
}
impl<S: SecurityLevel> Protocol for SecurityManager<S> {
const RSP_PDU_SIZE: u8 = S::MTU;
}
#[derive(Debug, Copy, Clone)]
enum Command<'a> {
PairingRequest {
io: IoCapabilities,
oob: bool,
auth_req: AuthReq,
max_keysize: u8,
initiator_dist: KeyDistribution,
responder_dist: KeyDistribution,
},
Unknown {
code: CommandCode,
data: &'a [u8],
},
}
impl<'a> FromBytes<'a> for Command<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
let code = CommandCode::from(bytes.read_u8()?);
Ok(match code {
CommandCode::PairingRequest => Command::PairingRequest {
io: IoCapabilities::from(bytes.read_u8()?),
oob: bytes.read_u8()? == 0x01,
auth_req: AuthReq(bytes.read_u8()?),
max_keysize: bytes.read_u8()?,
initiator_dist: KeyDistribution::from_bits_truncate(bytes.read_u8()?),
responder_dist: KeyDistribution::from_bits_truncate(bytes.read_u8()?),
},
_ => Command::Unknown {
code,
data: bytes.read_rest(),
},
})
}
}
enum_with_unknown! {
#[derive(Debug, Copy, Clone)]
enum CommandCode(u8) {
PairingRequest = 0x01,
PairingResponse = 0x02,
PairingConfirm = 0x03,
PairingRandom = 0x04,
PairingFailed = 0x05,
EncryptionInformation = 0x06,
MasterIdentification = 0x07,
IdentityInformation = 0x08,
IdentityAddressInformation = 0x09,
SigningInformation = 0x0A,
SecurityRequest = 0x0B,
PairingPublicKey = 0x0C,
PairingDhKeyCheck = 0x0D,
PairingKeypressNotification = 0x0E,
}
}
enum_with_unknown! {
#[derive(Debug, Copy, Clone)]
pub enum IoCapabilities(u8) {
DisplayOnly = 0x00,
DisplayYesNo = 0x01,
KeyboardOnly = 0x02,
NoInputNoOutput = 0x03,
KeyboardDisplay = 0x04,
}
}
#[derive(Copy, Clone)]
pub struct AuthReq(u8);
impl AuthReq {
const BITS_BONDING: u8 = 0b0000_0011;
const BITS_MITM: u8 = 0b0000_0100;
const BITS_SC: u8 = 0b0000_1000;
const BITS_KEYPRESS: u8 = 0b0001_0000;
pub fn bonding_type(&self) -> BondingType {
BondingType::from(self.0 & Self::BITS_BONDING)
}
pub fn set_bonding_type(&mut self, ty: BondingType) {
self.0 = (self.0 & !Self::BITS_BONDING) | u8::from(ty);
}
pub fn mitm(&self) -> bool {
self.0 & Self::BITS_MITM != 0
}
pub fn set_mitm(&mut self, mitm: bool) {
self.0 = (self.0 & !Self::BITS_MITM) | if mitm { Self::BITS_MITM } else { 0 };
}
pub fn secure_connection(&self) -> bool {
self.0 & Self::BITS_SC != 0
}
pub fn set_secure_connection(&mut self, sc: bool) {
self.0 = (self.0 & !Self::BITS_SC) | if sc { Self::BITS_SC } else { 0 };
}
pub fn keypress(&self) -> bool {
self.0 & Self::BITS_KEYPRESS != 0
}
pub fn set_keypress(&mut self, keypress: bool) {
self.0 = (self.0 & !Self::BITS_KEYPRESS) | if keypress { Self::BITS_KEYPRESS } else { 0 };
}
}
impl fmt::Debug for AuthReq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AuthReq")
.field("bonding_type", &self.bonding_type())
.field("mitm", &self.mitm())
.field("secure_connection", &self.secure_connection())
.field("keypress", &self.keypress())
.finish()
}
}
enum_with_unknown! {
#[derive(Debug, Copy, Clone)]
pub enum BondingType(u8) {
NoBonding = 0b00,
Bonding = 0b01,
}
}
bitflags! {
pub struct KeyDistribution: u8 {
const ENC_KEY = (1 << 0);
const ID_KEY = (1 << 1);
const SIGN_KEY = (1 << 2);
const LINK_KEY = (1 << 3);
}
}