use crate::error::Result;
use hap_crypto::SessionKeys;
fn nonce(counter: u64) -> [u8; 12] {
let mut n = [0u8; 12];
n[4..].copy_from_slice(&counter.to_le_bytes());
n
}
pub(crate) struct BleSession {
keys: SessionKeys,
send_counter: u64,
recv_counter: u64,
}
impl BleSession {
pub(crate) fn new(keys: SessionKeys) -> Self {
Self {
keys,
send_counter: 0,
recv_counter: 0,
}
}
pub(crate) fn seal(&mut self, pdu: &[u8]) -> Result<Vec<u8>> {
let out = hap_crypto::aead::chacha20poly1305_seal(
&self.keys.write_key,
&nonce(self.send_counter),
&[],
pdu,
)?;
self.send_counter += 1;
Ok(out)
}
pub(crate) fn open(&mut self, data: &[u8]) -> Result<Vec<u8>> {
let out = hap_crypto::aead::chacha20poly1305_open(
&self.keys.read_key,
&nonce(self.recv_counter),
&[],
data,
)?;
self.recv_counter += 1;
Ok(out)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use hap_crypto::SessionKeys;
#[test]
fn seal_then_peer_open_roundtrips() {
let keys = SessionKeys {
read_key: [7u8; 32],
write_key: [9u8; 32],
};
let mut ctrl = BleSession::new(keys);
let pdu = vec![0x00, 0x03, 0x11, 0x03, 0x02];
let sealed = ctrl.seal(&pdu).unwrap();
assert_ne!(sealed, pdu);
assert_eq!(sealed.len(), pdu.len() + 16);
let opened =
hap_crypto::aead::chacha20poly1305_open(&[9u8; 32], &nonce(0), &[], &sealed).unwrap();
assert_eq!(opened, pdu);
}
#[test]
fn counters_advance_per_message() {
let keys = SessionKeys {
read_key: [1u8; 32],
write_key: [2u8; 32],
};
let mut s = BleSession::new(keys);
let a = s.seal(&[1, 2, 3]).unwrap();
let b = s.seal(&[1, 2, 3]).unwrap();
assert_ne!(a, b); }
#[test]
fn open_uses_read_key_and_recv_counter() {
let keys = SessionKeys {
read_key: [5u8; 32],
write_key: [6u8; 32],
};
let mut s = BleSession::new(keys);
let cipher =
hap_crypto::aead::chacha20poly1305_seal(&[5u8; 32], &nonce(0), &[], &[9, 9]).unwrap();
assert_eq!(s.open(&cipher).unwrap(), vec![9, 9]);
}
}