1use nfc_sys::{
2 nfc_close, nfc_exit, nfc_init, nfc_initiator_init, nfc_open, nfc_perror,
3};
4use openssl::error::ErrorStack;
5use openssl::pkey::{PKey, Private, Public};
6use std::ffi::{c_char, CString};
7use std::fmt::{self, Display, Formatter};
8use std::marker::PhantomData;
9use std::mem::MaybeUninit;
10use std::time::Duration;
11
12mod desfire;
13mod mobile;
14use crate::desfire::*;
15use crate::mobile::*;
16
17impl Display for NfcError {
18 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
19 match self {
20 Self::NonceMismatch => write!(f, "None mismatch"),
21 Self::NoResponse => {
22 write!(f, "Did not receive a response. Is the tag too far away?")
23 }
24 Self::CryptoError(err) => write!(f, "Cryptography error: {err}"),
25 Self::ConnectFailed => write!(f, "Failed to connect"),
26 Self::CommunicationError => {
27 write!(f, "Communication failed. Is the tag too far away?")
28 }
29 Self::InvalidSignature => {
30 write!(f, "Association had an invalid signature!")
31 }
32 Self::BadAssociation => write!(f, "Association ID is not valid UTF-8"),
33 }
34 }
35}
36
37impl std::error::Error for NfcError {}
38
39#[derive(Debug)]
40pub enum NfcError {
41 NonceMismatch,
43 NoResponse,
44 CryptoError(ErrorStack),
45 ConnectFailed,
46 CommunicationError,
47 InvalidSignature,
48 BadAssociation,
49}
50
51#[derive(Debug)]
52pub(crate) struct DeviceWrapper<'device> {
53 _context: NfcContext<'device>,
54 device: *mut nfc_sys::nfc_device,
55}
56
57impl<'device> Drop for DeviceWrapper<'device> {
58 fn drop(&mut self) {
59 unsafe { nfc_close(self.device) };
60 }
61}
62
63#[derive(Debug)]
66struct NfcContext<'context> {
67 context: *mut nfc_sys::nfc_context,
68 _lifetime: PhantomData<&'context ()>,
69}
70
71impl Drop for NfcContext<'_> {
72 fn drop(&mut self) {
73 unsafe { nfc_exit(self.context) };
74 }
75}
76
77impl<'a> Default for NfcContext<'a> {
78 fn default() -> Self {
79 let mut context_uninit = MaybeUninit::<*mut nfc_sys::nfc_context>::uninit();
80 Self {
81 context: unsafe {
82 nfc_init(context_uninit.as_mut_ptr());
83 if context_uninit.as_mut_ptr().is_null() {
84 panic!("Malloc failed");
85 }
86 context_uninit.assume_init()
87 },
88 _lifetime: PhantomData,
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
96pub struct RealmWriteKeys<'a> {
97 update_key: &'a [u8],
100 desfire_signing_private_key: PKey<Private>,
103}
104
105#[derive(Debug, Clone)]
108pub struct Realm<'a> {
109 slot: RealmType,
111 auth_key: Vec<u8>,
113 read_key: Vec<u8>,
115 desfire_signing_public_key: PKey<Public>,
117 mobile_decryption_private_key: PKey<Private>,
120 mobile_signing_private_key: PKey<Private>,
122 secrets: Option<RealmWriteKeys<'a>>,
124}
125
126impl<'a> Realm<'a> {
127 pub fn new(
142 slot: RealmType,
143 auth_key: Vec<u8>,
144 read_key: Vec<u8>,
145 desfire_signing_public_key: &[u8],
146 mobile_decryption_private_key: &[u8],
147 mobile_signing_private_key: &[u8],
148 secrets: Option<RealmWriteKeys<'a>>,
149 ) -> Self {
150 Self {
151 slot,
152 auth_key,
153 read_key,
154 desfire_signing_public_key: PKey::public_key_from_pem(
155 desfire_signing_public_key,
156 )
157 .expect("Bad key format"),
158 mobile_decryption_private_key: PKey::private_key_from_pem(
159 mobile_decryption_private_key,
160 )
161 .expect("Bad key format"),
162 mobile_signing_private_key: PKey::private_key_from_pem(
163 mobile_signing_private_key,
164 )
165 .expect("Bad key format"),
166 secrets,
167 }
168 }
169}
170
171#[repr(u8)]
173#[derive(Debug, Clone)]
174pub enum RealmType {
175 Door = 0,
177 Drink = 1,
179 MemberProjects = 2,
184}
185
186impl Display for UndifferentiatedTag<'_> {
187 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
188 match self {
189 Self::Desfire(_) => write!(f, "Desfire"),
190 Self::Mobile(_) => write!(f, "Mobile"),
191 }
192 }
193}
194
195impl Display for RealmType {
196 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
197 match self {
198 Self::Door => write!(f, "Door"),
199 Self::Drink => write!(f, "Drink"),
200 Self::MemberProjects => write!(f, "Member Projects"),
201 }
202 }
203}
204
205impl Copy for RealmType {}
207
208pub struct GatekeeperReader<'device> {
211 device_wrapper: Option<DeviceWrapper<'device>>,
213 connection_string: String,
215 realm: Realm<'device>,
217}
218
219impl<'device> DeviceWrapper<'device> {
220 fn new(connection_string: String) -> Option<Self> {
224 let context = NfcContext::default();
225 let device_string = CString::new(connection_string).unwrap();
226 let device_string = device_string.into_bytes_with_nul();
227
228 let mut device_string_bytes: [c_char; 1024] = [0; 1024];
229 for (index, character) in device_string.into_iter().enumerate() {
230 device_string_bytes[index] = character as c_char;
231 }
232
233 let device_ptr = unsafe {
234 let device_ptr = nfc_open(context.context, &device_string_bytes);
235 if device_ptr.is_null() {
236 log::error!("Failed to open NFC device...");
237 return None;
238 }
239 log::debug!("Opened an NFC device!");
240 device_ptr
241 };
242 Some(Self {
243 device: device_ptr,
244 _context: context,
245 })
246 }
247}
248
249#[derive(Debug)]
252pub enum UndifferentiatedTag<'a> {
253 Desfire(DesfireNfcTag<'a>),
254 Mobile(MobileNfcTag<'a>),
255}
256
257pub trait NfcTag {
259 fn authenticate(&self) -> Result<String, NfcError>;
263}
264
265impl NfcTag for UndifferentiatedTag<'_> {
266 fn authenticate(&self) -> Result<String, NfcError> {
267 match self {
268 Self::Desfire(desfire) => desfire.authenticate(),
269 Self::Mobile(mobile) => mobile.authenticate(),
270 }
271 }
272}
273
274#[must_use]
276enum ReaderStatus {
277 Available,
279 Unavailable,
281}
282
283impl<'device> GatekeeperReader<'device> {
284 pub fn new(connection_string: String, realm: Realm<'device>) -> Option<Self> {
288 let device_wrapper = Some(DeviceWrapper::new(connection_string.clone())?);
289 Some(Self {
290 device_wrapper,
291 connection_string,
292 realm,
293 })
294 }
295
296 fn ensure_reader_available(&mut self) -> ReaderStatus {
300 for _ in 0..3 {
301 if self.device_wrapper.as_ref().map(|device_wrapper| unsafe {
302 nfc_initiator_init(device_wrapper.device)
303 } < 0).unwrap_or(true)
304 {
305 log::error!("Couldn't init NFC initiator!!!");
306 if let Some(device_wrapper) = &self.device_wrapper {
307 unsafe {
308 let msg = CString::new("Failed to init device initiator :(").unwrap();
309 nfc_perror(device_wrapper.device, msg.as_ptr())
310 };
311 }
312 log::error!("Resetting the device and trying again!");
313 std::thread::sleep(Duration::from_millis(500));
314 self.device_wrapper = None;
315 if let Some(device_wrapper) =
316 DeviceWrapper::new(self.connection_string.clone())
317 {
318 self.device_wrapper = Some(device_wrapper);
319 }
320 } else {
321 return ReaderStatus::Available;
323 }
324 }
325 ReaderStatus::Unavailable
326 }
327
328 pub fn get_nearby_tags(&mut self) -> Vec<UndifferentiatedTag> {
334 if let ReaderStatus::Unavailable = self.ensure_reader_available() {
336 return vec![];
337 }
338
339 if let Some(tag) = self.find_first_mobile_tag() {
341 return vec![UndifferentiatedTag::Mobile(tag)];
342 }
343
344 self.find_desfire_tags()
346 }
347}