use super::Handshake;
use crate::{
Error, Instruction, StatusCode,
crypto::{Backend, Kdf, Scheme},
io,
};
pub trait Initiate {
fn instruction() -> Instruction;
}
impl Initiate for Scheme<8, des::Des> {
fn instruction() -> Instruction {
Instruction::AuthenticateDes
}
}
impl Initiate for Scheme<16, aes::Aes128> {
fn instruction() -> Instruction {
Instruction::AuthenticateAes
}
}
pub trait AuthenticateExt<const KEY_SIZE: usize, AlgorithmT>
where
Scheme<KEY_SIZE, AlgorithmT>: Backend<KEY_SIZE>,
Self: io::Backend,
{
fn authenticate_with_rnd_a(
&mut self,
key_id: u8,
key: [u8; KEY_SIZE],
rnd_a: Option<[u8; KEY_SIZE]>,
) -> impl Future<Output = Result<[u8; KEY_SIZE], Error<<Self as io::Backend>::Error>>>;
fn authenticate(
&mut self,
key_id: u8,
key: [u8; KEY_SIZE],
) -> impl Future<Output = Result<[u8; KEY_SIZE], Error<<Self as io::Backend>::Error>>> {
self.authenticate_with_rnd_a(key_id, key, None)
}
}
impl<const KEY_SIZE: usize, AlgorithmT, IoBackendT> AuthenticateExt<KEY_SIZE, AlgorithmT>
for IoBackendT
where
IoBackendT: io::Backend,
([u8; KEY_SIZE], [u8; KEY_SIZE]): Kdf<KEY_SIZE>,
Scheme<KEY_SIZE, AlgorithmT>: Backend<KEY_SIZE>,
Scheme<KEY_SIZE, AlgorithmT>: Initiate,
{
async fn authenticate_with_rnd_a(
&mut self,
key_id: u8,
key: [u8; KEY_SIZE],
rnd_a: Option<[u8; KEY_SIZE]>,
) -> Result<[u8; KEY_SIZE], Error<<IoBackendT as io::Backend>::Error>> {
let mut buf_command = [0; 0xff];
let mut buf_response = [0; 0xff];
let (handshake, command) =
Handshake::<KEY_SIZE, AlgorithmT, _>::begin(&mut buf_command, key, key_id);
let (status_code, response) = self
.exchange(&mut buf_response, command)
.await
.map_err(Error::IoBackend)?;
if status_code != StatusCode::AdditionalData {
return Err(Error::BadStatusCode(status_code));
}
let (handshake, command) = if let Some(rnd_a) = rnd_a {
handshake.rnd_b_with_key(&mut buf_command, response, rnd_a)
} else {
handshake.rnd_b(&mut buf_command, response)
}?;
let (status_code, response) = self
.exchange(&mut buf_response, command)
.await
.map_err(Error::IoBackend)?;
if status_code != StatusCode::Ack {
return Err(Error::BadStatusCode(status_code));
}
Ok(handshake.complete(response)?.into_key())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::io::mock_backend;
use hex_literal::hex;
macro_rules! simulate_handshake {
(
$name:ident,
$key_size:literal,
$crypto:path,
key = $key:expr,
rnd_a = $rnd_a:expr,
session_key = $session_key:expr,
( $( ( $in:expr, $out:expr ) ),* )
) => {
#[tokio::test]
async fn $name() {
let mut mb = mock_backend!( $( ($in, $out) ),* );
let key = mb.authenticate_with_rnd_a(0x00, hex!($key), Some(hex!($rnd_a))).await.unwrap();
assert_eq!(hex!($session_key), key);
}
};
}
simulate_handshake!(
test_capture_des,
8,
des::Des,
key = "00 00 00 00 00 00 00 00",
rnd_a = "A0 CF B1 F4 35 29 4B 9B",
session_key = "A0 CE B0 F4 08 D4 60 DE",
(
(
"1A 00", "AF BE 06 BE 0C F7 19 E2 92" ),
(
"AF DB EB DB 42 F0 B8 87 0C F5 98 99 56 2A 44 C2 77", "00 8F 72 31 73 06 1C FF 81" )
)
);
simulate_handshake!(
test_capture_aes,
16,
aes::Aes128,
key = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
rnd_a = "DE 04 17 85 C0 C0 45 76 12 99 A6 67 C4 EB A7 EE",
session_key = "DE 04 17 85 F5 9C 23 F5 C4 EB A7 EE B7 89 78 55",
(
(
"AA 00", "AF BC 1C DE 5D 71 09 7F 97 DF E7 0D 24 A8 7A 4A 50" ),
(
"AF 3F D0 A9 C9 88 69 4E BB 12 35 49 C6 8D D6 61 B5 F9 69 6C 3D A4 6D 56 B7 FC 3B B4 8A 3B 6E A1 2F", "00 E7 60 EB 7A 31 DE 62 D5 C2 95 A2 D8 94 CA 18 14" )
)
);
}