implement MAC auth for felica
This commit is contained in:
@@ -22,6 +22,7 @@ cbc = { version = "~0.2", default-features = false, features = ["block-padding"]
|
||||
heapless = "~0.9"
|
||||
thiserror = { version = "~2", default-features = false }
|
||||
bitfields = "3.1.0"
|
||||
rand_core = "0.10.1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
+4
-34
@@ -107,41 +107,11 @@ pub const PN532_GPIO_P33: u8 = 3;
|
||||
pub const PN532_GPIO_P34: u8 = 4;
|
||||
pub const PN532_GPIO_P35: u8 = 5;
|
||||
|
||||
// FeliCa limits.
|
||||
pub const fn felica_read_max_service_num() -> usize {
|
||||
if cfg!(feature = "felica-lite-s") {
|
||||
8
|
||||
} else {
|
||||
16
|
||||
}
|
||||
}
|
||||
pub const fn felica_read_max_block_num() -> usize {
|
||||
if cfg!(feature = "felica-lite-s") {
|
||||
4
|
||||
} else {
|
||||
12
|
||||
}
|
||||
}
|
||||
pub const fn felica_write_max_service_num() -> usize {
|
||||
if cfg!(feature = "felica-lite-s") {
|
||||
4
|
||||
} else {
|
||||
16
|
||||
}
|
||||
}
|
||||
pub const fn felica_write_max_block_num() -> usize {
|
||||
if cfg!(feature = "felica-lite-s") {
|
||||
2
|
||||
} else {
|
||||
10
|
||||
}
|
||||
}
|
||||
pub const FELICA_READ_MAX_SERVICE_NUM: usize = 3;
|
||||
pub const FELICA_READ_MAX_BLOCK_NUM: usize = 3;
|
||||
|
||||
pub const FELICA_READ_MAX_SERVICE_NUM: usize = felica_read_max_service_num();
|
||||
pub const FELICA_READ_MAX_BLOCK_NUM: usize = felica_read_max_block_num();
|
||||
|
||||
pub const FELICA_WRITE_MAX_SERVICE_NUM: usize = felica_write_max_service_num();
|
||||
pub const FELICA_WRITE_MAX_BLOCK_NUM: usize = felica_write_max_block_num();
|
||||
pub const FELICA_WRITE_MAX_SERVICE_NUM: usize = 3;
|
||||
pub const FELICA_WRITE_MAX_BLOCK_NUM: usize = 3;
|
||||
|
||||
pub const FELICA_REQ_SERVICE_MAX_NODE_NUM: usize = 32;
|
||||
|
||||
|
||||
+4
-7
@@ -15,15 +15,12 @@ pub struct Uid {
|
||||
impl core::fmt::Display for Uid {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
#[cfg(feature = "alloc")]
|
||||
return write!(f, "{}", hex::encode(&self.bytes));
|
||||
{
|
||||
write!(f, "{}", hex::encode(&self.bytes))
|
||||
}
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
let mut buf: heapless::Vec<u8, 14> = heapless::Vec::new();
|
||||
hex::encode_to_slice(&self.bytes, &mut buf)
|
||||
.map_err(|_| core::fmt::Error)?;
|
||||
let s = heapless::String::from_utf8(buf)
|
||||
.map_err(|_| core::fmt::Error)?;
|
||||
write!(f, "{}", s)
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
use core::fmt::Debug;
|
||||
use bitfields::bitflag;
|
||||
use defmt::Debug2Format;
|
||||
use crate::felica;
|
||||
|
||||
/// Driver-level error type, generic over the transport (I2C) error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||
@@ -29,8 +30,8 @@ pub enum Error<E> {
|
||||
#[error("Invalid baud rate")]
|
||||
InvalidBaudRate,
|
||||
#[cfg(feature = "felica")]
|
||||
#[error("Felica error")]
|
||||
FelicaError
|
||||
#[error(transparent)]
|
||||
FelicaError(#[from] felica::error::Error),
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Status error: sf1:{0}, sf2:{1}")]
|
||||
Status(u8, u8)
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for Error {
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
defmt::write!(f, "{}", defmt::Display2Format(self))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
use des::cipher::{Block, BlockCipherEncrypt, Key, KeyInit};
|
||||
use des::TdesEde2;
|
||||
use rand_core::Rng;
|
||||
use crate::{Interface, Pn532};
|
||||
use crate::felica::types::{FelicaLiteSBlock, FelicaLiteSServiceCode, IDm, PMm};
|
||||
|
||||
impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
pub fn set_random_challenge(&mut self, card: &IDm, pmm: &PMm, rng: &mut impl Rng)
|
||||
-> Result<([u8; 8], [u8; 8]), crate::Error<B::TransportError>>
|
||||
{
|
||||
let mut data = [[0u8; 16]];
|
||||
rng.fill_bytes(&mut data[0]);
|
||||
self.felica_write_without_encryption(
|
||||
card, pmm,
|
||||
&[FelicaLiteSServiceCode::Write],
|
||||
&[FelicaLiteSBlock::RC],
|
||||
&data,
|
||||
)?;
|
||||
let rc1 = data[0][0..8].try_into().unwrap();
|
||||
let rc2 = data[0][8..16].try_into().unwrap();
|
||||
Ok((rc1, rc2))
|
||||
}
|
||||
|
||||
pub fn get_session_keys(
|
||||
&mut self,
|
||||
rc: &[u8; 16],
|
||||
card_key: &[u8; 16],
|
||||
) -> Result<([u8; 8], [u8; 8]), crate::Error<B::TransportError>> {
|
||||
// FeliCa byte order:
|
||||
//
|
||||
// CK1 = CK[0..8]
|
||||
// CK2 = CK[8..16]
|
||||
//
|
||||
// The DES interface receives each half reversed.
|
||||
let mut des_key = [0u8; 16];
|
||||
|
||||
des_key[0..8].copy_from_slice(&{
|
||||
let mut x = [0u8; 8];
|
||||
x.copy_from_slice(&card_key[0..8]);
|
||||
x.reverse();
|
||||
x
|
||||
});
|
||||
|
||||
des_key[8..16].copy_from_slice(&{
|
||||
let mut x = [0u8; 8];
|
||||
x.copy_from_slice(&card_key[8..16]);
|
||||
x.reverse();
|
||||
x
|
||||
});
|
||||
|
||||
// RC1 || RC2, with each 8-byte half reversed.
|
||||
let mut des_rc = [0u8; 16];
|
||||
|
||||
des_rc[0..8].copy_from_slice(&{
|
||||
let mut x = [0u8; 8];
|
||||
x.copy_from_slice(&rc[0..8]);
|
||||
x.reverse();
|
||||
x
|
||||
});
|
||||
|
||||
des_rc[8..16].copy_from_slice(&{
|
||||
let mut x = [0u8; 8];
|
||||
x.copy_from_slice(&rc[8..16]);
|
||||
x.reverse();
|
||||
x
|
||||
});
|
||||
|
||||
let key = Key::<TdesEde2>::try_from(des_key).unwrap();
|
||||
let cipher = TdesEde2::new(&key);
|
||||
|
||||
// 2-key 3DES-CBC, IV = 0.
|
||||
let mut state = Block::<TdesEde2>::default();
|
||||
|
||||
// RC1
|
||||
for i in 0..8 {
|
||||
state[i] ^= des_rc[i];
|
||||
}
|
||||
cipher.encrypt_block(&mut state);
|
||||
|
||||
let sk1_des: [u8; 8] = *state.as_array().unwrap();
|
||||
|
||||
// RC2
|
||||
for i in 0..8 {
|
||||
state[i] ^= des_rc[8 + i];
|
||||
}
|
||||
cipher.encrypt_block(&mut state);
|
||||
|
||||
let sk2_des: [u8; 8] = *state.as_array().unwrap();
|
||||
|
||||
// Convert the DES-side values back to FeliCa byte order.
|
||||
let mut sk1 = sk1_des;
|
||||
sk1.reverse();
|
||||
|
||||
let mut sk2 = sk2_des;
|
||||
sk2.reverse();
|
||||
|
||||
crate::debug!("RC: {:02X}", rc);
|
||||
crate::debug!("CK DES key: {:02X}", des_key);
|
||||
crate::debug!("RC DES input: {:02X}", des_rc);
|
||||
crate::debug!("SK1 DES: {:02X}", sk1_des);
|
||||
crate::debug!("SK1: {:02X}", sk1);
|
||||
crate::debug!("SK2 DES: {:02X}", sk2_des);
|
||||
crate::debug!("SK2: {:02X}", sk2);
|
||||
|
||||
Ok((sk1, sk2))
|
||||
}
|
||||
|
||||
pub fn get_plain_text(&mut self, card: &IDm, pmm: &PMm)
|
||||
-> Result<([u8; 56], [u8; 8]), crate::Error<B::TransportError>>
|
||||
{
|
||||
let mut data = [[0u8; 16]; 3];
|
||||
self.felica_read_without_encryption(
|
||||
card, pmm,
|
||||
&[FelicaLiteSServiceCode::Read],
|
||||
&[FelicaLiteSBlock::ID, FelicaLiteSBlock::CKV, FelicaLiteSBlock::MACA],
|
||||
&mut data
|
||||
)?;
|
||||
let id = data[0];
|
||||
let ckv = data[1];
|
||||
let maca = data[2];
|
||||
let mut data = [0u8; 56];
|
||||
data[0] = FelicaLiteSBlock::ID as u8;
|
||||
data[1] = 0x00;
|
||||
data[2] = FelicaLiteSBlock::CKV as u8;
|
||||
data[3] = 0x00;
|
||||
data[4] = FelicaLiteSBlock::MACA as u8;
|
||||
data[5] = 0x00;
|
||||
data[6] = 0xFF;
|
||||
data[7] = 0xFF;
|
||||
data[8..24].copy_from_slice(&id);
|
||||
data[24..40].copy_from_slice(&ckv);
|
||||
data[40..56].copy_from_slice(&maca);
|
||||
crate::debug!("{} {}", maca[0..8], maca[8..16]);
|
||||
let maca: [u8; 8] = maca[0..8].try_into().unwrap();
|
||||
Ok((data, maca))
|
||||
}
|
||||
|
||||
pub fn check_mac(
|
||||
&mut self,
|
||||
card: &IDm,
|
||||
pmm: &PMm,
|
||||
sk1: &[u8; 8],
|
||||
sk2: &[u8; 8],
|
||||
iv: &[u8; 8],
|
||||
) -> Result<bool, crate::Error<B::TransportError>> {
|
||||
let (data, mac_a) = self.get_plain_text(card, pmm)?;
|
||||
|
||||
// data layout from get_plain_text():
|
||||
//
|
||||
// [0..8] = block list
|
||||
// [8..24] = ID block
|
||||
// [24..40] = CKV block
|
||||
// [40..56] = MAC_A block (NOT used in MAC calculation)
|
||||
|
||||
let block_list = &data[0..8];
|
||||
let block_data = &data[8..40];
|
||||
|
||||
// FeliCa Lite-S uses the session keys with their byte order reversed
|
||||
// at the DES interface.
|
||||
let mut key_bytes = [0u8; 16];
|
||||
|
||||
for i in 0..8 {
|
||||
key_bytes[i] = sk1[7 - i];
|
||||
key_bytes[8 + i] = sk2[7 - i];
|
||||
}
|
||||
|
||||
let key = Key::<TdesEde2>::try_from(key_bytes).unwrap();
|
||||
let cipher = TdesEde2::new(&key);
|
||||
|
||||
// RC1 is also byte-reversed at the DES interface.
|
||||
let mut state_bytes = *iv;
|
||||
state_bytes.reverse();
|
||||
|
||||
// The initial plaintext for MAC_A is the block-number list.
|
||||
// Byte order is reversed before the DES operation.
|
||||
let mut first = [0u8; 8];
|
||||
first.copy_from_slice(block_list);
|
||||
first.reverse();
|
||||
|
||||
for i in 0..8 {
|
||||
state_bytes[i] ^= first[i];
|
||||
}
|
||||
|
||||
let mut state = Block::<TdesEde2>::try_from(state_bytes).unwrap();
|
||||
cipher.encrypt_block(&mut state);
|
||||
|
||||
crate::debug!(
|
||||
"MAC block list: {:02X}",
|
||||
block_list
|
||||
);
|
||||
|
||||
crate::debug!(
|
||||
"MAC first input: {:02X}",
|
||||
state_bytes
|
||||
);
|
||||
|
||||
// Process ID and CKV.
|
||||
//
|
||||
// Each 8-byte piece is byte-reversed before entering the
|
||||
// 2-key 3DES CBC operation.
|
||||
for chunk in block_data.chunks_exact(8) {
|
||||
let mut input = [0u8; 8];
|
||||
input.copy_from_slice(chunk);
|
||||
input.reverse();
|
||||
|
||||
for i in 0..8 {
|
||||
input[i] ^= state[i];
|
||||
}
|
||||
|
||||
crate::debug!("MAC input: {:02X}", input);
|
||||
|
||||
let mut block = Block::<TdesEde2>::try_from(input).unwrap();
|
||||
cipher.encrypt_block(&mut block);
|
||||
|
||||
state = block;
|
||||
}
|
||||
|
||||
// Reverse the final result back to FeliCa byte order.
|
||||
let mut mac: [u8; 8] = *state.as_array().unwrap();
|
||||
mac.reverse();
|
||||
|
||||
crate::debug!("mac: {:02X}, mac_a: {:02X}", mac, mac_a);
|
||||
|
||||
Ok(mac == mac_a[0..8])
|
||||
}
|
||||
}
|
||||
+60
-58
@@ -1,14 +1,12 @@
|
||||
mod types;
|
||||
|
||||
pub(crate) use types::AsBlock;
|
||||
pub use types::Block as FelicaBlock;
|
||||
pub use types::PollingRequestCode as FelicaPollingRequestCode;
|
||||
pub use types::PollingResponse as FelicaPollingResponse;
|
||||
pub use types::ServiceCode as FelicaServiceCode;
|
||||
pub mod types;
|
||||
#[cfg(feature="felica-lite-s")]
|
||||
mod felica_lite_s;
|
||||
pub mod error;
|
||||
|
||||
use error::Error as FelicaError;
|
||||
use crate::commands::*;
|
||||
use crate::driver::Pn532;
|
||||
use crate::error::{Error, StatusCode};
|
||||
use crate::error::Error;
|
||||
use crate::interface::Interface;
|
||||
use crate::BaudRate;
|
||||
use core::time::Duration;
|
||||
@@ -20,9 +18,9 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
&mut self,
|
||||
system_code: Option<u16>,
|
||||
baud_rate: BaudRate,
|
||||
request_code: PollingRequestCode,
|
||||
request_code: FelicaPollingRequestCode,
|
||||
timeout: Duration,
|
||||
) -> Result<Option<PollingResponse>, Error<B::TransportError>> {
|
||||
) -> Result<Option<FelicaPollingResponse>, Error<B::TransportError>> {
|
||||
if !matches!(baud_rate, BaudRate::Felica212kbps | BaudRate::Felica424kbps) {
|
||||
return Err(Error::InvalidBaudRate);
|
||||
}
|
||||
@@ -55,8 +53,9 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
if response_length != 18 && response_length != 20 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
|
||||
|
||||
let mut idm = [0u8; 8];
|
||||
|
||||
idm.copy_from_slice(&self.buffer[4..12]);
|
||||
let idm = IDm::from(idm);
|
||||
|
||||
@@ -70,7 +69,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Some(PollingResponse {
|
||||
Ok(Some(FelicaPollingResponse {
|
||||
idm,
|
||||
pmm,
|
||||
system_code_response,
|
||||
@@ -108,49 +107,8 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
Ok(response_len)
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Request Service" command.
|
||||
pub fn felica_request_service(
|
||||
&mut self,
|
||||
card: &IDm,
|
||||
node_code_list: &[u16],
|
||||
key_versions: &mut [u16],
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
let num_node = node_code_list.len();
|
||||
if num_node > FELICA_REQ_SERVICE_MAX_NODE_NUM || num_node > key_versions.len() {
|
||||
return Err(Error::InvalidParam);
|
||||
}
|
||||
|
||||
let mut cmd = [0u8; 1 + 8 + 1 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
|
||||
let mut j = 0;
|
||||
cmd[j] = FELICA_CMD_REQUEST_SERVICE;
|
||||
j += 1;
|
||||
cmd[j..j + 8].copy_from_slice(&card.into_be_bytes());
|
||||
j += 8;
|
||||
cmd[j] = num_node as u8;
|
||||
j += 1;
|
||||
for &code in node_code_list {
|
||||
cmd[j] = code as u8;
|
||||
cmd[j + 1] = (code >> 8) as u8;
|
||||
j += 2;
|
||||
}
|
||||
|
||||
let mut response = [0u8; 10 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
|
||||
let response_len = self.felica_send_command(
|
||||
&cmd[..j],
|
||||
&mut response,
|
||||
Duration::from_millis(200)
|
||||
)?;
|
||||
if response_len != 10 + 2 * num_node {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
for (i, kv) in key_versions.iter_mut().enumerate().take(num_node) {
|
||||
*kv = u16::from_le_bytes([response[10 + i * 2], response[10 + i * 2 + 1]]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Request Response" command, returning the card's mode.
|
||||
pub fn felica_request_response(&mut self, card: &PollingResponse) -> Result<u8, Error<B::TransportError>> {
|
||||
pub fn felica_request_response(&mut self, card: &FelicaPollingResponse) -> Result<u8, Error<B::TransportError>> {
|
||||
let mut cmd = [0u8; 9];
|
||||
cmd[0] = FELICA_CMD_REQUEST_RESPONSE;
|
||||
cmd[1..9].copy_from_slice(&card.idm.into_be_bytes());
|
||||
@@ -202,7 +160,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
// command
|
||||
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
|
||||
cmd.push(FELICA_CMD_READ_WITHOUT_ENCRYPTION).unwrap();
|
||||
cmd.extend_from_slice(&card.into_be_bytes()).unwrap();
|
||||
cmd.extend_from_slice(&card.into_bytes()).unwrap();
|
||||
cmd.push(num_service as u8).unwrap();
|
||||
for sc in service_code_list {
|
||||
let sc: [u8; 2] = sc.as_service_code().to_le_bytes();
|
||||
@@ -224,11 +182,12 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
&mut response,
|
||||
pmm.get_read_timeout(num_block, Some(Duration::from_millis(50)))
|
||||
)?;
|
||||
crate::trace!("Read Response: {:02X}", response);
|
||||
if response_len != 12 + 16 * num_block {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response[9] != 0 || response[10] != 0 {
|
||||
return Err(Error::Status(StatusCode::from_bits(response[9])));
|
||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||
}
|
||||
|
||||
let mut k = 12;
|
||||
@@ -275,7 +234,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
// command
|
||||
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
|
||||
cmd.push(FELICA_CMD_WRITE_WITHOUT_ENCRYPTION).unwrap();
|
||||
cmd.extend_from_slice(&card.into_be_bytes()).unwrap();
|
||||
cmd.extend_from_slice(&card.into_bytes()).unwrap();
|
||||
cmd.push(num_service as u8).unwrap();
|
||||
for sc in service_code_list {
|
||||
let sc = sc.as_service_code().to_le_bytes();
|
||||
@@ -301,7 +260,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response[9] != 0 || response[10] != 0 {
|
||||
return Err(Error::Status(StatusCode::from_bits(response[9])));
|
||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -309,6 +268,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
/// Send a FeliCa "Request System Code" command.
|
||||
///
|
||||
/// Returns the number of system codes written to `system_code_list`.
|
||||
#[cfg(not(feature = "felica-lite-s"))]
|
||||
pub fn felica_request_system_code(
|
||||
&mut self,
|
||||
card: &IDm,
|
||||
@@ -337,4 +297,46 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
|
||||
}
|
||||
Ok(num_system_code)
|
||||
}
|
||||
|
||||
/// Send a FeliCa "Request Service" command.
|
||||
#[cfg(not(feature = "felica-lite-s"))]
|
||||
pub fn felica_request_service(
|
||||
&mut self,
|
||||
card: &IDm,
|
||||
node_code_list: &[u16],
|
||||
key_versions: &mut [u16],
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
let num_node = node_code_list.len();
|
||||
if num_node > FELICA_REQ_SERVICE_MAX_NODE_NUM || num_node > key_versions.len() {
|
||||
return Err(Error::InvalidParam);
|
||||
}
|
||||
|
||||
let mut cmd = [0u8; 1 + 8 + 1 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
|
||||
let mut j = 0;
|
||||
cmd[j] = FELICA_CMD_REQUEST_SERVICE;
|
||||
j += 1;
|
||||
cmd[j..j + 8].copy_from_slice(&card.into_be_bytes());
|
||||
j += 8;
|
||||
cmd[j] = num_node as u8;
|
||||
j += 1;
|
||||
for &code in node_code_list {
|
||||
cmd[j] = code as u8;
|
||||
cmd[j + 1] = (code >> 8) as u8;
|
||||
j += 2;
|
||||
}
|
||||
|
||||
let mut response = [0u8; 10 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
|
||||
let response_len = self.felica_send_command(
|
||||
&cmd[..j],
|
||||
&mut response,
|
||||
Duration::from_millis(200)
|
||||
)?;
|
||||
if response_len != 10 + 2 * num_node {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
for (i, kv) in key_versions.iter_mut().enumerate().take(num_node) {
|
||||
*kv = u16::from_le_bytes([response[10 + i * 2], response[10 + i * 2 + 1]]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// use core::fmt::{Display, Formatter};
|
||||
// use bitfields::bitfield;
|
||||
//
|
||||
// #[bitfield([u8; 16])]
|
||||
// #[derive(Eq, PartialEq)]
|
||||
// pub struct ChallengeKey {
|
||||
// pub ck1: [u8; 8],
|
||||
// pub ck2: [u8; 8],
|
||||
// }
|
||||
//
|
||||
// impl ChallengeKey {
|
||||
// pub fn try_from_block(block: &[u8; 16]) -> Result<Self, <[u8; 8] as TryFrom<&[u8]>>::Error> {
|
||||
// let mut ck1: [u8; 8] = block[0..8].try_into()?;
|
||||
// ck1.reverse();
|
||||
// let mut ck2: [u8; 8] = block[8..16].try_into()?;
|
||||
// ck2.reverse();
|
||||
// let mut instance = Self::new();
|
||||
// instance.set_ck1(ck1);
|
||||
// instance.set_ck2(ck2);
|
||||
// Ok(instance)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Display for ChallengeKey {
|
||||
// fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
// #[cfg(feature = "alloc")]
|
||||
// {
|
||||
// let ck1 = hex::encode(self.ck1());
|
||||
// let ck2 = hex::encode(self.ck2());
|
||||
// write!(f, "{ck1},{ck2}")
|
||||
// }
|
||||
// #[cfg(not(feature = "alloc"))]
|
||||
// {
|
||||
// write!(f, "{:?}", self)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[cfg(feature = "defmt")]
|
||||
// impl defmt::Format for ChallengeKey {
|
||||
// fn format(&self, fmt: defmt::Formatter) {
|
||||
// defmt::write!(fmt, "{}", defmt::Display2Format(self))
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,9 @@
|
||||
use bitfields::bitfield;
|
||||
|
||||
#[bitfield([u8; 16])]
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct SubstractionRegisterBlock {
|
||||
pub reg_a: [u8; 4],
|
||||
pub reg_b: [u8; 4],
|
||||
pub reg_c: [u8; 8],
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
pub mod block;
|
||||
pub mod auth;
|
||||
|
||||
///! Types for Felica Lite-S (RC-S966)
|
||||
///! User Manual: https://www.sony.net/Products/felica/business/tech-support/data/fls_usmnl_1.4e.pdf
|
||||
|
||||
use bitfields::bitflag;
|
||||
use super::AsBlock;
|
||||
use super::AsServiceCode;
|
||||
|
||||
/// Service Code
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitflag(u16)]
|
||||
pub enum ServiceCode {
|
||||
#[base]
|
||||
Unknown = 0,
|
||||
/// FeliCa Lite read service code (read without key).
|
||||
Read = 0x000B,
|
||||
/// FeliCa Lite write service code (write without key).
|
||||
Write = 0x0009
|
||||
}
|
||||
|
||||
impl AsServiceCode for ServiceCode {
|
||||
fn as_service_code(&self) -> u16 {
|
||||
*self as u16
|
||||
}
|
||||
|
||||
fn from_service_code(val: u16) -> Self {
|
||||
Self::from_bits(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// FeliCa Lite system block numbers.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitflag(u16)]
|
||||
pub enum Block {
|
||||
#[base]
|
||||
Unknown = 0,
|
||||
PAD0 = 0x8000,
|
||||
PAD1 = 0x8001,
|
||||
PAD2 = 0x8002,
|
||||
PAD3 = 0x8003,
|
||||
PAD4 = 0x8004,
|
||||
PAD5 = 0x8005,
|
||||
PAD6 = 0x8006,
|
||||
PAD7 = 0x8007,
|
||||
PAD8 = 0x8008,
|
||||
PAD9 = 0x8009,
|
||||
PAD10 = 0x800A,
|
||||
PAD11 = 0x800B,
|
||||
PAD12 = 0x800C,
|
||||
PAD13 = 0x800D,
|
||||
/// Subtraction Register Block
|
||||
REG = 0x800E,
|
||||
/// Random challenge block.
|
||||
RC = 0x8080,
|
||||
/// MAC block.
|
||||
MAC = 0x8081,
|
||||
/// ID block.
|
||||
ID = 0x8082,
|
||||
/// Card key version block.
|
||||
CKV = 0x8086,
|
||||
/// Card key block.
|
||||
CK= 0x8087,
|
||||
/// Memory configuration block.
|
||||
MemoryConfig = 0x8088,
|
||||
/// Write counter block.
|
||||
WriteCounter = 0x8090,
|
||||
/// MAC_A block.
|
||||
MACA = 0x8091
|
||||
}
|
||||
|
||||
impl AsBlock for Block {
|
||||
fn as_block(&self) -> u16 {
|
||||
*self as u16
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use bitfields::bitflag;
|
||||
|
||||
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitflag(u8)]
|
||||
pub enum CardICType {
|
||||
#[base]
|
||||
Unknown = 0x00,
|
||||
S140 = 0x50,
|
||||
SA40_2P = 0x51,
|
||||
SA41_2C = 0x52,
|
||||
SA21_2 = 0x46,
|
||||
SA20_2 = 0x45,
|
||||
SA20_1 = 0x44,
|
||||
SA01_2 = 0x35,
|
||||
SA00_1 = 0x32,
|
||||
S962 = 0x20,
|
||||
S960 = 0x0D,
|
||||
S953 = 0x09,
|
||||
S952 = 0x08,
|
||||
S915 = 0x01,
|
||||
S982 = 0xF1,
|
||||
S978F = 0xF0,
|
||||
S967LiteS = 0xF2,
|
||||
S967Plug = 0xE1,
|
||||
S967NFC = 0xFF,
|
||||
S926 = 0xE0
|
||||
}
|
||||
|
||||
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MobileICType {
|
||||
/// 0x14~0x1F
|
||||
V3(u8),
|
||||
/// 0x10~0x13
|
||||
V2(u8),
|
||||
/// 0x06~0x07
|
||||
V1(u8)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
use bitfields::bitfield;
|
||||
|
||||
#[bitfield([u8; 8])]
|
||||
#[bitfield([u8; 8], from_endian = big, into_endian = big)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct IDm {
|
||||
pub manufacturer: u8,
|
||||
pub card_id: [u8; 7]
|
||||
pub manufacturer: [u8; 2],
|
||||
pub card_id: [u8; 6]
|
||||
}
|
||||
@@ -1,35 +1,29 @@
|
||||
mod polling;
|
||||
mod pmm;
|
||||
mod idm;
|
||||
#[cfg(feature = "felica-lite-s")]
|
||||
pub mod felica_lite_s;
|
||||
pub mod ic_type;
|
||||
|
||||
use bitfields::bitflag;
|
||||
pub use idm::*;
|
||||
pub use pmm::*;
|
||||
pub use polling::*;
|
||||
pub use polling::PollingRequestCode as FelicaPollingRequestCode;
|
||||
pub use polling::PollingResponse as FelicaPollingResponse;
|
||||
#[cfg(feature = "felica-lite-s")]
|
||||
pub use felica_lite_s::{Block as FelicaLiteSBlock, ServiceCode as FelicaLiteSServiceCode};
|
||||
|
||||
pub trait AsServiceCode {
|
||||
fn as_service_code(&self) -> u16;
|
||||
fn from_service_code(val: u16) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitflag(u16)]
|
||||
pub enum ServiceCode {
|
||||
#[base]
|
||||
Unknown = 0,
|
||||
/// FeliCa Lite read service code (read without key).
|
||||
Read = 0x000B,
|
||||
/// FeliCa Lite write service code (write without key).
|
||||
Write = 0x0009
|
||||
pub trait AsBlock {
|
||||
fn as_block(&self) -> u16;
|
||||
}
|
||||
|
||||
impl AsServiceCode for ServiceCode {
|
||||
fn as_service_code(&self) -> u16 {
|
||||
*self as u16
|
||||
}
|
||||
|
||||
fn from_service_code(val: u16) -> Self {
|
||||
Self::from_bits(val)
|
||||
impl AsBlock for u16 {
|
||||
fn as_block(&self) -> u16 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,81 +35,4 @@ impl AsServiceCode for u16 {
|
||||
fn from_service_code(val: u16) -> Self {
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsBlock {
|
||||
fn as_block(&self) -> u16;
|
||||
}
|
||||
|
||||
/// FeliCa Lite system block numbers.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitflag(u16)]
|
||||
pub enum Block {
|
||||
#[base]
|
||||
Unknown = 0,
|
||||
/// Random challenge block.
|
||||
RC = 0x8080,
|
||||
/// MAC block.
|
||||
MAC = 0x8081,
|
||||
/// ID block.
|
||||
ID = 0x8082,
|
||||
/// Card key version block.
|
||||
CKV = 0x8086,
|
||||
/// Card key block.
|
||||
CK= 0x8087,
|
||||
/// Memory configuration block.
|
||||
MemoryConfig = 0x8088,
|
||||
/// Write counter block.
|
||||
WriteCounter = 0x8090,
|
||||
/// MAC_A block.
|
||||
MACA = 0x8091
|
||||
}
|
||||
|
||||
impl AsBlock for Block {
|
||||
fn as_block(&self) -> u16 {
|
||||
*self as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBlock for u16 {
|
||||
fn as_block(&self) -> u16 {
|
||||
// A 2-byte block list element is `0x80 | block_number`: the MSB marks a
|
||||
// 2-byte element (access mode 0 = standard area, service list order 0).
|
||||
*self | 0x8000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitflag(u8)]
|
||||
pub enum CardICType {
|
||||
#[base]
|
||||
Unknown = 0x00,
|
||||
S140 = 0x50,
|
||||
SA40_2P = 0x51,
|
||||
SA41_2C = 0x52,
|
||||
SA21_2 = 0x46,
|
||||
SA20_2 = 0x45,
|
||||
SA20_1 = 0x44,
|
||||
SA01_2 = 0x35,
|
||||
SA00_1 = 0x32,
|
||||
S962 = 0x20,
|
||||
S960 = 0x0D,
|
||||
S953 = 0x09,
|
||||
S952 = 0x08,
|
||||
S915 = 0x01,
|
||||
S982 = 0xF1,
|
||||
S978F = 0xF0,
|
||||
S967LiteS = 0xF2,
|
||||
S967Plug = 0xE1,
|
||||
S967NFC = 0xFF,
|
||||
S926 = 0xE0
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MobileICType {
|
||||
V3(u8), // 0x14~0x1F
|
||||
V2(u8), // 0x10~0x13
|
||||
V1(u8) // 0x06~0x07
|
||||
}
|
||||
+2
-1
@@ -63,7 +63,8 @@ pub use error::*;
|
||||
pub use interface::*;
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
use defmt::debug;
|
||||
#[allow(unused_imports)]
|
||||
use defmt::{error, warn, info, debug, trace};
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
#[macro_export]
|
||||
|
||||
Reference in New Issue
Block a user