use super::{Bluetooth, Error};
use crate::timer::Timer;
const AES_KEY: [u8; 16] = [
0x4c, 0x68, 0x38, 0x41, 0x39, 0xf5, 0x74, 0xd8, 0x36, 0xbc, 0xf3, 0x4e, 0x9d, 0xfb, 0x01, 0xbf,
];
const AES_PLAINTEXT: [u8; 16] = [
0x02, 0x13, 0x24, 0x35, 0x46, 0x57, 0x68, 0x79, 0xac, 0xbd, 0xce, 0xdf, 0xe0, 0xf1, 0x02, 0x13,
];
const AES_CIPHERTEXT: [u8; 16] = [
0x99, 0xad, 0x1b, 0x52, 0x26, 0xa3, 0x7e, 0x3e, 0x05, 0x8e, 0x3b, 0x8e, 0x27, 0xc2, 0xc6, 0x66,
];
const C1_R: [u8; 16] = [
0xe0, 0x2e, 0x70, 0xc6, 0x4e, 0x27, 0x88, 0x63, 0x0e, 0x6f, 0xad, 0x56, 0x21, 0xd5, 0x83, 0x57,
];
const C1_PREQ: [u8; 7] = [0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07];
const C1_PRES: [u8; 7] = [0x02, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05];
const C1_IA: [u8; 6] = [0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1];
const C1_RA: [u8; 6] = [0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1];
const C1_IAT: u8 = 0x01;
const C1_RAT: u8 = 0x00;
const C1_EXPECTED: [u8; 16] = [
0x86, 0x3b, 0xf1, 0xbe, 0xc5, 0x4d, 0xa7, 0xd2, 0xea, 0x88, 0x89, 0x87, 0xef, 0x3f, 0x1e, 0x1e,
];
fn reverse16(x: &[u8; 16]) -> [u8; 16] {
let mut out = [0u8; 16];
for (i, b) in x.iter().rev().enumerate() {
out[i] = *b;
}
out
}
fn xor16(a: &[u8; 16], b: &[u8; 16]) -> [u8; 16] {
let mut out = [0u8; 16];
for i in 0..16 {
out[i] = a[i] ^ b[i];
}
out
}
fn detect(bt: &mut Bluetooth, timer: &Timer) -> Result<Option<bool>, Error> {
let direct = bt.le_encrypt(&AES_KEY, &AES_PLAINTEXT, timer)?;
if direct == AES_CIPHERTEXT {
return Ok(Some(false));
}
let swapped =
reverse16(&bt.le_encrypt(&reverse16(&AES_KEY), &reverse16(&AES_PLAINTEXT), timer)?);
if swapped == AES_CIPHERTEXT {
return Ok(Some(true));
}
Ok(None)
}
pub(crate) struct PairingContext {
pub preq: [u8; 7],
pub pres: [u8; 7],
pub ia: [u8; 6],
pub iat: u8,
pub ra: [u8; 6],
pub rat: u8,
}
pub(crate) struct Crypto {
swap: bool,
}
impl Crypto {
pub(crate) fn new(bt: &mut Bluetooth, timer: &Timer) -> Result<Self, Error> {
match detect(bt, timer)? {
Some(swap) => Ok(Self { swap }),
None => Err(Error::CryptoSelfTest),
}
}
fn aes(
&self,
bt: &mut Bluetooth,
timer: &Timer,
key: &[u8; 16],
block: &[u8; 16],
) -> Result<[u8; 16], Error> {
if self.swap {
let out = bt.le_encrypt(&reverse16(key), &reverse16(block), timer)?;
Ok(reverse16(&out))
} else {
bt.le_encrypt(key, block, timer)
}
}
fn e(
&self,
bt: &mut Bluetooth,
timer: &Timer,
k: &[u8; 16],
data: &[u8; 16],
) -> Result<[u8; 16], Error> {
let out = self.aes(bt, timer, &reverse16(k), &reverse16(data))?;
Ok(reverse16(&out))
}
pub(crate) fn c1(
&self,
bt: &mut Bluetooth,
timer: &Timer,
k: &[u8; 16],
r: &[u8; 16],
ctx: &PairingContext,
) -> Result<[u8; 16], Error> {
let mut p1 = [0u8; 16];
p1[0] = ctx.iat;
p1[1] = ctx.rat;
p1[2..9].copy_from_slice(&ctx.preq);
p1[9..16].copy_from_slice(&ctx.pres);
let step1 = self.e(bt, timer, k, &xor16(r, &p1))?;
let mut p2 = [0u8; 16];
p2[0..6].copy_from_slice(&ctx.ra);
p2[6..12].copy_from_slice(&ctx.ia);
self.e(bt, timer, k, &xor16(&step1, &p2))
}
pub(crate) fn s1(
&self,
bt: &mut Bluetooth,
timer: &Timer,
k: &[u8; 16],
r1: &[u8; 16],
r2: &[u8; 16],
) -> Result<[u8; 16], Error> {
let mut data = [0u8; 16];
data[0..8].copy_from_slice(&r2[0..8]);
data[8..16].copy_from_slice(&r1[0..8]);
self.e(bt, timer, k, &data)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SelfTest {
pub swapped: bool,
pub aes_ok: bool,
pub c1_ok: bool,
}
impl SelfTest {
pub fn passed(&self) -> bool {
self.aes_ok && self.c1_ok
}
}
pub fn self_test(bt: &mut Bluetooth, timer: &Timer) -> Result<SelfTest, Error> {
let Some(swap) = detect(bt, timer)? else {
return Ok(SelfTest {
swapped: false,
aes_ok: false,
c1_ok: false,
});
};
let crypto = Crypto { swap };
let ctx = PairingContext {
preq: C1_PREQ,
pres: C1_PRES,
ia: C1_IA,
iat: C1_IAT,
ra: C1_RA,
rat: C1_RAT,
};
let confirm = crypto.c1(bt, timer, &[0u8; 16], &C1_R, &ctx)?;
Ok(SelfTest {
swapped: swap,
aes_ok: true,
c1_ok: confirm == C1_EXPECTED,
})
}
const SMP_PAIRING_REQUEST: u8 = 0x01;
const SMP_PAIRING_RESPONSE: u8 = 0x02;
const SMP_PAIRING_CONFIRM: u8 = 0x03;
const SMP_PAIRING_RANDOM: u8 = 0x04;
const SMP_PAIRING_FAILED: u8 = 0x05;
const SMP_ENCRYPTION_INFORMATION: u8 = 0x06;
const SMP_MASTER_IDENTIFICATION: u8 = 0x07;
const SMP_ERR_CONFIRM_FAILED: u8 = 0x04;
const SMP_ERR_UNSPECIFIED: u8 = 0x08;
const IO_CAP_NO_INPUT_NO_OUTPUT: u8 = 0x03;
const AUTH_REQ_NO_BONDING: u8 = 0x00;
const AUTH_REQ_BONDING: u8 = 0x01;
const KEY_DIST_ENC_KEY: u8 = 0x01;
const MAX_ENC_KEY_SIZE: u8 = 16;
const TK_JUST_WORKS: [u8; 16] = [0u8; 16];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum State {
Idle,
WaitConfirm,
WaitRandom,
WaitLtk,
}
pub struct Smp {
crypto: Crypto,
bonding: bool,
state: State,
ctx: PairingContext,
prnd: [u8; 16],
mconfirm: [u8; 16],
stk: [u8; 16],
have_stk: bool,
bonded_ltk: [u8; 16],
bonded_ediv: u16,
bonded_rand: [u8; 8],
have_bond: bool,
distributed: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Bond {
pub ltk: [u8; 16],
pub ediv: u16,
pub rand: [u8; 8],
}
pub struct KeyDistribution {
pub encryption_information: [u8; 17],
pub master_identification: [u8; 11],
}
impl Smp {
pub fn new(bt: &mut Bluetooth, timer: &Timer, bonding: bool) -> Result<Self, Error> {
Ok(Self {
crypto: Crypto::new(bt, timer)?,
bonding,
state: State::Idle,
ctx: PairingContext {
preq: [0; 7],
pres: [0; 7],
ia: [0; 6],
iat: 0,
ra: [0; 6],
rat: 0,
},
prnd: [0; 16],
mconfirm: [0; 16],
stk: [0; 16],
have_stk: false,
bonded_ltk: [0; 16],
bonded_ediv: 0,
bonded_rand: [0; 8],
have_bond: false,
distributed: false,
})
}
pub fn begin(
&mut self,
peer_addr: [u8; 6],
peer_addr_type: u8,
own_addr: [u8; 6],
own_addr_type: u8,
) {
self.ctx.ia = peer_addr;
self.ctx.iat = peer_addr_type;
self.ctx.ra = own_addr;
self.ctx.rat = own_addr_type;
self.state = State::Idle;
self.have_stk = false;
self.distributed = false;
}
pub fn reset(&mut self) {
self.state = State::Idle;
self.have_stk = false;
self.distributed = false;
}
pub fn long_term_key(&self, ediv: u16, rand: [u8; 8]) -> Option<[u8; 16]> {
if ediv == 0 && rand == [0u8; 8] && self.have_stk {
return Some(self.stk);
}
if self.have_bond && ediv == self.bonded_ediv && rand == self.bonded_rand {
return Some(self.bonded_ltk);
}
None
}
pub fn bond(&self) -> Option<Bond> {
self.have_bond.then_some(Bond {
ltk: self.bonded_ltk,
ediv: self.bonded_ediv,
rand: self.bonded_rand,
})
}
pub fn restore_bond(&mut self, bond: &Bond) {
self.bonded_ltk = bond.ltk;
self.bonded_ediv = bond.ediv;
self.bonded_rand = bond.rand;
self.have_bond = true;
}
pub fn distribute_keys(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
) -> Result<Option<KeyDistribution>, Error> {
if !self.bonding || self.distributed || !self.have_stk {
return Ok(None);
}
let mut ltk = [0u8; 16];
ltk[0..8].copy_from_slice(&bt.le_rand(timer)?);
ltk[8..16].copy_from_slice(&bt.le_rand(timer)?);
let rand = bt.le_rand(timer)?;
let ediv_bytes = bt.le_rand(timer)?;
let mut ediv = u16::from_le_bytes([ediv_bytes[0], ediv_bytes[1]]);
if ediv == 0 {
ediv = 1;
}
self.bonded_ltk = ltk;
self.bonded_rand = rand;
self.bonded_ediv = ediv;
self.have_bond = true;
self.distributed = true;
let mut encryption_information = [0u8; 17];
encryption_information[0] = SMP_ENCRYPTION_INFORMATION;
encryption_information[1..17].copy_from_slice(<k);
let mut master_identification = [0u8; 11];
master_identification[0] = SMP_MASTER_IDENTIFICATION;
master_identification[1..3].copy_from_slice(&ediv.to_le_bytes());
master_identification[3..11].copy_from_slice(&rand);
Ok(Some(KeyDistribution {
encryption_information,
master_identification,
}))
}
pub fn handle(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Option<usize>, Error> {
let Some(&code) = pdu.first() else {
return Ok(None);
};
match code {
SMP_PAIRING_REQUEST => self.on_pairing_request(bt, timer, pdu, out),
SMP_PAIRING_CONFIRM => self.on_pairing_confirm(bt, timer, pdu, out),
SMP_PAIRING_RANDOM => self.on_pairing_random(bt, timer, pdu, out),
SMP_PAIRING_FAILED => {
self.state = State::Idle;
Ok(None)
}
_ => Ok(None),
}
}
fn on_pairing_request(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Option<usize>, Error> {
if pdu.len() < 7 {
return Ok(Some(fail(out, SMP_ERR_UNSPECIFIED)));
}
self.ctx.preq.copy_from_slice(&pdu[0..7]);
self.prnd[0..8].copy_from_slice(&bt.le_rand(timer)?);
self.prnd[8..16].copy_from_slice(&bt.le_rand(timer)?);
let (authreq, rkd) = if self.bonding {
(AUTH_REQ_BONDING, KEY_DIST_ENC_KEY)
} else {
(AUTH_REQ_NO_BONDING, 0x00)
};
let pres = [
SMP_PAIRING_RESPONSE,
IO_CAP_NO_INPUT_NO_OUTPUT,
0x00,
authreq,
MAX_ENC_KEY_SIZE,
0x00,
rkd,
];
self.ctx.pres.copy_from_slice(&pres);
self.state = State::WaitConfirm;
out[0..7].copy_from_slice(&pres);
Ok(Some(7))
}
fn on_pairing_confirm(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Option<usize>, Error> {
if self.state != State::WaitConfirm || pdu.len() < 17 {
return Ok(Some(fail(out, SMP_ERR_UNSPECIFIED)));
}
self.mconfirm.copy_from_slice(&pdu[1..17]);
let confirm = self
.crypto
.c1(bt, timer, &TK_JUST_WORKS, &self.prnd, &self.ctx)?;
self.state = State::WaitRandom;
out[0] = SMP_PAIRING_CONFIRM;
out[1..17].copy_from_slice(&confirm);
Ok(Some(17))
}
fn on_pairing_random(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Option<usize>, Error> {
if self.state != State::WaitRandom || pdu.len() < 17 {
return Ok(Some(fail(out, SMP_ERR_UNSPECIFIED)));
}
let mut mrand = [0u8; 16];
mrand.copy_from_slice(&pdu[1..17]);
let check = self
.crypto
.c1(bt, timer, &TK_JUST_WORKS, &mrand, &self.ctx)?;
if check != self.mconfirm {
self.state = State::Idle;
return Ok(Some(fail(out, SMP_ERR_CONFIRM_FAILED)));
}
self.stk = self
.crypto
.s1(bt, timer, &TK_JUST_WORKS, &self.prnd, &mrand)?;
self.have_stk = true;
self.state = State::WaitLtk;
out[0] = SMP_PAIRING_RANDOM;
out[1..17].copy_from_slice(&self.prnd);
Ok(Some(17))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Action {
Idle,
Send(usize),
StartEncryption,
Failed(usize),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum InitState {
Idle,
WaitResponse,
WaitConfirm,
WaitRandom,
WaitEncryption,
}
pub struct Initiator {
crypto: Crypto,
bonding: bool,
state: InitState,
ctx: PairingContext,
mrand: [u8; 16],
sconfirm: [u8; 16],
stk: [u8; 16],
have_stk: bool,
peer_ltk: [u8; 16],
peer_ediv: u16,
peer_rand: [u8; 8],
have_bond: bool,
}
impl Initiator {
pub fn new(bt: &mut Bluetooth, timer: &Timer, bonding: bool) -> Result<Self, Error> {
Ok(Self {
crypto: Crypto::new(bt, timer)?,
bonding,
state: InitState::Idle,
ctx: PairingContext {
preq: [0; 7],
pres: [0; 7],
ia: [0; 6],
iat: 0,
ra: [0; 6],
rat: 0,
},
mrand: [0; 16],
sconfirm: [0; 16],
stk: [0; 16],
have_stk: false,
peer_ltk: [0; 16],
peer_ediv: 0,
peer_rand: [0; 8],
have_bond: false,
})
}
pub fn begin(
&mut self,
own_addr: [u8; 6],
own_addr_type: u8,
peer_addr: [u8; 6],
peer_addr_type: u8,
) {
self.ctx.ia = own_addr;
self.ctx.iat = own_addr_type;
self.ctx.ra = peer_addr;
self.ctx.rat = peer_addr_type;
self.state = InitState::Idle;
self.have_stk = false;
}
pub fn reset(&mut self) {
self.state = InitState::Idle;
self.have_stk = false;
}
pub fn start_pairing(&mut self, out: &mut [u8]) -> usize {
let (authreq, rkd) = if self.bonding {
(AUTH_REQ_BONDING, KEY_DIST_ENC_KEY)
} else {
(AUTH_REQ_NO_BONDING, 0x00)
};
let preq = [
SMP_PAIRING_REQUEST,
IO_CAP_NO_INPUT_NO_OUTPUT,
0x00,
authreq,
MAX_ENC_KEY_SIZE,
0x00,
rkd,
];
self.ctx.preq.copy_from_slice(&preq);
self.state = InitState::WaitResponse;
out[0..7].copy_from_slice(&preq);
7
}
pub fn short_term_key(&self) -> [u8; 16] {
self.stk
}
pub fn bond(&self) -> Option<Bond> {
self.have_bond.then_some(Bond {
ltk: self.peer_ltk,
ediv: self.peer_ediv,
rand: self.peer_rand,
})
}
pub fn restore_bond(&mut self, bond: &Bond) {
self.peer_ltk = bond.ltk;
self.peer_ediv = bond.ediv;
self.peer_rand = bond.rand;
self.have_bond = true;
}
pub fn handle(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Action, Error> {
let Some(&code) = pdu.first() else {
return Ok(Action::Idle);
};
match code {
SMP_PAIRING_RESPONSE => self.on_pairing_response(bt, timer, pdu, out),
SMP_PAIRING_CONFIRM => self.on_pairing_confirm(pdu, out),
SMP_PAIRING_RANDOM => self.on_pairing_random(bt, timer, pdu, out),
SMP_ENCRYPTION_INFORMATION => {
if pdu.len() >= 17 {
self.peer_ltk.copy_from_slice(&pdu[1..17]);
}
Ok(Action::Idle)
}
SMP_MASTER_IDENTIFICATION => {
if pdu.len() >= 11 {
self.peer_ediv = u16::from_le_bytes([pdu[1], pdu[2]]);
self.peer_rand.copy_from_slice(&pdu[3..11]);
self.have_bond = true;
}
Ok(Action::Idle)
}
SMP_PAIRING_FAILED => {
self.state = InitState::Idle;
Ok(Action::Idle)
}
_ => Ok(Action::Idle),
}
}
fn on_pairing_response(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Action, Error> {
if self.state != InitState::WaitResponse || pdu.len() < 7 {
self.state = InitState::Idle;
return Ok(Action::Failed(fail(out, SMP_ERR_UNSPECIFIED)));
}
self.ctx.pres.copy_from_slice(&pdu[0..7]);
self.mrand[0..8].copy_from_slice(&bt.le_rand(timer)?);
self.mrand[8..16].copy_from_slice(&bt.le_rand(timer)?);
let confirm = self
.crypto
.c1(bt, timer, &TK_JUST_WORKS, &self.mrand, &self.ctx)?;
self.state = InitState::WaitConfirm;
out[0] = SMP_PAIRING_CONFIRM;
out[1..17].copy_from_slice(&confirm);
Ok(Action::Send(17))
}
fn on_pairing_confirm(&mut self, pdu: &[u8], out: &mut [u8]) -> Result<Action, Error> {
if self.state != InitState::WaitConfirm || pdu.len() < 17 {
self.state = InitState::Idle;
return Ok(Action::Failed(fail(out, SMP_ERR_UNSPECIFIED)));
}
self.sconfirm.copy_from_slice(&pdu[1..17]);
self.state = InitState::WaitRandom;
out[0] = SMP_PAIRING_RANDOM;
out[1..17].copy_from_slice(&self.mrand);
Ok(Action::Send(17))
}
fn on_pairing_random(
&mut self,
bt: &mut Bluetooth,
timer: &Timer,
pdu: &[u8],
out: &mut [u8],
) -> Result<Action, Error> {
if self.state != InitState::WaitRandom || pdu.len() < 17 {
self.state = InitState::Idle;
return Ok(Action::Failed(fail(out, SMP_ERR_UNSPECIFIED)));
}
let mut srand = [0u8; 16];
srand.copy_from_slice(&pdu[1..17]);
let check = self
.crypto
.c1(bt, timer, &TK_JUST_WORKS, &srand, &self.ctx)?;
if check != self.sconfirm {
self.state = InitState::Idle;
return Ok(Action::Failed(fail(out, SMP_ERR_CONFIRM_FAILED)));
}
self.stk = self
.crypto
.s1(bt, timer, &TK_JUST_WORKS, &srand, &self.mrand)?;
self.have_stk = true;
self.state = InitState::WaitEncryption;
Ok(Action::StartEncryption)
}
}
fn fail(out: &mut [u8], reason: u8) -> usize {
out[0] = SMP_PAIRING_FAILED;
out[1] = reason;
2
}