Finish pn532 crate

This commit is contained in:
2026-09-13 20:51:26 +08:00
parent 89dfed231f
commit 70194f98c1
23 changed files with 610 additions and 204 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ runner = "espflash flash --monitor -B 921600 --chip esp32s3 --log-format defmt"
linker = "/home/fromost/.rustup/toolchains/esp/xtensa-esp-elf/esp-15.2.0_20250920/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc" linker = "/home/fromost/.rustup/toolchains/esp/xtensa-esp-elf/esp-15.2.0_20250920/xtensa-esp-elf/bin/xtensa-esp32s3-elf-gcc"
[env] [env]
DEFMT_LOG = "info,lora=trace,eeprom24x=debug,pn532=trace" DEFMT_LOG = "info,lora=trace,eeprom24x=debug,pn532=debug"
[build] [build]
rustflags = [ rustflags = [
+1
View File
@@ -0,0 +1 @@
*.pdf filter=lfs, diff=lfs merge=lfs -text
+7
View File
@@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ComposeUnknownValues" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>
+6 -6
View File
@@ -15,21 +15,21 @@ test = false
debug = [] debug = []
[dependencies] [dependencies]
esp-hal = { version = "~1.1", features = ["defmt", "esp32s3", "unstable"] } esp-hal = { version = "~1.2", features = ["defmt", "esp32s3", "unstable"] }
esp-rtos = { version = "0.3.0", features = [ esp-rtos = { version = "0.4.0", features = [
"defmt", "defmt",
"embassy", "embassy",
"esp-alloc", "esp-alloc",
"esp32s3", "esp32s3",
] } ] }
esp-alloc = { version = "0.10.0", features = ["defmt"] } esp-alloc = { version = "0.11.0", features = ["defmt"] }
esp-backtrace = { version = "0.19.0", features = [ esp-backtrace = { version = "0.20.0", features = [
"defmt", "defmt",
"esp32s3", "esp32s3",
"panic-handler", "panic-handler",
] } ] }
esp-println = { version = "0.17.0", features = ["defmt-espflash", "esp32s3"] } esp-println = { version = "0.18.0", features = ["defmt-espflash", "esp32s3"] }
esp-bootloader-esp-idf = { version = "0.5.0", features = ["defmt", "esp32s3"] } esp-bootloader-esp-idf = { version = "0.6.0", features = ["defmt", "esp32s3"] }
embassy-executor = { version = "0.10.0", features = ["defmt"] } embassy-executor = { version = "0.10.0", features = ["defmt"] }
embassy-time = { version = "0.5.0", features = ["defmt", "generic-queue-8", "defmt-timestamp-uptime"] } embassy-time = { version = "0.5.0", features = ["defmt", "generic-queue-8", "defmt-timestamp-uptime"] }
+2 -2
View File
@@ -2,10 +2,10 @@ fn main() {
println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=Cargo.toml");
patch_crate::run().expect("Failed while patching"); patch_crate::run().expect("Failed while patching");
let iter = dotenvy::from_path_iter(".env") let environment = dotenvy::from_path_iter(".env")
.expect("failed to read .env"); .expect("failed to read .env");
for item in iter { for item in environment {
let (key, value) = item.expect("invalid .env entry"); let (key, value) = item.expect("invalid .env entry");
println!("cargo::rustc-env={key}={value}"); println!("cargo::rustc-env={key}={value}");
Binary file not shown.
+24 -17
View File
@@ -1,14 +1,14 @@
//! High-level PN532 command driver. //! High-level PN532 command driver.
use core::marker::PhantomData;
use crate::commands::*; use crate::commands::*;
use crate::error::Error; use crate::error::Error;
use crate::interface::Interface; 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 core::time::Duration;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{InputPin, OutputPin}; 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. /// Driver for the NXP PN532 NFC controller.
pub struct Pn532<RST, IRQ, DELAY, B, const N: usize = 64> pub struct Pn532<RST, IRQ, DELAY, B, const N: usize = 64>
@@ -16,7 +16,7 @@ where
RST: OutputPin, RST: OutputPin,
IRQ: InputPin, IRQ: InputPin,
DELAY: DelayNs, DELAY: DelayNs,
B: Interface<RST, IRQ, DELAY> B: Interface<RST, IRQ, DELAY>,
{ {
pub(crate) interface: B, pub(crate) interface: B,
pub(crate) in_listed_tag: u8, pub(crate) in_listed_tag: u8,
@@ -31,7 +31,7 @@ where
RST: OutputPin, RST: OutputPin,
IRQ: InputPin, IRQ: InputPin,
DELAY: DelayNs, DELAY: DelayNs,
B: Interface<RST, IRQ, DELAY> B: Interface<RST, IRQ, DELAY>,
{ {
/// Create a driver over the given transport interface. /// Create a driver over the given transport interface.
pub fn new(interface: B) -> Self { 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. /// to become ready.
pub fn begin(&mut self) -> Result<(), Error<B::TransportError>> { pub fn begin(&mut self) -> Result<(), Error<B::TransportError>> {
if let Err(Error::UnusedPin) = self.interface.begin() { match self.interface.begin() {
crate::debug!("No reset pin") Ok(_) => Ok(()),
} Err(Error::UnusedPin) => {
crate::debug!("Unused pin");
Ok(()) Ok(())
},
Err(e) => Err(e),
}
} }
// -- internal helpers --------------------------------------------------- // -- internal helpers ---------------------------------------------------
pub(crate) fn send(&mut self, header_len: usize) -> Result<(), Error<B::TransportError>> { 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( pub(crate) fn send_with_body(
@@ -73,8 +78,12 @@ where
self.interface.read_response(&mut self.buffer, 1000) self.interface.read_response(&mut self.buffer, 1000)
} }
pub(crate) fn read_timeout(&mut self, timeout: Duration) -> Result<usize, Error<B::TransportError>> { pub(crate) fn read_timeout(
self.interface.read_response(&mut self.buffer, timeout.as_millis() as u16) &mut self,
timeout: Duration,
) -> Result<usize, Error<B::TransportError>> {
self.interface
.read_response(&mut self.buffer, timeout.as_millis() as u16)
} }
// -- generic PN532 functions ------------------------------------------- // -- generic PN532 functions -------------------------------------------
@@ -120,10 +129,10 @@ where
} }
/// Set the PN532's GPIO pins (see the PN532 user manual for valid pins). /// 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>> { pub fn write_gpio(&mut self, pin_state: u8) -> Result<(), Error<B::TransportError>> {
let pinstate = pinstate | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34); let pin_state = pin_state | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
self.buffer[0] = PN532Command::WriteGpio.into_bits(); 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.buffer[2] = 0x00;
self.send(3)?; self.send(3)?;
let len = self.read()?; let len = self.read()?;
@@ -190,8 +199,6 @@ where
} }
} }
/// Release the card. /// Release the card.
pub fn release(&mut self) -> Result<(), Error<B::TransportError>> { pub fn release(&mut self) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532Command::InRelease.into_bits(); self.buffer[0] = PN532Command::InRelease.into_bits();
-13
View File
@@ -118,11 +118,7 @@ where
buf: &mut [u8], buf: &mut [u8],
timeout_ms: u16, timeout_ms: u16,
) -> Result<usize, Error<Self::TransportError>> { ) -> Result<usize, Error<Self::TransportError>> {
// Wait for the PN532 to signal data is ready.
self.wait_ready(timeout_ms)?; 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; let frame_len = buf.len() + 9;
if frame_len > MAX_DATA_LEN + 10 { if frame_len > MAX_DATA_LEN + 10 {
return Err(Error::NoSpace); return Err(Error::NoSpace);
@@ -148,18 +144,11 @@ where
IRQ: InputPin, IRQ: InputPin,
{ {
/// Wait until the PN532 signals data is ready. /// 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>> { fn wait_ready(&mut self, timeout_ms: u16) -> Result<(), Error<I2C::Error>> {
let mut elapsed = 0u16; let mut elapsed = 0u16;
loop { loop {
match self.irq.is_low() { match self.irq.is_low() {
Ok(true) => { 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]; let mut status = [0u8; 1];
match self.i2c.read(PN532_I2C_ADDRESS, &mut status) { match self.i2c.read(PN532_I2C_ADDRESS, &mut status) {
Ok(()) if status[0] & 1 == 1 => return Ok(()), Ok(()) if status[0] & 1 == 1 => return Ok(()),
@@ -173,8 +162,6 @@ where
} }
} }
Ok(false) | Err(_) => { Ok(false) | Err(_) => {
// IRQ connected but not asserted yet (or a read error):
// keep waiting.
self.delay.delay_ms(1); self.delay.delay_ms(1);
elapsed += 1; elapsed += 1;
if timeout_ms != 0 && elapsed >= timeout_ms { if timeout_ms != 0 && elapsed >= timeout_ms {
+31 -43
View File
@@ -40,66 +40,39 @@ where
let cipher = TdesEde2::new(&key); let cipher = TdesEde2::new(&key);
Ok(SessionKeys::new(&cipher, &rc)) 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, &mut self,
session: &AuthSession, session: &AuthSession,
input: &MacInput<U> key_bytes: [u8; 16],
block_list: &[u8; 8],
block_data: &[u8],
) -> [u8; 8] { ) -> [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 key = Key::<TdesEde2>::try_from(key_bytes).unwrap();
let cipher = TdesEde2::new(&key); let cipher = TdesEde2::new(&key);
// RC1 is also byte-reversed at the DES interface.
let mut state_bytes = session.rc.rc1(); let mut state_bytes = session.rc.rc1();
state_bytes.reverse(); state_bytes.reverse();
// The initial plaintext for MAC_A is the block-number list. let mut block_list = block_list.clone();
// Byte order is reversed before the DES operation. block_list.reverse();
let mut first = [0u8; 8];
first.copy_from_slice(block_list);
first.reverse();
for i in 0..8 { 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(); let mut state_block = Block::<TdesEde2>::try_from(state_bytes).unwrap();
cipher.encrypt_block(&mut state); cipher.encrypt_block(&mut state_block);
// 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) { for chunk in block_data.chunks_exact(8) {
let mut input = [0u8; 8]; let mut input = [0u8; 8];
input.copy_from_slice(chunk); input.copy_from_slice(chunk);
input.reverse(); input.reverse();
for i in 0..8 { for i in 0..8 {
input[i] ^= state[i]; input[i] ^= state_block[i];
} }
crate::trace!("MAC input: {:02X}", input); crate::trace!("MAC input: {:02X}", input);
@@ -107,16 +80,26 @@ where
let mut block = Block::<TdesEde2>::try_from(input).unwrap(); let mut block = Block::<TdesEde2>::try_from(input).unwrap();
cipher.encrypt_block(&mut block); 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_block.0;
let mut mac: [u8; 8] = state.0;
mac.reverse(); mac.reverse();
crate::trace!("mac: {:02X}, maca: {:02X}", mac, input.maca()); crate::trace!("mac: {:02X}", mac);
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( pub(crate) fn check_auth(
&mut self, &mut self,
card: &CardSession, card: &CardSession,
@@ -133,7 +116,12 @@ where
let input = MacInput::new( let input = MacInput::new(
&data, block_list &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()) Ok(mac == input.maca())
} }
} }
@@ -1,6 +1,9 @@
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{InputPin, OutputPin}; 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> impl<RST, IRQ, DELAY, B, const N: usize> Pn532<RST, IRQ, DELAY, B, N>
where where
@@ -9,7 +12,92 @@ where
DELAY: DelayNs, DELAY: DelayNs,
B: Interface<RST, IRQ, DELAY> B: Interface<RST, IRQ, DELAY>
{ {
pub fn setup_first_issuance(&mut self) { pub fn set_id(&mut self, card: &CardSession, arbitrary: [u8; 6], dfc: Option<[u8; 2]>) -> Result<(), Error<B::TransportError>> {
todo!() 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]
)
} }
} }
+52 -5
View File
@@ -23,11 +23,12 @@ where
let sk = self.get_session_keys(&rc, key)?; let sk = self.get_session_keys(&rc, key)?;
let session = AuthSession::new(sk, rc); let session = AuthSession::new(sk, rc);
let is_valid_auth = self.check_auth(card, &session)?; let is_valid_auth = self.check_auth(card, &session)?;
if is_valid_auth {
Ok(Some(session)) if !is_valid_auth {
} else { return Ok(None);
Ok(None)
} }
self.try_external_authentication(card, &session)?;
Ok(Some(session))
} }
pub fn read_with_mac( pub fn read_with_mac(
@@ -40,7 +41,12 @@ where
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16]; let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
self.read_block(card, &block_list, &mut block_data)?; self.read_block(card, &block_list, &mut block_data)?;
let input = MacInput::new(&block_data, &block_list); 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() { if mac != input.maca() {
return Err(crate::Error::FelicaError(Error::MACMismatch)); return Err(crate::Error::FelicaError(Error::MACMismatch));
} }
@@ -48,6 +54,47 @@ where
Ok(block_data[0]) 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>( pub fn read_block<const U: usize>(
&mut self, &mut self,
card: &CardSession, card: &CardSession,
+8 -6
View File
@@ -184,12 +184,13 @@ where
card.pmm.get_read_timeout(U, Some(Duration::from_millis(50))) card.pmm.get_read_timeout(U, Some(Duration::from_millis(50)))
)?; )?;
crate::trace!("Read Response: {:02X}", response); 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 { if response_len != 12 + 16 * U {
return Err(Error::InvalidFrame); 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; let mut k = 12;
for block in block_data.iter_mut().take(U) { for block in block_data.iter_mut().take(U) {
@@ -253,12 +254,13 @@ where
&mut response, &mut response,
card.pmm.get_write_timeout(U, Some(Duration::from_millis(50))) 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 { if response_len != RESPONSE_SIZE {
return Err(Error::InvalidFrame); return Err(Error::InvalidFrame);
} }
if response[9] != 0 || response[10] != 0 {
return Err(Error::FelicaError(FelicaError::Status(response[9], response[10])));
}
Ok(()) Ok(())
} }
} }
@@ -3,7 +3,7 @@ use core::ops::Deref;
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CardKey(pub [u8; 16]); pub struct CardKey([u8; 16]);
impl CardKey { impl CardKey {
/// FeliCa byte order: /// FeliCa byte order:
@@ -51,3 +51,40 @@ impl Display for CardKey {
} }
} }
} }
#[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,11 +41,21 @@ impl SessionKeys {
} }
pub fn as_des(&self) -> [u8; 16] { 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]; let mut key_bytes = [0u8; 16];
for i in 0..8 { for i in 0..8 {
key_bytes[i] = self.sk1[7 - i]; key_bytes[i] = first[7 - i];
key_bytes[8 + i] = self.sk2[7 - i]; key_bytes[8 + i] = second[7 - i];
} }
key_bytes 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(),
}
}
}
+5 -2
View File
@@ -2,9 +2,10 @@
///! User Manual: https://www.sony.net/Products/felica/business/tech-support/data/fls_usmnl_1.4e.pdf ///! User Manual: https://www.sony.net/Products/felica/business/tech-support/data/fls_usmnl_1.4e.pdf
pub mod auth; pub mod auth;
pub mod config;
use bitfields::bitflag; use bitfields::bitflag;
use super::{AsBlock, IDm, PMm}; use super::AsBlock;
use super::AsServiceCode; use super::AsServiceCode;
/// Service Code /// Service Code
@@ -68,7 +69,9 @@ pub enum Block {
/// Write counter block. /// Write counter block.
WCNT = 0x8090, WCNT = 0x8090,
/// MAC_A block. /// MAC_A block.
MACA = 0x8091 MACA = 0x8091,
/// STATE block (EXT_AUTH / POLL_DIS).
STATE = 0x8092
} }
impl AsBlock for Block { impl AsBlock for Block {
+2 -19
View File
@@ -7,25 +7,8 @@ use bitfields::bitflag;
pub enum CardICType { pub enum CardICType {
#[base] #[base]
Unknown = 0x00, Unknown = 0x00,
S140 = 0x50, S966 = 0xF1,
SA40_2P = 0x51, S965 = 0xF0,
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 /// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
+1 -1
View File
@@ -1,7 +1,7 @@
use core::fmt::{Display, Formatter}; use core::fmt::{Display, Formatter};
use bitfields::bitfield; use bitfields::bitfield;
#[bitfield([u8; 8], from_endian = big, into_endian = big)] #[bitfield([u8; 8], from_endian = little, into_endian = little)]
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IDm { pub struct IDm {
+1 -1
View File
@@ -2,7 +2,7 @@ use core::fmt::{Display, Formatter};
use core::time::Duration; use core::time::Duration;
use bitfields::bitfield; use bitfields::bitfield;
#[bitfield([u8; 8])] #[bitfield([u8; 8], from_endian = little, into_endian = little)]
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PMm { pub struct PMm {
+34 -35
View File
@@ -13,7 +13,6 @@ use embassy_sync::pubsub::PubSubChannel;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use esp_hal::gpio::{InputConfig, Level, Pull}; use esp_hal::gpio::{InputConfig, Level, Pull};
use esp_hal::i2c::master::I2c; use esp_hal::i2c::master::I2c;
use esp_hal::interrupt::software::SoftwareInterruptControl;
use esp_hal::peripherals::Peripherals; use esp_hal::peripherals::Peripherals;
use esp_hal::rng::{Trng, TrngSource}; use esp_hal::rng::{Trng, TrngSource};
use esp_hal::spi::master::{Config, Spi}; use esp_hal::spi::master::{Config, Spi};
@@ -22,6 +21,7 @@ use esp_hal::{gpio, Async, Blocking};
use static_cell::StaticCell; use static_cell::StaticCell;
pub(crate) static LED_STATE: AtomicBool = AtomicBool::new(false); pub(crate) static LED_STATE: AtomicBool = AtomicBool::new(false);
static TRNG_SOURCE: StaticCell<TrngSource> = StaticCell::new();
pub struct Board { pub struct Board {
spawner: Spawner, spawner: Spawner,
@@ -29,16 +29,19 @@ pub struct Board {
clock: Clock<'static>, clock: Clock<'static>,
storage: RomStorage<'static>, storage: RomStorage<'static>,
card_reader: CardReader<'static>, card_reader: CardReader<'static>,
trng: Trng trng: Trng,
} }
impl Board { impl Board {
pub async fn new(spawner: Spawner, peripherals: Peripherals) -> Result<Self, crate::error::Error> { pub async fn new(
spawner: Spawner,
peripherals: Peripherals,
) -> Result<Self, crate::error::Error> {
let timer_group = TimerGroup::new(peripherals.TIMG0); let timer_group = TimerGroup::new(peripherals.TIMG0);
let sw_ctrl = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timer_group.timer0, peripherals.FROM_CPU_INTR0);
esp_rtos::start(timer_group.timer0, sw_ctrl.software_interrupt0);
static SPI_BUS: StaticCell<Mutex<CriticalSectionRawMutex, Spi<'static, Async>>> = StaticCell::new(); static SPI_BUS: StaticCell<Mutex<CriticalSectionRawMutex, Spi<'static, Async>>> =
StaticCell::new();
let spi = Spi::new(peripherals.SPI2, Config::default())? let spi = Spi::new(peripherals.SPI2, Config::default())?
.into_async() .into_async()
.with_sck(peripherals.GPIO9) .with_sck(peripherals.GPIO9)
@@ -46,21 +49,22 @@ impl Board {
.with_miso(peripherals.GPIO11); .with_miso(peripherals.GPIO11);
let spi_bus = SPI_BUS.init(Mutex::new(spi)); let spi_bus = SPI_BUS.init(Mutex::new(spi));
static I2C_BUS: StaticCell<BlockingMutex<NoopRawMutex, RefCell<I2c<'static, Blocking>>>> = StaticCell::new(); static I2C_BUS: StaticCell<BlockingMutex<NoopRawMutex, RefCell<I2c<'static, Blocking>>>> =
let async_i2c = I2c::new( StaticCell::new();
peripherals.I2C0, let async_i2c = I2c::new(peripherals.I2C0, esp_hal::i2c::master::Config::default())?
esp_hal::i2c::master::Config::default()
)?
.with_sda(peripherals.GPIO21) .with_sda(peripherals.GPIO21)
.with_scl(peripherals.GPIO19); .with_scl(peripherals.GPIO19);
let i2c_bus = I2C_BUS.init(BlockingMutex::new(RefCell::new(async_i2c))); let i2c_bus = I2C_BUS.init(BlockingMutex::new(RefCell::new(async_i2c)));
let irq = gpio::Input::new(peripherals.GPIO6, InputConfig::default().with_pull(Pull::Up)); let irq = gpio::Input::new(
peripherals.GPIO6,
InputConfig::default().with_pull(Pull::Up),
);
let reset = gpio::Output::new(peripherals.GPIO5, Level::High, Default::default()); let reset = gpio::Output::new(peripherals.GPIO5, Level::High, Default::default());
let card_reader = CardReader::new(SharedI2c::new(i2c_bus), irq, reset); let card_reader = CardReader::new(SharedI2c::new(i2c_bus), irq, reset);
let vext = gpio::Output::new(peripherals.GPIO36, Level::Low, Default::default());
let lora = { let lora = {
let vext = gpio::Output::new(peripherals.GPIO36, Level::Low, Default::default());
let reset = gpio::Output::new(peripherals.GPIO12, Level::Low, Default::default()); let reset = gpio::Output::new(peripherals.GPIO12, Level::Low, Default::default());
let busy = gpio::Input::new(peripherals.GPIO13, InputConfig::default()); let busy = gpio::Input::new(peripherals.GPIO13, InputConfig::default());
let dio1 = gpio::Input::new(peripherals.GPIO14, InputConfig::default()); let dio1 = gpio::Input::new(peripherals.GPIO14, InputConfig::default());
@@ -74,12 +78,10 @@ impl Board {
let led = gpio::Output::new(peripherals.GPIO35, Level::Low, Default::default()); let led = gpio::Output::new(peripherals.GPIO35, Level::Low, Default::default());
spawner.spawn(led_task(led)?); spawner.spawn(led_task(led)?);
let storage = RomStorage::new( let storage = RomStorage::new(SharedI2c::new(i2c_bus));
SharedI2c::new(i2c_bus)
);
let trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1); let trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
core::mem::forget(trng_source); TRNG_SOURCE.init(trng_source);
let trng = Trng::try_new()?; let trng = Trng::try_new()?;
let instance = Self { let instance = Self {
@@ -88,12 +90,15 @@ impl Board {
clock, clock,
storage, storage,
card_reader, card_reader,
trng trng,
}; };
Ok(instance) Ok(instance)
} }
pub async fn start(mut self) -> Result<(), crate::error::Error> { pub async fn start(mut self) -> Result<(), crate::error::Error> {
static LORA_CHANNEL: StaticCell<LoraPubSub> = StaticCell::new();
let lora_channel = LORA_CHANNEL.init(PubSubChannel::new());
self.card_reader.init()?; self.card_reader.init()?;
self.lora.try_join().await?; self.lora.try_join().await?;
defmt::info!("Board started"); defmt::info!("Board started");
@@ -102,30 +107,24 @@ impl Board {
let time = self.lora.get_time(self.clock.get_datetime()?).await?; let time = self.lora.get_time(self.clock.get_datetime()?).await?;
let datetime = self.clock.update_time(time)?; let datetime = self.clock.update_time(time)?;
let app_data = AppData { let app_data = AppData {
last_boot_time: datetime last_boot_time: datetime,
}; };
defmt::debug!("stored app data: {}", self.storage.get_data::<AppData>()?); defmt::debug!("stored app data: {}", self.storage.get_data::<AppData>()?);
self.storage.write_data(&app_data).await?; self.storage.write_data(&app_data).await?;
static LORA_CHANNEL: StaticCell<LoraPubSub> = StaticCell::new(); self.spawner
let lora_channel = LORA_CHANNEL.init(PubSubChannel::new()); .spawn(components::card_reader::start_card_reader(
self.spawner.spawn(
components::card_reader::start_card_reader(
self.card_reader, self.card_reader,
lora_channel.publisher()?, lora_channel.publisher()?,
self.trng.clone() self.trng.clone(),
)? )?);
); self.spawner.spawn(components::lora::start_send_data_task(
self.spawner.spawn(
components::lora::start_send_data_task(
self.lora, self.lora,
lora_channel.subscriber()? lora_channel.subscriber()?,
)? )?);
); self.spawner.spawn(components::lora::start_heartbeat_task(
self.spawner.spawn( lora_channel.publisher()?,
components::lora::start_heartbeat_task(lora_channel.publisher()?)? )?);
);
loop { loop {
Timer::after_millis(100).await; Timer::after_millis(100).await;
+75 -23
View File
@@ -4,10 +4,10 @@ use embassy_sync::once_lock::OnceLock;
use embassy_time::{Delay, Timer}; use embassy_time::{Delay, Timer};
use esp_hal::gpio; use esp_hal::gpio;
use esp_hal::rng::Trng; use esp_hal::rng::Trng;
use pn532::types::felica::{CardSession, FelicaLiteSBlock, FelicaPollingRequestCode}; use pn532::types::felica::felica_lite_s::auth::{CardKey, CardKeyVersion};
use pn532::{Pn532}; use pn532::types::felica::felica_lite_s::config::{MemoryConfig, RFParameter};
use pn532::types::felica::felica_lite_s::auth::CardKey; use pn532::types::felica::{CardSession, FelicaLiteSBlock};
use pn532::types::BaudRate; use pn532::Pn532;
static CARD_KEY: OnceLock<CardKey> = OnceLock::new(); static CARD_KEY: OnceLock<CardKey> = OnceLock::new();
const CARD_KEY_STR: &str = env!("CARD_KEY"); const CARD_KEY_STR: &str = env!("CARD_KEY");
@@ -16,24 +16,30 @@ fn get_card_key() -> &'static CardKey {
CARD_KEY.get_or_init(|| { CARD_KEY.get_or_init(|| {
let mut buffer = [0u8; 16]; let mut buffer = [0u8; 16];
hex::decode_to_slice(CARD_KEY_STR, &mut buffer).unwrap(); hex::decode_to_slice(CARD_KEY_STR, &mut buffer).unwrap();
CardKey(buffer) CardKey::from(buffer)
}) })
} }
pub struct CardReader<'a> { pub struct CardReader<'a> {
driver: Pn532<gpio::Output<'a>, gpio::Input<'a>, Delay, pn532::I2cInterface<SharedI2c<'a>, Delay, gpio::Output<'a>, gpio::Input<'a>>> driver: Pn532<
gpio::Output<'a>,
gpio::Input<'a>,
Delay,
pn532::I2cInterface<SharedI2c<'a>, Delay, gpio::Output<'a>, gpio::Input<'a>>,
>,
} }
impl<'a> CardReader<'a> { impl<'a> CardReader<'a> {
pub fn new(i2c: SharedI2c<'a>, irq: gpio::Input<'a>, reset: gpio::Output<'a>) -> Self { pub fn new(i2c: SharedI2c<'a>, irq: gpio::Input<'a>, reset: gpio::Output<'a>) -> Self {
Self { Self {
driver: Pn532::new(pn532::I2cInterface::with_reset_irq(i2c, Delay, reset, irq)) driver: Pn532::new(pn532::I2cInterface::with_reset_irq(i2c, Delay, reset, irq)),
} }
} }
pub fn init(&mut self) -> Result<(), crate::error::Error> { pub fn init(&mut self) -> Result<(), crate::error::Error> {
self.driver.begin()?; self.driver.begin()?;
self.driver.sam_config()?; self.driver.sam_config()?;
self.driver.set_passive_activation_retries(5)?;
Ok(()) Ok(())
} }
} }
@@ -42,16 +48,29 @@ impl<'a> CardReader<'a> {
pub async fn start_card_reader( pub async fn start_card_reader(
mut card_reader: CardReader<'static>, mut card_reader: CardReader<'static>,
_publisher: LoraPublisher<'static>, _publisher: LoraPublisher<'static>,
mut trng: Trng mut trng: Trng,
) -> () { ) -> () {
loop {
match _start_card_reader(&mut card_reader, &_publisher, &mut trng).await {
Ok(_) => {}
Err(e) => {
defmt::error!("Error while reading card: {}", e);
}
}
}
}
async fn _start_card_reader(
card_reader: &mut CardReader<'static>,
_publisher: &LoraPublisher<'static>,
trng: &mut Trng,
) -> Result<(), crate::error::Error> {
let card_key = get_card_key(); let card_key = get_card_key();
defmt::trace!("card_key: {}", card_key); defmt::trace!("card_key: {}", card_key);
let driver = &mut card_reader.driver;
loop { loop {
Timer::after_millis(50).await; Timer::after_millis(10).await;
let Some(response) = (match card_reader.driver.felica_polling( let Some(response) = (match driver.felica_polling(Default::default()) {
Default::default()
) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
defmt::error!("card_reader driver error: {}", e); defmt::error!("card_reader driver error: {}", e);
@@ -62,6 +81,7 @@ pub async fn start_card_reader(
continue; continue;
}; };
defmt::info!("Polling card: {}", Debug2Format(&response)); defmt::info!("Polling card: {}", Debug2Format(&response));
defmt::debug!("IDm: {}", response.idm.into_bytes());
let card = CardSession::from(response); let card = CardSession::from(response);
// if let Err(e) = card_reader.driver.felica_write_without_encryption( // if let Err(e) = card_reader.driver.felica_write_without_encryption(
@@ -73,23 +93,55 @@ pub async fn start_card_reader(
// defmt::error!("card_reader write ck error: {}", e); // defmt::error!("card_reader write ck error: {}", e);
// continue; // continue;
// } // }
let id = driver.get_id(&card)?;
defmt::debug!("ID: {}", id);
let auth_result = card_reader.driver.try_authenticate( if let Some(card_id) = option_env!("CARD_ID") {
&mut trng, &card, card_key let mut id = [0u8; 6];
); hex::decode_to_slice(card_id,&mut id)?;
let session = match auth_result { driver.set_id(&card, id, None)?;
}
let mut mc = driver.get_mc(&card)?;
defmt::info!("Config: {}", mc);
mc.access.set_system_block(0x00);
mc.ndef = 0x00;
mc.rf = RFParameter::from(0xFF);
mc.access.set_is_ck_ckv_rewritable(true);
let mut read_auth = mc.auth.scratch_pad_read();
read_auth.set_pad0(true);
mc.auth.set_scratch_pad_read(read_auth);
let mut write_auth = mc.auth.scratch_pad_write();
write_auth.set_pad0(true);
mc.auth.set_scratch_pad_write(write_auth);
let mut mac_auth = mc.auth.scratch_pad_mac_write();
mac_auth.set_pad0(true);
mc.auth.set_scratch_pad_mac_write(mac_auth);
let block: [u8; 16] = mc.into();
defmt::debug!("1st insurance Config: {}", block);
driver.set_mc(&card, mc)?;
let session = match driver.try_authenticate(trng, &card, card_key) {
Ok(Some(sk)) => sk, Ok(Some(sk)) => sk,
Ok(None) => { Ok(None) => {
defmt::warn!("card key is invalid"); defmt::warn!("card key is invalid");
continue; continue;
}, }
Err(e) => { Err(e) => {
defmt::error!("card_reader auth error: {}", e); defmt::error!("card_reader auth error: {}", e);
continue; continue;
} }
}; };
let read_result = card_reader.driver.read_with_mac(&card, &session, FelicaLiteSBlock::PAD0);
let _read_data = match read_result { driver.set_ck_with_mac(&card, &session, &card_key)?;
driver.set_ckv_with_mac(&card, &session, &CardKeyVersion::new(1))?;
driver.get_ckv(&card)?;
let _read_data = match driver.read_with_mac(&card, &session, FelicaLiteSBlock::PAD0) {
Ok(d) => d, Ok(d) => d,
Err(e) => { Err(e) => {
defmt::error!("card_reader read with mac error: {}", e); defmt::error!("card_reader read with mac error: {}", e);
@@ -97,8 +149,8 @@ pub async fn start_card_reader(
} }
}; };
match card_reader.driver.release() { match driver.release() {
Ok(_) => {}, Ok(_) => {}
Err(e) => { Err(e) => {
defmt::error!("card_reader release error: {}", e); defmt::error!("card_reader release error: {}", e);
continue; continue;
@@ -107,7 +159,7 @@ pub async fn start_card_reader(
// InRelease switches the RF field off when no target remains // InRelease switches the RF field off when no target remains
// (UM0701-02 §7.3.11), so re-enable it before the next poll. // (UM0701-02 §7.3.11), so re-enable it before the next poll.
if let Err(e) = card_reader.driver.set_rf_field(0x00, 0x01) { if let Err(e) = driver.set_rf_field(0x00, 0x01) {
defmt::error!("card_reader RF field error: {}", e); defmt::error!("card_reader RF field error: {}", e);
} }
Timer::after_millis(200).await; Timer::after_millis(200).await;
+4 -4
View File
@@ -7,7 +7,7 @@ use embassy_time::{Duration, Timer};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
const ADDR_SIZE: usize = 256; const BLOCK_SIZE: usize = 256;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub(crate) enum StorageError { pub(crate) enum StorageError {
@@ -44,7 +44,7 @@ impl<'d> RomStorage<'d> {
Ok(addr.clone()) Ok(addr.clone())
} else { } else {
let size = self.addr_table.len(); let size = self.addr_table.len();
let addr = size as u32 * ADDR_SIZE as u32; let addr = size as u32 * BLOCK_SIZE as u32;
if addr >= u16::MAX as u32 { if addr >= u16::MAX as u32 {
return Err(StorageError::OutOfAddress) return Err(StorageError::OutOfAddress)
} }
@@ -58,7 +58,7 @@ impl<'d> RomStorage<'d> {
let addr = self.get_or_insert_addr::<T>()?; let addr = self.get_or_insert_addr::<T>()?;
let bytes = postcard::to_allocvec(data)?; let bytes = postcard::to_allocvec(data)?;
let bytes_len = bytes.len(); let bytes_len = bytes.len();
if bytes_len > ADDR_SIZE { if bytes_len > BLOCK_SIZE {
return Err(StorageError::StorageOverSize(bytes_len).into()); return Err(StorageError::StorageOverSize(bytes_len).into());
} }
for (i, chunk) in bytes.chunks(32).enumerate() { for (i, chunk) in bytes.chunks(32).enumerate() {
@@ -74,7 +74,7 @@ impl<'d> RomStorage<'d> {
pub fn get_data<T: DeserializeOwned + 'static>(&mut self) -> Result<T, crate::error::Error> { pub fn get_data<T: DeserializeOwned + 'static>(&mut self) -> Result<T, crate::error::Error> {
let addr = self.get_or_insert_addr::<T>()?; let addr = self.get_or_insert_addr::<T>()?;
let mut bytes = [0u8; ADDR_SIZE]; let mut bytes = [0u8; BLOCK_SIZE];
self.driver.read_data(addr, &mut bytes) self.driver.read_data(addr, &mut bytes)
.map_err(|e| StorageError::EEPROMError(e))?; .map_err(|e| StorageError::EEPROMError(e))?;
let data = postcard::from_bytes(&bytes)?; let data = postcard::from_bytes(&bytes)?;