use super::*;
const ISO_READ_BINARY_CHUNK: usize = 256;
const ISO_UPDATE_BINARY_CHUNK: usize = 255;
pub struct Unauthenticated;
impl Session<Unauthenticated> {
pub fn new() -> Self {
Self {
state: Unauthenticated,
ndef_selected: false,
ef_selected: None,
}
}
}
impl Default for Session<Unauthenticated> {
fn default() -> Self {
Self::new()
}
}
impl Session<Unauthenticated> {
pub(crate) async fn select_ndef_application<T: Transport>(
&mut self,
transport: &mut T,
) -> Result<(), SessionError<T::Error>> {
if self.ndef_selected {
return Ok(());
}
select_ndef_application(transport).await?;
self.ndef_selected = true;
self.ef_selected = None;
Ok(())
}
pub async fn read_file_unauthenticated<T: Transport>(
&mut self,
transport: &mut T,
file: File,
offset: u16,
buf: &mut [u8],
) -> Result<usize, SessionError<T::Error>> {
self.select_ndef_application(transport).await?;
if self.ef_selected != Some(file.file_id()) {
iso_select_ef_by_fid(transport, file.file_id()).await?;
self.ef_selected = Some(file.file_id());
}
if buf.is_empty() {
return iso_read_binary(transport, None, offset, buf).await;
}
let mut total: usize = 0;
while total < buf.len() {
let want = (buf.len() - total).min(ISO_READ_BINARY_CHUNK);
let abs = offset as usize + total;
if abs > 0x7FFF {
return Err(SessionError::InvalidCommandParameter {
parameter: "offset",
value: abs,
reason: "must be <= 0x7FFF",
});
}
let n =
iso_read_binary(transport, None, abs as u16, &mut buf[total..total + want]).await?;
total += n;
if n < want {
break;
}
}
Ok(total)
}
pub async fn write_file_unauthenticated<T: Transport>(
&mut self,
transport: &mut T,
file: File,
offset: u16,
data: &[u8],
) -> Result<(), SessionError<T::Error>> {
self.select_ndef_application(transport).await?;
if self.ef_selected != Some(file.file_id()) {
iso_select_ef_by_fid(transport, file.file_id()).await?;
self.ef_selected = Some(file.file_id());
}
if data.is_empty() {
return iso_update_binary(transport, None, offset, data).await;
}
let mut written: usize = 0;
while written < data.len() {
let chunk_len = (data.len() - written).min(ISO_UPDATE_BINARY_CHUNK);
let abs = offset as usize + written;
if abs > 0x7FFF {
return Err(SessionError::InvalidCommandParameter {
parameter: "offset",
value: abs,
reason: "must be <= 0x7FFF",
});
}
iso_update_binary(
transport,
None,
abs as u16,
&data[written..written + chunk_len],
)
.await?;
written += chunk_len;
}
Ok(())
}
pub async fn get_file_settings<T: Transport>(
&mut self,
transport: &mut T,
file: File,
) -> Result<FileSettingsView, SessionError<T::Error>> {
self.select_ndef_application(transport).await?;
get_file_settings(transport, file.file_no()).await
}
pub async fn get_version<T: Transport>(
&self,
transport: &mut T,
) -> Result<Version, SessionError<T::Error>> {
get_version(transport).await
}
pub async fn authenticate_aes<T: Transport>(
mut self,
transport: &mut T,
key_no: KeyNumber,
key: &[u8; 16],
rnd_a: [u8; 16],
) -> Result<Session<Authenticated<AesSuite>>, SessionError<T::Error>> {
self.select_ndef_application(transport).await?;
let ef_selected = self.ef_selected;
let auth_result = authenticate_ev2_first_aes(transport, key_no, key, rnd_a).await?;
Ok(Session {
state: Authenticated::with_auth_result(auth_result, key_no),
ndef_selected: true,
ef_selected,
})
}
pub async fn authenticate_lrp<T: Transport>(
mut self,
transport: &mut T,
key_no: KeyNumber,
key: &[u8; 16],
rnd_a: [u8; 16],
) -> Result<Session<Authenticated<LrpSuite>>, SessionError<T::Error>> {
self.select_ndef_application(transport).await?;
let ef_selected = self.ef_selected;
let auth_result = authenticate_ev2_first_lrp(transport, key_no, key, rnd_a).await?;
Ok(Session {
state: Authenticated::with_auth_result(auth_result, key_no),
ndef_selected: true,
ef_selected,
})
}
pub async fn verify_originality<T: Transport>(
&self,
transport: &mut T,
uid: &[u8; 7],
) -> Result<(), SessionError<T::Error>> {
let sig = read_sig(transport).await?;
originality::verify(uid, &sig).map_err(SessionError::OriginalityVerificationFailed)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::{Exchange, TestTransport, block_on};
use alloc::vec;
const SELECT_NDEF_APP: [u8; 13] = [
0x00, 0xA4, 0x04, 0x00, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00,
];
const SELECT_NDEF_EF: [u8; 7] = [0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04];
#[test]
fn write_file_unauthenticated_chunks_at_255_byte_boundary() {
let payload = &crate::testing::TEST_PAYLOAD_256;
let mut apdu1 = vec![0x00, 0xD6, 0x00, 0x00, 0xFF];
apdu1.extend_from_slice(&payload[..255]);
let apdu2 = [0x00, 0xD6, 0x00, 0xFF, 0x01, payload[255]];
let mut transport = TestTransport::new([
Exchange::new(&SELECT_NDEF_APP, &[], 0x90, 0x00),
Exchange::new(&SELECT_NDEF_EF, &[], 0x90, 0x00),
Exchange::new(&apdu1, &[], 0x90, 0x00),
Exchange::new(&apdu2, &[], 0x90, 0x00),
]);
block_on(Session::new().write_file_unauthenticated(&mut transport, File::Ndef, 0, payload))
.expect("chunked write ok");
assert_eq!(transport.remaining(), 0);
}
#[test]
fn read_file_unauthenticated_chunks_at_256_byte_boundary() {
let mut payload1 = [0u8; 256];
let mut payload2 = [0u8; 256];
for (i, b) in payload1.iter_mut().enumerate() {
*b = i as u8;
}
for (i, b) in payload2.iter_mut().enumerate() {
*b = (i ^ 0xAA) as u8;
}
let apdu1 = [0x00, 0xB0, 0x00, 0x00, 0x00];
let apdu2 = [0x00, 0xB0, 0x01, 0x00, 0x00];
let mut transport = TestTransport::new([
Exchange::new(&SELECT_NDEF_APP, &[], 0x90, 0x00),
Exchange::new(&SELECT_NDEF_EF, &[], 0x90, 0x00),
Exchange::new(&apdu1, &payload1, 0x90, 0x00),
Exchange::new(&apdu2, &payload2, 0x90, 0x00),
]);
let mut buf = [0u8; 512];
let n = block_on(Session::new().read_file_unauthenticated(
&mut transport,
File::Ndef,
0,
&mut buf,
))
.expect("chunked read ok");
assert_eq!(n, 512);
assert_eq!(&buf[..256], &payload1);
assert_eq!(&buf[256..], &payload2);
assert_eq!(transport.remaining(), 0);
}
#[test]
fn read_file_unauthenticated_stops_on_short_payload() {
let payload = [0xABu8; 100];
let apdu = [0x00, 0xB0, 0x00, 0x00, 0x00];
let mut transport = TestTransport::new([
Exchange::new(&SELECT_NDEF_APP, &[], 0x90, 0x00),
Exchange::new(&SELECT_NDEF_EF, &[], 0x90, 0x00),
Exchange::new(&apdu, &payload, 0x90, 0x00),
]);
let mut buf = [0u8; 256];
let n = block_on(Session::new().read_file_unauthenticated(
&mut transport,
File::Ndef,
0,
&mut buf,
))
.expect("read ok");
assert_eq!(n, 100);
assert_eq!(&buf[..100], &payload);
assert_eq!(transport.remaining(), 0);
}
}