Finish pn532 crate
This commit is contained in:
Binary file not shown.
+25
-18
@@ -1,14 +1,14 @@
|
||||
//! High-level PN532 command driver.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use crate::commands::*;
|
||||
use crate::error::Error;
|
||||
use crate::interface::Interface;
|
||||
use crate::{NoIrq, NoReset, StatusCode};
|
||||
use crate::types::constants::{PN532_GPIO_P32, PN532_GPIO_P34, PN532_GPIO_VALIDATION_BIT};
|
||||
use crate::{StatusCode};
|
||||
use core::marker::PhantomData;
|
||||
use core::time::Duration;
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use embedded_hal::digital::{InputPin, OutputPin};
|
||||
use crate::types::constants::{PN532_GPIO_P32, PN532_GPIO_P34, PN532_GPIO_VALIDATION_BIT};
|
||||
|
||||
/// Driver for the NXP PN532 NFC controller.
|
||||
pub struct Pn532<RST, IRQ, DELAY, B, const N: usize = 64>
|
||||
@@ -16,7 +16,7 @@ where
|
||||
RST: OutputPin,
|
||||
IRQ: InputPin,
|
||||
DELAY: DelayNs,
|
||||
B: Interface<RST, IRQ, DELAY>
|
||||
B: Interface<RST, IRQ, DELAY>,
|
||||
{
|
||||
pub(crate) interface: B,
|
||||
pub(crate) in_listed_tag: u8,
|
||||
@@ -31,7 +31,7 @@ where
|
||||
RST: OutputPin,
|
||||
IRQ: InputPin,
|
||||
DELAY: DelayNs,
|
||||
B: Interface<RST, IRQ, DELAY>
|
||||
B: Interface<RST, IRQ, DELAY>,
|
||||
{
|
||||
/// Create a driver over the given transport interface.
|
||||
pub fn new(interface: B) -> Self {
|
||||
@@ -45,19 +45,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise the PN532: pulse the reset pin (if provided) and wait for it
|
||||
/// Initialize the PN532: pulse the reset pin (if provided) and wait for it
|
||||
/// to become ready.
|
||||
pub fn begin(&mut self) -> Result<(), Error<B::TransportError>> {
|
||||
if let Err(Error::UnusedPin) = self.interface.begin() {
|
||||
crate::debug!("No reset pin")
|
||||
match self.interface.begin() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::UnusedPin) => {
|
||||
crate::debug!("Unused pin");
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// -- internal helpers ---------------------------------------------------
|
||||
|
||||
pub(crate) fn send(&mut self, header_len: usize) -> Result<(), Error<B::TransportError>> {
|
||||
self.interface.write_command(&self.buffer[..header_len], &[])
|
||||
self.interface
|
||||
.write_command(&self.buffer[..header_len], &[])
|
||||
}
|
||||
|
||||
pub(crate) fn send_with_body(
|
||||
@@ -73,8 +78,12 @@ where
|
||||
self.interface.read_response(&mut self.buffer, 1000)
|
||||
}
|
||||
|
||||
pub(crate) fn read_timeout(&mut self, timeout: Duration) -> Result<usize, Error<B::TransportError>> {
|
||||
self.interface.read_response(&mut self.buffer, timeout.as_millis() as u16)
|
||||
pub(crate) fn read_timeout(
|
||||
&mut self,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, Error<B::TransportError>> {
|
||||
self.interface
|
||||
.read_response(&mut self.buffer, timeout.as_millis() as u16)
|
||||
}
|
||||
|
||||
// -- generic PN532 functions -------------------------------------------
|
||||
@@ -120,10 +129,10 @@ where
|
||||
}
|
||||
|
||||
/// Set the PN532's GPIO pins (see the PN532 user manual for valid pins).
|
||||
pub fn write_gpio(&mut self, pinstate: u8) -> Result<(), Error<B::TransportError>> {
|
||||
let pinstate = pinstate | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
|
||||
pub fn write_gpio(&mut self, pin_state: u8) -> Result<(), Error<B::TransportError>> {
|
||||
let pin_state = pin_state | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
|
||||
self.buffer[0] = PN532Command::WriteGpio.into_bits();
|
||||
self.buffer[1] = PN532_GPIO_VALIDATION_BIT | pinstate;
|
||||
self.buffer[1] = PN532_GPIO_VALIDATION_BIT | pin_state;
|
||||
self.buffer[2] = 0x00;
|
||||
self.send(3)?;
|
||||
let len = self.read()?;
|
||||
@@ -180,7 +189,7 @@ where
|
||||
self.read()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// return error if current buffer contains an error code
|
||||
pub(crate) fn validate_buffer(&self, len: usize) -> Result<(), Error<B::TransportError>> {
|
||||
if len == 0 || (self.buffer[0] & 0x3F) != 0 {
|
||||
@@ -190,8 +199,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Release the card.
|
||||
pub fn release(&mut self) -> Result<(), Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532Command::InRelease.into_bits();
|
||||
|
||||
@@ -118,11 +118,7 @@ where
|
||||
buf: &mut [u8],
|
||||
timeout_ms: u16,
|
||||
) -> Result<usize, Error<Self::TransportError>> {
|
||||
// Wait for the PN532 to signal data is ready.
|
||||
self.wait_ready(timeout_ms)?;
|
||||
|
||||
// Read the full response frame in a single transaction, stripping the
|
||||
// RDY byte. Frame layout: PREAMBLE(3) + LEN + LCS + TFI + CMD + payload + DCS + POSTAMBLE.
|
||||
let frame_len = buf.len() + 9;
|
||||
if frame_len > MAX_DATA_LEN + 10 {
|
||||
return Err(Error::NoSpace);
|
||||
@@ -148,18 +144,11 @@ where
|
||||
IRQ: InputPin,
|
||||
{
|
||||
/// Wait until the PN532 signals data is ready.
|
||||
///
|
||||
/// With an IRQ pin this blocks until the pin goes low; without one it polls
|
||||
/// the I2C status byte. Either way the status byte is consumed, so the next
|
||||
/// read returns the frame itself.
|
||||
fn wait_ready(&mut self, timeout_ms: u16) -> Result<(), Error<I2C::Error>> {
|
||||
let mut elapsed = 0u16;
|
||||
loop {
|
||||
match self.irq.is_low() {
|
||||
Ok(true) => {
|
||||
// IRQ asserted (active-low): data is ready. Poll the status
|
||||
// byte to confirm and consume it so the next read gets the
|
||||
// frame itself.
|
||||
let mut status = [0u8; 1];
|
||||
match self.i2c.read(PN532_I2C_ADDRESS, &mut status) {
|
||||
Ok(()) if status[0] & 1 == 1 => return Ok(()),
|
||||
@@ -173,8 +162,6 @@ where
|
||||
}
|
||||
}
|
||||
Ok(false) | Err(_) => {
|
||||
// IRQ connected but not asserted yet (or a read error):
|
||||
// keep waiting.
|
||||
self.delay.delay_ms(1);
|
||||
elapsed += 1;
|
||||
if timeout_ms != 0 && elapsed >= timeout_ms {
|
||||
|
||||
@@ -40,66 +40,39 @@ where
|
||||
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);
|
||||
}
|
||||
|
||||
pub(crate) fn compute_mac<const U: usize>(
|
||||
/// 2-key Triple DES-CBC MAC over `block_list` followed by `block_data`.
|
||||
pub(crate) fn compute_mac(
|
||||
&mut self,
|
||||
session: &AuthSession,
|
||||
input: &MacInput<U>
|
||||
key_bytes: [u8; 16],
|
||||
block_list: &[u8; 8],
|
||||
block_data: &[u8],
|
||||
) -> [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();
|
||||
|
||||
let mut block_list = block_list.clone();
|
||||
block_list.reverse();
|
||||
|
||||
for i in 0..8 {
|
||||
state_bytes[i] ^= first[i];
|
||||
state_bytes[i] ^= block_list[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.
|
||||
let mut state_block = Block::<TdesEde2>::try_from(state_bytes).unwrap();
|
||||
cipher.encrypt_block(&mut state_block);
|
||||
|
||||
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];
|
||||
input[i] ^= state_block[i];
|
||||
}
|
||||
|
||||
crate::trace!("MAC input: {:02X}", input);
|
||||
@@ -107,16 +80,26 @@ where
|
||||
let mut block = Block::<TdesEde2>::try_from(input).unwrap();
|
||||
cipher.encrypt_block(&mut block);
|
||||
|
||||
state = block;
|
||||
state_block = block;
|
||||
}
|
||||
|
||||
// Reverse the final result back to FeliCa byte order.
|
||||
let mut mac: [u8; 8] = state.0;
|
||||
let mut mac: [u8; 8] = state_block.0;
|
||||
mac.reverse();
|
||||
crate::trace!("mac: {:02X}, maca: {:02X}", mac, input.maca());
|
||||
crate::trace!("mac: {:02X}", mac);
|
||||
mac
|
||||
}
|
||||
|
||||
/// Compute the MAC_A value for a Write With MAC operation.
|
||||
pub(crate) fn compute_write_mac(
|
||||
&mut self,
|
||||
session: &AuthSession,
|
||||
plain: &[u8; 24],
|
||||
) -> [u8; 8] {
|
||||
let mut header = [0u8; 8];
|
||||
header.copy_from_slice(&plain[0..8]);
|
||||
self.compute_mac(session, session.sk.as_des_write(), &header, &plain[8..24])
|
||||
}
|
||||
|
||||
pub(crate) fn check_auth(
|
||||
&mut self,
|
||||
card: &CardSession,
|
||||
@@ -133,7 +116,12 @@ where
|
||||
let input = MacInput::new(
|
||||
&data, block_list
|
||||
);
|
||||
let mac = self.compute_mac(session, &input);
|
||||
let mac = self.compute_mac(
|
||||
session,
|
||||
session.sk.as_des(),
|
||||
&input.block_list(),
|
||||
&input.block_data(),
|
||||
);
|
||||
Ok(mac == input.maca())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use embedded_hal::digital::{InputPin, OutputPin};
|
||||
use crate::{Interface, Pn532};
|
||||
use crate::{Error, Interface, Pn532};
|
||||
use crate::types::felica::{CardSession, FelicaLiteSBlock};
|
||||
use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, CardKeyVersion};
|
||||
use crate::types::felica::felica_lite_s::config::MemoryConfig;
|
||||
|
||||
impl<RST, IRQ, DELAY, B, const N: usize> Pn532<RST, IRQ, DELAY, B, N>
|
||||
where
|
||||
@@ -9,7 +12,92 @@ where
|
||||
DELAY: DelayNs,
|
||||
B: Interface<RST, IRQ, DELAY>
|
||||
{
|
||||
pub fn setup_first_issuance(&mut self) {
|
||||
todo!()
|
||||
pub fn set_id(&mut self, card: &CardSession, arbitrary: [u8; 6], dfc: Option<[u8; 2]>) -> Result<(), Error<B::TransportError>> {
|
||||
let dfc = dfc.unwrap_or_default();
|
||||
let mut block = [0u8; 16];
|
||||
block[8] = dfc[0];
|
||||
block[9] = dfc[1];
|
||||
block[10..16].copy_from_slice(&arbitrary);
|
||||
self.write_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::ID],
|
||||
&[block]
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_id(&mut self, card: &CardSession) -> Result<[u8; 16], Error<B::TransportError>> {
|
||||
let mut block = [[0u8; 16]];
|
||||
self.read_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::ID],
|
||||
&mut block
|
||||
)?;
|
||||
crate::trace!("ID block: {:02X}", block);
|
||||
Ok(block[0])
|
||||
}
|
||||
|
||||
pub fn set_ck(&mut self, card: &CardSession, key: &CardKey) -> Result<(), Error<B::TransportError>> {
|
||||
self.write_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::CK],
|
||||
&[**key]
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_ck_with_mac(&mut self, card: &CardSession, session: &AuthSession, key: &CardKey) -> Result<(), Error<B::TransportError>> {
|
||||
self.write_with_mac(
|
||||
card,
|
||||
session,
|
||||
FelicaLiteSBlock::CK,
|
||||
&**key
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_ckv(&mut self, card: &CardSession, version: &CardKeyVersion) -> Result<(), Error<B::TransportError>> {
|
||||
self.write_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::CKV],
|
||||
&[version.as_block()]
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_ckv_with_mac(&mut self, card: &CardSession, session: &AuthSession, version: &CardKeyVersion) -> Result<(), Error<B::TransportError>> {
|
||||
self.write_with_mac(
|
||||
card,
|
||||
session,
|
||||
FelicaLiteSBlock::CKV,
|
||||
&version.as_block()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_ckv(&mut self, card: &CardSession) -> Result<CardKeyVersion, Error<B::TransportError>> {
|
||||
let mut block = [[0u8; 16]];
|
||||
self.read_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::CKV],
|
||||
&mut block
|
||||
)?;
|
||||
crate::trace!("CKV block: {:02X}", block);
|
||||
Ok(CardKeyVersion::from(block[0]))
|
||||
}
|
||||
|
||||
pub fn get_mc(&mut self, card: &CardSession) -> Result<MemoryConfig, Error<B::TransportError>> {
|
||||
let mut block = [[0u8; 16]];
|
||||
self.read_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::MemoryConfig],
|
||||
&mut block
|
||||
)?;
|
||||
crate::trace!("MC block: {:02X}", block[0]);
|
||||
Ok(MemoryConfig::from(block[0]))
|
||||
}
|
||||
|
||||
pub fn set_mc(&mut self, card: &CardSession, config: MemoryConfig) -> Result<(), Error<B::TransportError>> {
|
||||
let block: [u8; 16] = config.into();
|
||||
self.write_block(
|
||||
card,
|
||||
&[FelicaLiteSBlock::MemoryConfig],
|
||||
&[block]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,12 @@ where
|
||||
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)
|
||||
|
||||
if !is_valid_auth {
|
||||
return Ok(None);
|
||||
}
|
||||
self.try_external_authentication(card, &session)?;
|
||||
Ok(Some(session))
|
||||
}
|
||||
|
||||
pub fn read_with_mac(
|
||||
@@ -40,7 +41,12 @@ where
|
||||
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
|
||||
self.read_block(card, &block_list, &mut block_data)?;
|
||||
let input = MacInput::new(&block_data, &block_list);
|
||||
let mac = self.compute_mac(session, &input);
|
||||
let mac = self.compute_mac(
|
||||
session,
|
||||
session.sk.as_des(),
|
||||
&input.block_list(),
|
||||
&input.block_data(),
|
||||
);
|
||||
if mac != input.maca() {
|
||||
return Err(crate::Error::FelicaError(Error::MACMismatch));
|
||||
}
|
||||
@@ -48,6 +54,47 @@ where
|
||||
Ok(block_data[0])
|
||||
}
|
||||
|
||||
/// Write one data block protected by a Write With MAC (manual §5.4.4).
|
||||
pub fn write_with_mac(
|
||||
&mut self,
|
||||
card: &CardSession,
|
||||
session: &AuthSession,
|
||||
block: impl AsBlock,
|
||||
data: &[u8; 16],
|
||||
) -> Result<(), crate::Error<B::TransportError>> {
|
||||
let mut wcnt_block = [[0u8; 16]];
|
||||
self.read_block(card, &[FelicaLiteSBlock::WCNT], &mut wcnt_block)?;
|
||||
let mut wcnt = [0u8; 3];
|
||||
wcnt.copy_from_slice(&wcnt_block[0][0..3]);
|
||||
|
||||
let mut plain = [0u8; 24];
|
||||
plain[0..3].copy_from_slice(&wcnt);
|
||||
plain[4] = block.as_block() as u8;
|
||||
plain[6] = FelicaLiteSBlock::MACA.as_block() as u8;
|
||||
plain[8..24].copy_from_slice(data);
|
||||
|
||||
let mac = self.compute_write_mac(session, &plain);
|
||||
|
||||
let mut mac_a = [0u8; 16];
|
||||
mac_a[0..8].copy_from_slice(&mac);
|
||||
mac_a[8..11].copy_from_slice(&wcnt);
|
||||
|
||||
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
|
||||
let block_data = [*data, mac_a];
|
||||
self.write_block(card, &block_list, &block_data)
|
||||
}
|
||||
|
||||
/// Complete External Authentication (manual §5.4.2, Figure 5-10).
|
||||
pub(crate) fn try_external_authentication(
|
||||
&mut self,
|
||||
card: &CardSession,
|
||||
session: &AuthSession,
|
||||
) -> Result<(), crate::Error<B::TransportError>> {
|
||||
let mut state = [0u8; 16];
|
||||
state[0] = 0x01; // EXT_AUTH: after authentication
|
||||
self.write_with_mac(card, session, FelicaLiteSBlock::STATE, &state)
|
||||
}
|
||||
|
||||
pub fn read_block<const U: usize>(
|
||||
&mut self,
|
||||
card: &CardSession,
|
||||
|
||||
@@ -184,12 +184,13 @@ where
|
||||
card.pmm.get_read_timeout(U, Some(Duration::from_millis(50)))
|
||||
)?;
|
||||
crate::trace!("Read Response: {:02X}", response);
|
||||
|
||||
if response_len >= 11 && (response[9] != 0 || response[10] != 0) {
|
||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||
}
|
||||
if response_len != 12 + 16 * U {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response[9] != 0 || response[10] != 0 {
|
||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||
}
|
||||
|
||||
let mut k = 12;
|
||||
for block in block_data.iter_mut().take(U) {
|
||||
@@ -253,12 +254,13 @@ where
|
||||
&mut response,
|
||||
card.pmm.get_write_timeout(U, Some(Duration::from_millis(50)))
|
||||
)?;
|
||||
|
||||
if response_len >= 11 && (response[9] != 0 || response[10] != 0) {
|
||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||
}
|
||||
if response_len != RESPONSE_SIZE {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
if response[9] != 0 || response[10] != 0 {
|
||||
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use core::ops::Deref;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct CardKey(pub [u8; 16]);
|
||||
pub struct CardKey([u8; 16]);
|
||||
|
||||
impl CardKey {
|
||||
/// FeliCa byte order:
|
||||
@@ -50,4 +50,41 @@ impl Display for CardKey {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct CardKeyVersion([u8; 2]);
|
||||
|
||||
impl CardKeyVersion {
|
||||
pub fn new(version: u16) -> Self {
|
||||
let bytes = version.to_be_bytes();
|
||||
CardKeyVersion(bytes)
|
||||
}
|
||||
|
||||
pub fn as_block(&self) -> [u8; 16] {
|
||||
let mut block = [0u8; 16];
|
||||
block[0] = self.0[0];
|
||||
block[1] = self.0[1];
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 16]> for CardKeyVersion {
|
||||
fn from(val: [u8; 16]) -> Self {
|
||||
CardKeyVersion([val[0], val[1]])
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CardKeyVersion {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,13 +41,23 @@ impl SessionKeys {
|
||||
}
|
||||
|
||||
pub fn as_des(&self) -> [u8; 16] {
|
||||
Self::key_des(&self.sk1, &self.sk2)
|
||||
}
|
||||
|
||||
/// Session keys in DES byte order with the halves swapped (`SK2 || SK1`),
|
||||
/// as required by the data-write MAC generation.
|
||||
pub fn as_des_write(&self) -> [u8; 16] {
|
||||
Self::key_des(&self.sk2, &self.sk1)
|
||||
}
|
||||
|
||||
fn key_des(first: &[u8; 8], second: &[u8; 8]) -> [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[i] = first[7 - i];
|
||||
key_bytes[8 + i] = second[7 - i];
|
||||
}
|
||||
|
||||
|
||||
key_bytes
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use bitfields::bitflag;
|
||||
|
||||
#[bitflag(u8)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AccessLevel {
|
||||
ReadOnly = 0,
|
||||
#[base]
|
||||
ReadWrite = 1,
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
mod access_level;
|
||||
use access_level::AccessLevel;
|
||||
|
||||
use bitfields::bitfield;
|
||||
|
||||
#[bitfield([u8; 4], from_endian = little, into_endian = little)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Access {
|
||||
#[bits(16)]
|
||||
pub scratch_pad: ScratchPadAccessLevel,
|
||||
/// 0xFF: ReadWrite
|
||||
/// Other than 0xFF: Readonly
|
||||
#[bits(8, default = 0)]
|
||||
pub system_block: u8,
|
||||
#[bits(1)]
|
||||
pub is_ck_ckv_rewritable: bool,
|
||||
#[bits(7)]
|
||||
_reserved: u8,
|
||||
}
|
||||
|
||||
#[bitfield([u8; 7], from_endian = little, into_endian = little)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Authentication {
|
||||
#[bits(16)]
|
||||
pub scratch_pad_read: ScratchPadAuth,
|
||||
#[bits(16)]
|
||||
pub scratch_pad_write: ScratchPadAuth,
|
||||
#[bits(16)]
|
||||
pub scratch_pad_mac_write: ScratchPadAuth,
|
||||
#[bits(1, default = false)]
|
||||
pub state_with_mac: bool,
|
||||
#[bits(7, default = 0)]
|
||||
_reserved_state: u8,
|
||||
}
|
||||
|
||||
#[bitfield(u16)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ScratchPadAccessLevel {
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad0: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad1: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad2: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad3: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad4: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad5: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad6: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad7: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad8: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad9: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad10: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad11: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad12: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub pad13: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub reg: AccessLevel,
|
||||
#[bits(1, default = AccessLevel::ReadWrite)]
|
||||
pub mc: AccessLevel,
|
||||
}
|
||||
|
||||
#[bitfield(u16)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ScratchPadAuth {
|
||||
#[bits(1, default = false)]
|
||||
pub pad0: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad1: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad2: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad3: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad4: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad5: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad6: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad7: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad8: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad9: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad10: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad11: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad12: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub pad13: bool,
|
||||
#[bits(1, default = false)]
|
||||
pub reg: bool,
|
||||
#[bits(1, default = false)]
|
||||
_reserved: bool,
|
||||
}
|
||||
|
||||
#[bitfield(u8)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct RFParameter {
|
||||
#[bits(3, default = 0x07)]
|
||||
pub parameter: u8,
|
||||
#[bits(5, default = 0x1F)]
|
||||
_reserved: u8,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct MemoryConfig {
|
||||
pub access: Access,
|
||||
pub rf: RFParameter,
|
||||
pub auth: Authentication,
|
||||
/// 0x01: compatible
|
||||
/// 0x00: incompatible
|
||||
/// 0x02~0xFF: prohibited
|
||||
pub ndef: u8,
|
||||
_reserved: [u8; 3],
|
||||
}
|
||||
|
||||
impl Default for MemoryConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
access: Access::from([0xFF, 0xFF, 0xFF, 0x00]),
|
||||
rf: RFParameter::default(),
|
||||
ndef: 0x00,
|
||||
auth: Authentication::default(),
|
||||
_reserved: [0u8; 3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<[u8; 16]> for MemoryConfig {
|
||||
fn into(self) -> [u8; 16] {
|
||||
let mut block = [0u8; 16];
|
||||
|
||||
let access = self.access.into_bytes();
|
||||
|
||||
block[0] = access[0];
|
||||
block[1] = access[1];
|
||||
block[2] = access[2];
|
||||
block[3] = self.ndef;
|
||||
block[4] = self.rf.into_bits();
|
||||
block[5] = access[3];
|
||||
|
||||
block[6..13].copy_from_slice(&self.auth.into_bytes());
|
||||
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 16]> for MemoryConfig {
|
||||
fn from(value: [u8; 16]) -> Self {
|
||||
let access = Access::from([value[0], value[1], value[2], value[5]]);
|
||||
|
||||
let rf = RFParameter::from(value[4]);
|
||||
|
||||
let auth: [u8; 7] = value[6..13].try_into().unwrap();
|
||||
let auth = Authentication::from(auth);
|
||||
|
||||
Self {
|
||||
access,
|
||||
rf,
|
||||
auth,
|
||||
ndef: value[3],
|
||||
_reserved: value[13..16].try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@
|
||||
///! User Manual: https://www.sony.net/Products/felica/business/tech-support/data/fls_usmnl_1.4e.pdf
|
||||
|
||||
pub mod auth;
|
||||
pub mod config;
|
||||
|
||||
use bitfields::bitflag;
|
||||
use super::{AsBlock, IDm, PMm};
|
||||
use super::AsBlock;
|
||||
use super::AsServiceCode;
|
||||
|
||||
/// Service Code
|
||||
@@ -68,7 +69,9 @@ pub enum Block {
|
||||
/// Write counter block.
|
||||
WCNT = 0x8090,
|
||||
/// MAC_A block.
|
||||
MACA = 0x8091
|
||||
MACA = 0x8091,
|
||||
/// STATE block (EXT_AUTH / POLL_DIS).
|
||||
STATE = 0x8092
|
||||
}
|
||||
|
||||
impl AsBlock for Block {
|
||||
|
||||
@@ -7,25 +7,8 @@ use bitfields::bitflag;
|
||||
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
|
||||
S966 = 0xF1,
|
||||
S965 = 0xF0,
|
||||
}
|
||||
|
||||
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::fmt::{Display, Formatter};
|
||||
use bitfields::bitfield;
|
||||
|
||||
#[bitfield([u8; 8], from_endian = big, into_endian = big)]
|
||||
#[bitfield([u8; 8], from_endian = little, into_endian = little)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct IDm {
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::fmt::{Display, Formatter};
|
||||
use core::time::Duration;
|
||||
use bitfields::bitfield;
|
||||
|
||||
#[bitfield([u8; 8])]
|
||||
#[bitfield([u8; 8], from_endian = little, into_endian = little)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PMm {
|
||||
|
||||
Reference in New Issue
Block a user