use crate::{
Error, Instruction, Key, KeyId, StatusCode, Unauthenticated,
client::{
AuthenticateExt, Authenticated, AuthenticationState, Card, KeyingState, Session,
command_header,
},
io,
};
impl<'card, IoBackendT, AuthenticationStateT> Card<'card, IoBackendT, AuthenticationStateT>
where
AuthenticationStateT: AuthenticationState,
IoBackendT: io::Backend,
IoBackendT: AuthenticateExt<8, des::Des>,
IoBackendT: AuthenticateExt<16, aes::Aes128>,
{
pub async fn authenticate(
self,
key_id: KeyId,
key: Key,
) -> Result<Card<'card, IoBackendT, Authenticated>, Error<IoBackendT::Error>> {
let session: Session = match key {
Key::Aes(key) => {
let session_key =
AuthenticateExt::<16, aes::Aes128>::authenticate(&self.card, key_id, key)
.await?;
Session::Aes {
key_id,
keying: KeyingState::<16, aes::Aes128>::new(session_key),
}
}
Key::Des(key) => {
let session_key =
AuthenticateExt::<8, des::Des>::authenticate(&self.card, key_id, key).await?;
Session::Des {
key_id,
keying: KeyingState::<8, des::Des>::new(session_key),
}
}
};
let Self {
card,
application_id,
authentication: _,
} = self;
Ok(Card {
card,
application_id,
authentication: Authenticated { session },
})
}
}
impl<'card, IoBackendT> Card<'card, IoBackendT, Authenticated>
where
IoBackendT: io::Backend,
{
pub async fn change_current_key(
self,
out: &mut [u8],
new_key: Key,
new_key_version: u8,
) -> Result<Card<'card, IoBackendT, Unauthenticated>, Error<IoBackendT::Error>> {
let header: &[u8] = command_header!({
instruction: Instruction = Instruction::ChangeKey,
key_id: KeyId = self.authentication.session.get_key_id()
});
let Self {
mut authentication,
application_id,
card,
} = self;
let (status_code, response) = match (&mut authentication.session, new_key) {
(Session::Aes { keying, .. }, Key::Aes(key)) => {
io::encrypted_out_plain_in(&card, keying, out, header, &[&key, &[new_key_version]])
.await?
}
(Session::Des { keying, .. }, Key::Des(key)) => {
io::encrypted_out_plain_in(&card, keying, out, header, &[&key]).await?
}
_ => {
return Err(Error::BadAlgorithm);
}
};
if !response.is_empty() {
return Err(Error::BadSize);
};
if status_code != StatusCode::Ack {
return Err(Error::BadStatusCode);
}
Ok(Card {
authentication: Unauthenticated,
application_id,
card,
})
}
}