use crate::{
Error, Instruction, Key, KeyId, StatusCode, Unauthenticated,
client::{
AuthenticateExt, Authenticated, AuthenticationState, Card, KeyingState, Session,
command_header,
},
crc32,
crypto::xor,
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 mut key_id = self.authentication.session.get_key_id();
if self.application_id == [0; 3] {
key_id |= match new_key {
Key::Aes(_) => 0x80,
Key::Des(_) => 0x00,
};
}
let header: [u8; 2] = command_header!({
instruction: Instruction = Instruction::ChangeKey,
key_id: KeyId = key_id
}, 2);
let Self {
mut authentication,
application_id,
card,
} = self;
let (status_code, response) = match (&mut authentication.session, new_key) {
(Session::Aes { keying, .. }, Key::Aes(key)) => {
let crc = crc32(&header, &[&key, &[new_key_version]]).to_le_bytes();
io::encrypted_out_plain_in(
&card,
keying,
out,
&header,
&[&key, &[new_key_version], &crc],
)
.await?
}
(Session::Aes { keying, .. }, Key::Des(key)) => {
let crc = crc32(&header, &[&key, &[0; 8]]).to_le_bytes();
io::encrypted_out_plain_in(&card, keying, out, &header, &[&key, &[0; 8], &crc])
.await?
}
(Session::Des { keying, .. }, Key::Aes(key)) => {
let crc = crc32(&header, &[&key, &[new_key_version]]).to_le_bytes();
io::encrypted_out_plain_in(
&card,
keying,
out,
&header,
&[&key, &[new_key_version], &crc],
)
.await?
}
(Session::Des { keying, .. }, Key::Des(key)) => {
let crc = crc32(&header, &[&key]).to_le_bytes();
io::encrypted_out_plain_in(&card, keying, out, &header, &[&key, &crc]).await?
}
};
if !response.is_empty() {
return Err(Error::BadSize);
};
if status_code != StatusCode::Ack {
return Err(Error::BadStatusCode);
}
Ok(Card {
authentication: Unauthenticated,
application_id,
card,
})
}
pub async fn change_key(
&mut self,
out: &mut [u8],
key_id: KeyId,
current_key: Key,
new_key: Key,
new_key_version: u8,
) -> Result<(), Error<IoBackendT::Error>> {
if self.application_id == [0; 3] {
return Err(Error::NoSelectedApplication);
}
let header: [u8; 2] = command_header!({
instruction: Instruction = Instruction::ChangeKey,
key_id: KeyId = key_id
}, 2);
let mut xor_key = new_key;
let new_key_crc = match (&mut xor_key, ¤t_key) {
(Key::Aes(xor_key), Key::Aes(current_key)) => {
let crc = crc32(xor_key, &[]);
xor(xor_key, current_key);
crc
}
(Key::Des(xor_key), Key::Des(current_key)) => {
let crc = crc32(xor_key, &[]);
xor(xor_key, current_key);
crc
}
_ => {
return Err(Error::BadAlgorithm);
}
}
.to_le_bytes();
let (status_code, response) = match (&mut self.authentication.session, xor_key) {
(Session::Aes { keying, .. }, Key::Aes(key)) => {
let crc = crc32(&header, &[&key, &[new_key_version]]).to_le_bytes();
io::encrypted_out_cmac_in(
&self.card,
keying,
out,
&header,
&[&key, &[new_key_version], &crc, &new_key_crc],
)
.await?
}
(Session::Des { keying, .. }, Key::Des(key)) => {
let crc = crc32(&header, &[&key]).to_le_bytes();
io::encrypted_out_cmac_in(
&self.card,
keying,
out,
&header,
&[&key, &crc, &new_key_crc],
)
.await?
}
_ => {
return Err(Error::BadAlgorithm);
}
};
if !response.is_empty() {
return Err(Error::BadSize);
};
if status_code != StatusCode::Ack {
return Err(Error::BadStatusCode);
}
Ok(())
}
}