implement MAC auth for felica

This commit is contained in:
2026-08-28 21:56:37 +08:00
parent f523b7d394
commit 494f4b92eb
20 changed files with 562 additions and 233 deletions
+4 -34
View File
@@ -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
View File
@@ -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
View File
@@ -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")]
+12
View File
@@ -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))
}
}
+226
View File
@@ -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
View File
@@ -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
}
}
+39
View File
@@ -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)
}
+3 -3
View File
@@ -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]
}
+12 -95
View File
@@ -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
View File
@@ -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]