ESPHome  2024.3.1
pn532_mifare_classic.cpp
Go to the documentation of this file.
1 #include <memory>
2 
3 #include "pn532.h"
4 #include "esphome/core/log.h"
5 
6 namespace esphome {
7 namespace pn532 {
8 
9 static const char *const TAG = "pn532.mifare_classic";
10 
11 std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(std::vector<uint8_t> &uid) {
12  uint8_t current_block = 4;
13  uint8_t message_start_index = 0;
14  uint32_t message_length = 0;
15 
16  if (this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
17  std::vector<uint8_t> data;
18  if (this->read_mifare_classic_block_(current_block, data)) {
19  if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) {
20  return make_unique<nfc::NfcTag>(uid, nfc::ERROR);
21  }
22  } else {
23  ESP_LOGE(TAG, "Failed to read block %d", current_block);
24  return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
25  }
26  } else {
27  ESP_LOGV(TAG, "Tag is not NDEF formatted");
28  return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
29  }
30 
31  uint32_t index = 0;
32  uint32_t buffer_size = nfc::get_mifare_classic_buffer_size(message_length);
33  std::vector<uint8_t> buffer;
34 
35  while (index < buffer_size) {
36  if (nfc::mifare_classic_is_first_block(current_block)) {
37  if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
38  ESP_LOGE(TAG, "Error, Block authentication failed for %d", current_block);
39  }
40  }
41  std::vector<uint8_t> block_data;
42  if (this->read_mifare_classic_block_(current_block, block_data)) {
43  buffer.insert(buffer.end(), block_data.begin(), block_data.end());
44  } else {
45  ESP_LOGE(TAG, "Error reading block %d", current_block);
46  }
47 
48  index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
49  current_block++;
50 
51  if (nfc::mifare_classic_is_trailer_block(current_block)) {
52  current_block++;
53  }
54  }
55 
56  if (buffer.begin() + message_start_index < buffer.end()) {
57  buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
58  } else {
59  return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
60  }
61 
62  return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC, buffer);
63 }
64 
65 bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data) {
66  if (!this->write_command_({
67  PN532_COMMAND_INDATAEXCHANGE,
68  0x01, // One card
69  nfc::MIFARE_CMD_READ,
70  block_num,
71  })) {
72  return false;
73  }
74 
75  if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) {
76  return false;
77  }
78  data.erase(data.begin());
79 
80  ESP_LOGVV(TAG, " Block %d: %s", block_num, nfc::format_bytes(data).c_str());
81  return true;
82 }
83 
84 bool PN532::auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num,
85  const uint8_t *key) {
86  std::vector<uint8_t> data({
87  PN532_COMMAND_INDATAEXCHANGE,
88  0x01, // One card
89  key_num, // Mifare Key slot
90  block_num, // Block number
91  });
92  data.insert(data.end(), key, key + 6);
93  data.insert(data.end(), uid.begin(), uid.end());
94  if (!this->write_command_(data)) {
95  ESP_LOGE(TAG, "Authentication failed - Block %d", block_num);
96  return false;
97  }
98 
99  std::vector<uint8_t> response;
100  if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) {
101  ESP_LOGE(TAG, "Authentication failed - Block 0x%02x", block_num);
102  return false;
103  }
104 
105  return true;
106 }
107 
108 bool PN532::format_mifare_classic_mifare_(std::vector<uint8_t> &uid) {
109  std::vector<uint8_t> blank_buffer(
110  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
111  std::vector<uint8_t> trailer_buffer(
112  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
113 
114  bool error = false;
115 
116  for (int block = 0; block < 64; block += 4) {
117  if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
118  continue;
119  }
120  if (block != 0) {
121  if (!this->write_mifare_classic_block_(block, blank_buffer)) {
122  ESP_LOGE(TAG, "Unable to write block %d", block);
123  error = true;
124  }
125  }
126  if (!this->write_mifare_classic_block_(block + 1, blank_buffer)) {
127  ESP_LOGE(TAG, "Unable to write block %d", block + 1);
128  error = true;
129  }
130  if (!this->write_mifare_classic_block_(block + 2, blank_buffer)) {
131  ESP_LOGE(TAG, "Unable to write block %d", block + 2);
132  error = true;
133  }
134  if (!this->write_mifare_classic_block_(block + 3, trailer_buffer)) {
135  ESP_LOGE(TAG, "Unable to write block %d", block + 3);
136  error = true;
137  }
138  }
139 
140  return !error;
141 }
142 
143 bool PN532::format_mifare_classic_ndef_(std::vector<uint8_t> &uid) {
144  std::vector<uint8_t> empty_ndef_message(
145  {0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
146  std::vector<uint8_t> blank_block(
147  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
148  std::vector<uint8_t> block_1_data(
149  {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1});
150  std::vector<uint8_t> block_2_data(
151  {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1});
152  std::vector<uint8_t> block_3_trailer(
153  {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
154  std::vector<uint8_t> ndef_trailer(
155  {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
156 
157  if (!this->auth_mifare_classic_block_(uid, 0, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
158  ESP_LOGE(TAG, "Unable to authenticate block 0 for formatting!");
159  return false;
160  }
161  if (!this->write_mifare_classic_block_(1, block_1_data))
162  return false;
163  if (!this->write_mifare_classic_block_(2, block_2_data))
164  return false;
165  if (!this->write_mifare_classic_block_(3, block_3_trailer))
166  return false;
167 
168  ESP_LOGD(TAG, "Sector 0 formatted to NDEF");
169 
170  for (int block = 4; block < 64; block += 4) {
171  if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
172  return false;
173  }
174  if (block == 4) {
175  if (!this->write_mifare_classic_block_(block, empty_ndef_message)) {
176  ESP_LOGE(TAG, "Unable to write block %d", block);
177  }
178  } else {
179  if (!this->write_mifare_classic_block_(block, blank_block)) {
180  ESP_LOGE(TAG, "Unable to write block %d", block);
181  }
182  }
183  if (!this->write_mifare_classic_block_(block + 1, blank_block)) {
184  ESP_LOGE(TAG, "Unable to write block %d", block + 1);
185  }
186  if (!this->write_mifare_classic_block_(block + 2, blank_block)) {
187  ESP_LOGE(TAG, "Unable to write block %d", block + 2);
188  }
189  if (!this->write_mifare_classic_block_(block + 3, ndef_trailer)) {
190  ESP_LOGE(TAG, "Unable to write trailer block %d", block + 3);
191  }
192  }
193  return true;
194 }
195 
196 bool PN532::write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &write_data) {
197  std::vector<uint8_t> data({
198  PN532_COMMAND_INDATAEXCHANGE,
199  0x01, // One card
200  nfc::MIFARE_CMD_WRITE,
201  block_num,
202  });
203  data.insert(data.end(), write_data.begin(), write_data.end());
204  if (!this->write_command_(data)) {
205  ESP_LOGE(TAG, "Error writing block %d", block_num);
206  return false;
207  }
208 
209  std::vector<uint8_t> response;
210  if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response)) {
211  ESP_LOGE(TAG, "Error writing block %d", block_num);
212  return false;
213  }
214 
215  return true;
216 }
217 
218 bool PN532::write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
219  auto encoded = message->encode();
220 
221  uint32_t message_length = encoded.size();
222  uint32_t buffer_length = nfc::get_mifare_classic_buffer_size(message_length);
223 
224  encoded.insert(encoded.begin(), 0x03);
225  if (message_length < 255) {
226  encoded.insert(encoded.begin() + 1, message_length);
227  } else {
228  encoded.insert(encoded.begin() + 1, 0xFF);
229  encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
230  encoded.insert(encoded.begin() + 3, message_length & 0xFF);
231  }
232  encoded.push_back(0xFE);
233 
234  encoded.resize(buffer_length, 0);
235 
236  uint32_t index = 0;
237  uint8_t current_block = 4;
238 
239  while (index < buffer_length) {
240  if (nfc::mifare_classic_is_first_block(current_block)) {
241  if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
242  return false;
243  }
244  }
245 
246  std::vector<uint8_t> data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_CLASSIC_BLOCK_SIZE);
247  if (!this->write_mifare_classic_block_(current_block, data)) {
248  return false;
249  }
250  index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
251  current_block++;
252 
253  if (nfc::mifare_classic_is_trailer_block(current_block)) {
254  // Skipping as cannot write to trailer
255  current_block++;
256  }
257  }
258  return true;
259 }
260 
261 } // namespace pn532
262 } // namespace esphome
bool format_mifare_classic_ndef_(std::vector< uint8_t > &uid)
virtual bool write_data(const std::vector< uint8_t > &data)=0
virtual bool read_response(uint8_t command, std::vector< uint8_t > &data)=0
uint32_t get_mifare_classic_buffer_size(uint32_t message_length)
Definition: nfc.cpp:78
bool mifare_classic_is_trailer_block(uint8_t block_num)
Definition: nfc.cpp:99
bool decode_mifare_classic_tlv(std::vector< uint8_t > &data, uint32_t &message_length, uint8_t &message_start_index)
Definition: nfc.cpp:55
bool write_mifare_classic_block_(uint8_t block_num, std::vector< uint8_t > &data)
bool format_mifare_classic_mifare_(std::vector< uint8_t > &uid)
bool auth_mifare_classic_block_(std::vector< uint8_t > &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key)
std::vector< uint8_t > encode()
bool write_mifare_classic_tag_(std::vector< uint8_t > &uid, nfc::NdefMessage *message)
bool mifare_classic_is_first_block(uint8_t block_num)
Definition: nfc.cpp:91
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
std::unique_ptr< nfc::NfcTag > read_mifare_classic_tag_(std::vector< uint8_t > &uid)
bool write_command_(const std::vector< uint8_t > &data)
Definition: pn532.cpp:246
std::string format_bytes(std::vector< uint8_t > &bytes)
Definition: nfc.cpp:22
bool read_mifare_classic_block_(uint8_t block_num, std::vector< uint8_t > &data)