Skip to content

Commit f0a9550

Browse files
committed
fix: use previous NDEF parser and fix NFC reader halt logic
1 parent 76fbdff commit f0a9550

8 files changed

Lines changed: 1756 additions & 869 deletions

File tree

Cargo.lock

Lines changed: 1451 additions & 838 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ license-file = "LICENSE"
1010
[features]
1111
default = ["hardware"]
1212
hardware-emulation = ["dep:eframe", "dep:egui"]
13-
hardware = ["dep:aw2013", "dep:mfrc522", "dep:gpiocdev", "dep:ndef-rs", "dep:linux-embedded-hal"]
13+
hardware = ["dep:aw2013", "dep:mfrc522", "dep:gpiocdev", "dep:linux-embedded-hal"]
1414
with-bindgen = ["dep:aws-lc-sys"]
1515

1616
[dependencies]
@@ -40,7 +40,6 @@ eframe = { version = "0.32.0", optional = true }
4040
egui = { version = "0.32.0", optional = true }
4141
gpiocdev = { version = "0.7.3", features = ["async_tokio"], optional = true }
4242
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
43-
ndef-rs = { version = "0.2.2", optional = true }
4443
tokio-util = "0.7.15"
4544
directories = { version = "6.0.0" }
4645
uuid = { version = "1.17.0", features = ["serde"] }

src/hardware/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod buttons;
1818
#[cfg(feature = "hardware-emulation")]
1919
mod emulated;
2020
pub mod led;
21+
mod ndef;
2122
pub mod nfc;
2223
#[cfg(not(feature = "hardware-emulation"))]
2324
mod pi;

src/hardware/ndef.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
use anyhow::{anyhow, Result};
2+
3+
// References:
4+
//
5+
// NDEF parser: https://github.com/TapTrack/NdefLibrary
6+
// NDEF message spec: https://www.netes.com.tr/netes/dosyalar/dosya/B6159F60458582512B16EF1263ADE707.pdf
7+
// Mifare reader: https://github.com/hackeriet/pyhackeriet
8+
// Mifare specs: https://www.puntoflotante.net/TUTORIAL-RFID-ISO-14443A-TAGS-13.56-MHZ.htm
9+
10+
#[derive(PartialEq)]
11+
enum NdefMessageParserState {
12+
Init,
13+
Length,
14+
Value,
15+
}
16+
17+
pub struct NdefMessageParser {
18+
state: NdefMessageParserState,
19+
length: i32,
20+
pub data: Vec<u8>,
21+
}
22+
23+
impl NdefMessageParser {
24+
pub fn new() -> Self {
25+
Self {
26+
state: NdefMessageParserState::Init,
27+
length: -1,
28+
data: Vec::new(),
29+
}
30+
}
31+
32+
pub fn add_data(&mut self, data: &[u8]) {
33+
for byte in data {
34+
match self.state {
35+
NdefMessageParserState::Init => {
36+
if *byte == 0x00 {
37+
continue;
38+
}
39+
40+
if *byte == 0x03 {
41+
self.state = NdefMessageParserState::Length;
42+
}
43+
}
44+
45+
NdefMessageParserState::Length => {
46+
if self.length == -1 {
47+
if *byte == 0xff {
48+
self.length = -2;
49+
} else {
50+
self.length = *byte as i32;
51+
self.state = NdefMessageParserState::Value;
52+
}
53+
54+
continue;
55+
}
56+
57+
if self.length == -2 {
58+
self.length = *byte as i32;
59+
} else {
60+
self.length = (self.length << 8) & *byte as i32;
61+
self.state = NdefMessageParserState::Value;
62+
}
63+
}
64+
65+
NdefMessageParserState::Value => {
66+
self.data.push(*byte);
67+
68+
if self.data.len() as i32 == self.length {
69+
return;
70+
}
71+
}
72+
}
73+
}
74+
}
75+
76+
pub fn is_done(&self) -> bool {
77+
self.data.len() as i32 == self.length
78+
}
79+
80+
pub fn has_started(&self) -> bool {
81+
self.state != NdefMessageParserState::Init
82+
}
83+
}
84+
85+
pub struct NdefTextRecord {
86+
_id: Vec<u8>,
87+
value: Vec<u8>,
88+
}
89+
90+
impl NdefTextRecord {
91+
pub fn text(&self) -> Result<String> {
92+
if self.value.is_empty() {
93+
return Ok(String::new());
94+
}
95+
96+
let code_length = self.value[0] & 0b00111111;
97+
Ok(String::from_utf8(
98+
self.value[(code_length as usize + 1)..].to_vec(),
99+
)?)
100+
}
101+
}
102+
103+
pub fn parse_ndef_text_record(data: &[u8]) -> Result<NdefTextRecord> {
104+
let record_length = data.len();
105+
let mut index = 0;
106+
107+
if record_length <= index {
108+
return Err(anyhow!("Flags missing"));
109+
}
110+
111+
let flags = data[index];
112+
113+
let _is_message_begin = (flags & 0b10000000) != 0;
114+
let _is_message_end = (flags & 0b01000000) != 0;
115+
let is_chunked = (flags & 0b00100000) != 0;
116+
let is_short_record = (flags & 0b00010000) != 0;
117+
let has_id_length = (flags & 0b00001000) != 0;
118+
let type_name_format = flags & 0b00000111;
119+
120+
if is_chunked {
121+
return Err(anyhow!("Chunked records are not supported"));
122+
}
123+
124+
index += 1;
125+
126+
if record_length <= index {
127+
return Err(anyhow!("Type length missing"));
128+
}
129+
130+
let type_length = data[index];
131+
132+
index += 1;
133+
134+
let payload_length = if is_short_record {
135+
if record_length <= index {
136+
return Err(anyhow!("Payload length missing"));
137+
}
138+
139+
let payload_length_index = index;
140+
index += 1;
141+
data[payload_length_index] as u32
142+
} else {
143+
if record_length <= index + 3 {
144+
return Err(anyhow!("Payload length missing"));
145+
}
146+
147+
let payload_length_index = index;
148+
index += 4;
149+
u32::from_be_bytes(data[payload_length_index..(payload_length_index + 4)].try_into()?)
150+
};
151+
152+
let id_length = if has_id_length {
153+
if record_length <= index {
154+
return Err(anyhow!("ID length missing"));
155+
}
156+
157+
let id_length_index = index;
158+
index += 1;
159+
data[id_length_index]
160+
} else {
161+
0
162+
};
163+
164+
let payload_type = if type_length > 0 {
165+
let type_index = index;
166+
index += type_length as usize;
167+
168+
if record_length < type_index + type_length as usize {
169+
return Err(anyhow!("Type missing"));
170+
}
171+
172+
data[type_index..(type_index + type_length as usize)].to_vec()
173+
} else {
174+
vec![]
175+
};
176+
177+
let payload_id = if id_length > 0 {
178+
let id_index = index;
179+
index += id_length as usize;
180+
181+
if record_length < id_index + id_length as usize {
182+
return Err(anyhow!("ID missing"));
183+
}
184+
185+
data[id_index..(id_index + id_length as usize)].to_vec()
186+
} else {
187+
vec![]
188+
};
189+
190+
let payload_value = if payload_length > 0 {
191+
let value_index = index;
192+
193+
if record_length < value_index + payload_length as usize {
194+
return Err(anyhow!("Payload missing"));
195+
}
196+
197+
data[value_index..(value_index + payload_length as usize)].to_vec()
198+
} else {
199+
vec![]
200+
};
201+
202+
if type_name_format != 1 {
203+
return Err(anyhow!("Not a well known payload type"));
204+
}
205+
206+
if payload_type != [0x54] {
207+
return Err(anyhow!("Not a text record"));
208+
}
209+
210+
Ok(NdefTextRecord {
211+
_id: payload_id,
212+
value: payload_value,
213+
})
214+
}

src/hardware/nfc.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use hex::{decode_to_slice, FromHex, FromHexError};
22
#[cfg(not(feature = "hardware-emulation"))]
33
use mfrc522::Uid;
4-
use serde::{Deserialize, Serialize};
4+
use serde::{Deserialize, Deserializer, Serialize};
55
use std::fmt;
66
use std::fmt::Display;
77
use thiserror::Error;
@@ -13,7 +13,7 @@ pub enum NfcUidError {
1313
InvalidLength,
1414
}
1515

16-
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
16+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1717
pub enum NfcUid {
1818
Single([u8; 4]),
1919
Double([u8; 7]),
@@ -42,6 +42,25 @@ impl Display for NfcUid {
4242
}
4343
}
4444

45+
impl<'de> Deserialize<'de> for NfcUid {
46+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
47+
where
48+
D: Deserializer<'de>,
49+
{
50+
let hex = String::deserialize(deserializer)?;
51+
NfcUid::from_hex(&hex).map_err(serde::de::Error::custom)
52+
}
53+
}
54+
55+
impl Serialize for NfcUid {
56+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
57+
where
58+
S: serde::Serializer,
59+
{
60+
serializer.serialize_str(&hex::encode(self.as_bytes()))
61+
}
62+
}
63+
4564
#[cfg(not(feature = "hardware-emulation"))]
4665
impl From<Uid> for NfcUid {
4766
fn from(uid: Uid) -> Self {
@@ -79,6 +98,14 @@ impl FromHex for NfcUid {
7998
}
8099

81100
impl NfcUid {
101+
pub fn as_bytes(&self) -> &[u8] {
102+
match self {
103+
NfcUid::Single(data) => data,
104+
NfcUid::Double(data) => data,
105+
NfcUid::Triple(data) => data,
106+
}
107+
}
108+
82109
pub fn as_tagged_bytes(&self) -> Vec<u8> {
83110
let raw_bytes: Vec<u8> = match self {
84111
NfcUid::Single(data) => data.into(),

0 commit comments

Comments
 (0)