refactor MAC auth

This commit is contained in:
2026-08-29 21:41:07 +08:00
parent 494f4b92eb
commit 8f65670a8f
23 changed files with 557 additions and 356 deletions
+2 -7
View File
@@ -8,6 +8,7 @@ use core::time::Duration;
/// A card UID read by [`Pn532::read_passive_target_id`].
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Uid {
pub bytes: heapless::Vec<u8, 7>,
}
@@ -25,15 +26,9 @@ impl core::fmt::Display for Uid {
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Uid {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{}", defmt::Display2Format(self));
}
}
/// Outcome of [`Pn532::tg_init_as_target`] / [`Pn532::tg_init_as_target_default`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TargetInitStatus {
/// The PN532 entered target mode.
Success,
+2 -14
View File
@@ -1,10 +1,10 @@
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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error<E> {
/// The underlying transport (I2C) reported an error.
#[error("transport error: {0:?}")]
@@ -34,14 +34,8 @@ pub enum Error<E> {
FelicaError(#[from] felica::error::Error),
}
#[cfg(feature = "defmt")]
impl<E: Debug> defmt::Format for Error<E> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{:?}", Debug2Format(self))
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitflag(u8)]
pub enum StatusCode {
#[base]
@@ -74,9 +68,3 @@ pub enum StatusCode {
NADMissing = 0x2E,
Max = 0x2F
}
impl defmt::Format for StatusCode {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{}", Debug2Format(self))
}
}
+5 -8
View File
@@ -1,12 +1,9 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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))
}
Status(u8, u8),
#[cfg(feature = "felica-lite-s")]
#[error("MAC mismatch")]
MACMismatch,
}
-226
View File
@@ -1,226 +0,0 @@
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])
}
}
@@ -0,0 +1,7 @@
use crate::{Interface, Pn532};
impl<B: Interface, const N: usize> Pn532<B, N> {
pub fn setup_first_issuance(&mut self) {
}
}
+171
View File
@@ -0,0 +1,171 @@
pub mod issuance;
use des::cipher::{Block, BlockCipherEncrypt, Key, KeyInit};
use des::TdesEde2;
use rand_core::Rng;
use crate::{Interface, Pn532};
use crate::felica::error::Error;
use crate::felica::types::{AsBlock, CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode};
use crate::felica::types::felica_lite_s::auth::{AuthSession, CardKey, MacInput, RandomChallenge, SessionKeys};
impl<B: Interface, const N: usize> Pn532<B, N> {
pub fn try_authenticate(&mut self, rng: &mut impl Rng, card: &CardSession, key: &CardKey)
-> Result<Option<AuthSession>, crate::Error<B::TransportError>>
{
let rc = self.set_random_challenge(card, rng)?;
let sk = self.get_session_keys(&rc, key)?;
let session = AuthSession::new(sk, rc);
let is_valid_auth = self.check_auth(card, &session)?;
if is_valid_auth {
Ok(Some(session))
} else {
Ok(None)
}
}
pub fn read_with_mac(
&mut self,
card: &CardSession,
session: &AuthSession,
block: impl AsBlock
) -> Result<[u8; 16], crate::Error<B::TransportError>> {
let mut block_data = [[0u8; 16]; 2];
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
self.felica_read_without_encryption(
card,
&[FelicaLiteSServiceCode::Read],
&block_list,
&mut block_data
)?;
let input = MacInput::new(&block_data, &block_list);
let mac = self.compute_mac(session, &input);
if mac != input.maca() {
return Err(crate::Error::FelicaError(Error::MACMismatch));
}
Ok(block_data[0])
}
fn set_random_challenge(&mut self, card: &CardSession, rng: &mut impl Rng)
-> Result<RandomChallenge, crate::Error<B::TransportError>>
{
let mut data = [[0u8; 16]];
rng.fill_bytes(&mut data[0]);
self.felica_write_without_encryption(
card,
&[FelicaLiteSServiceCode::Write],
&[FelicaLiteSBlock::RC],
&data,
)?;
let rc = RandomChallenge::from(data[0]);
Ok(rc)
}
fn get_session_keys(
&mut self,
rc: &RandomChallenge,
card_key: &CardKey,
) -> Result<SessionKeys, crate::Error<B::TransportError>> {
let des_key = card_key.as_des();
let key = Key::<TdesEde2>::try_from(des_key).unwrap();
let cipher = TdesEde2::new(&key);
Ok(SessionKeys::new(&cipher, &rc))
// crate::trace!("RC: {:02X}", rc);
// crate::trace!("CK DES key: {:02X}", des_key);
// crate::trace!("RC DES input: {:02X}", des_rc);
// crate::trace!("SK1 DES: {:02X}", sk1_des);
// crate::trace!("SK1: {:02X}", sk1);
// crate::trace!("SK2 DES: {:02X}", sk2_des);
// crate::trace!("SK2: {:02X}", sk2);
}
fn compute_mac<const U: usize>(
&mut self,
session: &AuthSession,
input: &MacInput<U>
) -> [u8; 8] {
// 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 = &input.block_list();
let block_data = &input.block_data();
// FeliCa Lite-S uses the session keys with their byte order reversed
// at the DES interface.
let key_bytes = session.sk.as_des();
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 = session.rc.rc1();
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);
// 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::trace!("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.0;
mac.reverse();
crate::trace!("mac: {:02X}, maca: {:02X}", mac, input.maca());
mac
}
fn check_auth(
&mut self,
card: &CardSession,
session: &AuthSession,
) -> Result<bool, crate::Error<B::TransportError>> {
let mut data = [[0u8; 16]; 3];
self.felica_read_without_encryption(
card,
&[FelicaLiteSServiceCode::Read],
&[FelicaLiteSBlock::ID, FelicaLiteSBlock::CKV, FelicaLiteSBlock::MACA],
&mut data
)?;
let block_list = &[FelicaLiteSBlock::ID, FelicaLiteSBlock::CKV, FelicaLiteSBlock::MACA];
let input = MacInput::new(
&data, block_list
);
let mac = self.compute_mac(session, &input);
Ok(mac == input.maca())
}
}
+6 -8
View File
@@ -126,8 +126,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Note: the number of blocks is limited by the 64-byte response buffer.
pub fn felica_read_without_encryption(
&mut self,
card: &IDm,
pmm: &PMm,
card: &CardSession,
service_code_list: &[impl AsServiceCode],
block_list: &[impl AsBlock],
block_data: &mut [[u8; 16]],
@@ -160,7 +159,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_bytes()).unwrap();
cmd.extend_from_slice(&card.idm.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();
@@ -180,7 +179,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let response_len = self.felica_send_command(
&cmd,
&mut response,
pmm.get_read_timeout(num_block, Some(Duration::from_millis(50)))
card.pmm.get_read_timeout(num_block, Some(Duration::from_millis(50)))
)?;
crate::trace!("Read Response: {:02X}", response);
if response_len != 12 + 16 * num_block {
@@ -201,8 +200,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Send a FeliCa "Write Without Encryption" command.
pub fn felica_write_without_encryption(
&mut self,
card: &IDm,
pmm: &PMm,
card: &CardSession,
service_code_list: &[impl AsServiceCode],
block_list: &[impl AsBlock],
block_data: &[[u8; 16]],
@@ -234,7 +232,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_bytes()).unwrap();
cmd.extend_from_slice(&card.idm.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();
@@ -254,7 +252,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let response_len = self.felica_send_command(
&cmd,
&mut response,
pmm.get_write_timeout(num_block, Some(Duration::from_millis(50)))
card.pmm.get_write_timeout(num_block, Some(Duration::from_millis(50)))
)?;
if response_len != RESPONSE_SIZE {
return Err(Error::InvalidFrame);
@@ -1,44 +0,0 @@
// 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,53 @@
use core::fmt::Display;
use core::ops::Deref;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CardKey(pub [u8; 16]);
impl CardKey {
/// FeliCa byte order:
///
/// CK1 = CK[0..8]
///
/// CK2 = CK[8..16]
///
/// The DES interface receives each half reversed.
pub fn as_des(&self) -> [u8; 16] {
let card_key = self.0;
let mut des_key = [0u8; 16];
for i in 0..8 {
des_key[7-i] = card_key[i];
des_key[15-i] = card_key[i + 8];
}
des_key
}
}
impl Deref for CardKey {
type Target = [u8; 16];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<[u8; 16]> for CardKey {
fn from(val: [u8; 16]) -> Self {
CardKey(val)
}
}
impl Display for CardKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
#[cfg(feature = "alloc")]
{
write!(f, "{}", hex::encode_upper(self.0))
}
#[cfg(not(feature = "alloc"))]
{
write!(f, "{:?}", self.0)
}
}
}
@@ -0,0 +1,57 @@
use crate::felica::types::AsBlock;
use crate::FELICA_READ_MAX_BLOCK_NUM;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MacInput<const N: usize> {
block_data: [[u8; 16]; N],
block_list: [u8; N]
}
impl<const N: usize> MacInput<N> {
pub fn new(block_data: &[[u8; 16]; N], blocks_list: &[impl AsBlock; N]) -> MacInput<N> {
if N > FELICA_READ_MAX_BLOCK_NUM {
panic!("too many blocks");
}
if N < 2 {
panic!("block should have at least 2 blocks");
}
let mut block_nums= [0u8; N];
for (i, block) in blocks_list.iter().enumerate(){
block_nums[i] = block.as_block() as u8;
}
Self {
block_data: *block_data,
block_list: block_nums
}
}
pub fn as_vec(&self) -> heapless::Vec<u8, 64> {
let mut data = heapless::Vec::new();
data.extend(self.block_list());
data.extend_from_slice(&self.block_data()).unwrap();
data
}
pub fn block_data(&self) -> heapless::Vec<u8, 64> {
let mut data = heapless::Vec::<u8, 64>::new();
for block in self.block_data.iter().take(N - 1) {
data.extend_from_slice(block).unwrap();
}
data
}
pub fn block_list(&self) -> [u8; 8] {
let mut data = [0xFFu8; 8];
for (i, block) in self.block_list.iter().enumerate() {
data[2 * i] = *block;
data[1 + 2 * i] = 0x00;
}
data
}
pub fn maca(&self) -> [u8; 8] {
self.block_data[N - 1][0..8].try_into().unwrap()
}
}
@@ -0,0 +1,23 @@
mod card_key;
mod rc;
mod sk;
mod mac_input;
pub use card_key::*;
pub use rc::*;
pub use sk::*;
pub use mac_input::*;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[readonly::make]
pub struct AuthSession {
pub sk: SessionKeys,
pub rc: RandomChallenge
}
impl AuthSession {
pub fn new(sk: SessionKeys, rc: RandomChallenge) -> Self {
AuthSession { sk, rc }
}
}
@@ -0,0 +1,58 @@
use core::fmt::Display;
use core::ops::Deref;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RandomChallenge([u8; 16]);
impl Deref for RandomChallenge {
type Target = [u8; 16];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<[u8; 16]> for RandomChallenge {
fn from(data: [u8; 16]) -> Self {
RandomChallenge(data)
}
}
impl RandomChallenge {
/// RC1 || RC2, with each 8-byte half reversed.
pub fn as_des(&self) -> [u8; 16] {
let rc = self.0;
let mut des = [0u8; 16];
for i in 0..8 {
des[7-i] = rc[i];
des[15-i] = rc[i + 8];
}
des
}
pub fn rc1(&self) -> [u8; 8] {
let rc = self.0;
rc[0..8].try_into().unwrap()
}
pub fn rc2(&self) -> [u8; 8] {
let rc = self.0;
rc[8..16].try_into().unwrap()
}
}
impl Display for RandomChallenge {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
#[cfg(feature = "alloc")]
{
write!(f, "{},{}", hex::encode_upper(self.rc1()), hex::encode_upper(self.rc2()))
}
#[cfg(not(feature = "alloc"))]
{
write!(f, "{:?}", self.0)
}
}
}
@@ -0,0 +1,68 @@
use cbc::cipher::{Block, BlockCipherEncrypt};
use des::TdesEde2;
use crate::felica::types::felica_lite_s::auth::RandomChallenge;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SessionKeys {
sk1: [u8; 8],
sk2: [u8; 8],
}
impl SessionKeys {
pub fn new(cipher: &TdesEde2, rc: &RandomChallenge) -> Self {
let des_rc = rc.as_des();
// 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();
Self { sk1, sk2 }
}
pub fn as_des(&self) -> [u8; 16] {
let mut key_bytes = [0u8; 16];
for i in 0..8 {
key_bytes[i] = self.sk1[7 - i];
key_bytes[8 + i] = self.sk2[7 - i];
}
key_bytes
}
pub fn as_bytes(&self) -> [u8; 16] {
let mut key_bytes = [0u8; 16];
key_bytes[0..8].copy_from_slice(&self.sk1);
key_bytes[8..16].copy_from_slice(&self.sk2);
key_bytes
}
pub fn sk1(&self) -> [u8; 8] {
self.sk1
}
pub fn sk2(&self) -> [u8; 8] {
self.sk2
}
}
@@ -1,9 +0,0 @@
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],
}
+2 -1
View File
@@ -1,4 +1,3 @@
pub mod block;
pub mod auth;
///! Types for Felica Lite-S (RC-S966)
@@ -10,6 +9,7 @@ use super::AsServiceCode;
/// Service Code
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitflag(u16)]
pub enum ServiceCode {
#[base]
@@ -32,6 +32,7 @@ impl AsServiceCode for ServiceCode {
/// FeliCa Lite system block numbers.
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitflag(u16)]
pub enum Block {
#[base]
+2
View File
@@ -2,6 +2,7 @@ use bitfields::bitflag;
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitflag(u8)]
pub enum CardICType {
#[base]
@@ -29,6 +30,7 @@ pub enum CardICType {
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MobileICType {
/// 0x14~0x1F
V3(u8),
+15
View File
@@ -1,8 +1,23 @@
use core::fmt::{Display, Formatter};
use bitfields::bitfield;
#[bitfield([u8; 8], from_endian = big, into_endian = big)]
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IDm {
pub manufacturer: [u8; 2],
pub card_id: [u8; 6]
}
impl Display for IDm {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
#[cfg(feature = "alloc")]
{
write!(f, "{}", hex::encode_upper(self.0))
}
#[cfg(not(feature = "alloc"))]
{
write!(f, "{:?}", self)
}
}
}
+31
View File
@@ -5,12 +5,14 @@ mod idm;
pub mod felica_lite_s;
pub mod ic_type;
use core::fmt::{Display, Formatter};
pub use idm::*;
pub use pmm::*;
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};
use crate::felica::types::polling::PollingResponse;
pub trait AsServiceCode {
fn as_service_code(&self) -> u16;
@@ -35,4 +37,33 @@ impl AsServiceCode for u16 {
fn from_service_code(val: u16) -> Self {
val
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[readonly::make]
pub struct CardSession {
pub idm: IDm,
pub pmm: PMm
}
impl CardSession {
pub fn new(idm: IDm, pmm: PMm) -> Self {
Self { idm, pmm }
}
}
impl From<PollingResponse> for CardSession {
fn from(value: PollingResponse) -> Self {
Self {
idm: value.idm,
pmm: value.pmm
}
}
}
impl Display for CardSession {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "IDm: {}, PMm: {}", self.idm, self.pmm)
}
}
+15
View File
@@ -1,8 +1,10 @@
use core::fmt::{Display, Formatter};
use core::time::Duration;
use bitfields::bitfield;
#[bitfield([u8; 8])]
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PMm {
pub rom_type: u8,
pub ic_type: u8,
@@ -37,4 +39,17 @@ impl PMm {
// Round up: a timeout must never underestimate the max response time.
Duration::from_micros(us as u64 + 1)
}
}
impl Display for PMm {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
#[cfg(feature = "alloc")]
{
write!(f, "{}", hex::encode_upper(self.0))
}
#[cfg(not(feature = "alloc"))]
{
write!(f, "{:?}", self)
}
}
}
+15
View File
@@ -1,7 +1,10 @@
use alloc::string::ToString;
use core::fmt::Display;
use bitfields::bitflag;
use super::{IDm, PMm};
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitflag(u8)]
pub enum PollingRequestCode {
NoRequest = 0x00,
@@ -11,6 +14,7 @@ pub enum PollingRequestCode {
}
#[derive(Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitflag(u8)]
pub enum PollingTimeSlot {
#[default]
@@ -24,6 +28,7 @@ pub enum PollingTimeSlot {
/// Result of a successful FeliCa polling request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PollingResponse {
/// The card's IDm (NFCID2).
pub idm: IDm,
@@ -31,4 +36,14 @@ pub struct PollingResponse {
pub pmm: PMm,
/// The card's system code, when returned.
pub system_code_response: Option<u16>,
}
impl Display for PollingResponse {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let system_code = match self.system_code_response {
Some(system_code) => hex::encode_upper(system_code.to_be_bytes().to_vec()),
None => "None".to_string()
};
write!(f, "IDm: {}, PMm, {}, System Code: {}", self.idm, self.pmm, system_code)
}
}
+2 -2
View File
@@ -214,11 +214,11 @@ where
frame[idx] = (!sum).wrapping_add(1);
frame[idx + 1] = PN532_POSTAMBLE;
crate::debug!("pn532: write cmd=0x{:02X} len={}", header[0], frame_len);
crate::trace!("pn532: write cmd=0x{:02X} len={}", header[0], frame_len);
self.i2c
.write(PN532_I2C_ADDRESS, &frame[..frame_len])
.map_err(Error::Transport)?;
crate::debug!("pn532: write ACKed, reading ACK frame");
crate::trace!("pn532: write ACKed, reading ACK frame");
self.read_ack_frame()
}