ESPHome  2024.12.2
pn7160_mifare_classic.cpp
Go to the documentation of this file.
1 #include <memory>
2 
3 #include "pn7160.h"
4 #include "esphome/core/log.h"
5 
6 namespace esphome {
7 namespace pn7160 {
8 
9 static const char *const TAG = "pn7160.mifare_classic";
10 
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_(current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY) != nfc::STATUS_OK) {
17  ESP_LOGE(TAG, "Tag auth failed while attempting to read tag data");
18  return nfc::STATUS_FAILED;
19  }
20  std::vector<uint8_t> data;
21 
22  if (this->read_mifare_classic_block_(current_block, data) == nfc::STATUS_OK) {
23  if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) {
24  return nfc::STATUS_FAILED;
25  }
26  } else {
27  ESP_LOGE(TAG, "Failed to read block %u", current_block);
28  return nfc::STATUS_FAILED;
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_(current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY) != nfc::STATUS_OK) {
38  ESP_LOGE(TAG, "Block authentication failed for %u", current_block);
39  return nfc::STATUS_FAILED;
40  }
41  }
42  std::vector<uint8_t> block_data;
43  if (this->read_mifare_classic_block_(current_block, block_data) != nfc::STATUS_OK) {
44  ESP_LOGE(TAG, "Error reading block %u", current_block);
45  return nfc::STATUS_FAILED;
46  } else {
47  buffer.insert(buffer.end(), block_data.begin(), block_data.end());
48  }
49 
50  index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
51  current_block++;
52 
53  if (nfc::mifare_classic_is_trailer_block(current_block)) {
54  current_block++;
55  }
56  }
57 
58  if (buffer.begin() + message_start_index < buffer.end()) {
59  buffer.erase(buffer.begin(), buffer.begin() + message_start_index);
60  } else {
61  return nfc::STATUS_FAILED;
62  }
63 
64  tag.set_ndef_message(make_unique<nfc::NdefMessage>(buffer));
65 
66  return nfc::STATUS_OK;
67 }
68 
69 uint8_t PN7160::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data) {
70  nfc::NciMessage rx;
71  nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {XCHG_DATA_OID, nfc::MIFARE_CMD_READ, block_num});
72 
73  ESP_LOGVV(TAG, "Read XCHG_DATA_REQ: %s", nfc::format_bytes(tx.get_message()).c_str());
74  if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
75  ESP_LOGE(TAG, "Timeout reading tag data");
76  return nfc::STATUS_FAILED;
77  }
78 
79  if ((!rx.message_type_is(nfc::NCI_PKT_MT_DATA)) || (!rx.simple_status_response_is(XCHG_DATA_OID)) ||
80  (!rx.message_length_is(18))) {
81  ESP_LOGE(TAG, "MFC read block failed - block 0x%02x", block_num);
82  ESP_LOGV(TAG, "Read response: %s", nfc::format_bytes(rx.get_message()).c_str());
83  return nfc::STATUS_FAILED;
84  }
85 
86  data.insert(data.begin(), rx.get_message().begin() + 4, rx.get_message().end() - 1);
87 
88  ESP_LOGVV(TAG, " Block %u: %s", block_num, nfc::format_bytes(data).c_str());
89  return nfc::STATUS_OK;
90 }
91 
92 uint8_t PN7160::auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key) {
93  nfc::NciMessage rx;
94  nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {MFC_AUTHENTICATE_OID, this->sect_to_auth_(block_num), key_num});
95 
96  switch (key_num) {
97  case nfc::MIFARE_CMD_AUTH_A:
98  tx.get_message().back() = MFC_AUTHENTICATE_PARAM_KS_A;
99  break;
100 
101  case nfc::MIFARE_CMD_AUTH_B:
102  tx.get_message().back() = MFC_AUTHENTICATE_PARAM_KS_B;
103  break;
104 
105  default:
106  break;
107  }
108 
109  if (key != nullptr) {
110  tx.get_message().back() |= MFC_AUTHENTICATE_PARAM_EMBED_KEY;
111  tx.get_message().insert(tx.get_message().end(), key, key + 6);
112  }
113 
114  ESP_LOGVV(TAG, "MFC_AUTHENTICATE_REQ: %s", nfc::format_bytes(tx.get_message()).c_str());
115  if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
116  ESP_LOGE(TAG, "Sending MFC_AUTHENTICATE_REQ failed");
117  return nfc::STATUS_FAILED;
118  }
119  if ((!rx.message_type_is(nfc::NCI_PKT_MT_DATA)) || (!rx.simple_status_response_is(MFC_AUTHENTICATE_OID)) ||
120  (rx.get_message()[4] != nfc::STATUS_OK)) {
121  ESP_LOGE(TAG, "MFC authentication failed - block 0x%02x", block_num);
122  ESP_LOGVV(TAG, "MFC_AUTHENTICATE_RSP: %s", nfc::format_bytes(rx.get_message()).c_str());
123  return nfc::STATUS_FAILED;
124  }
125 
126  ESP_LOGV(TAG, "MFC block %u authentication succeeded", block_num);
127  return nfc::STATUS_OK;
128 }
129 
130 uint8_t PN7160::sect_to_auth_(const uint8_t block_num) {
131  const uint8_t first_high_block = nfc::MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW * nfc::MIFARE_CLASSIC_16BLOCK_SECT_START;
132  if (block_num >= first_high_block) {
133  return ((block_num - first_high_block) / nfc::MIFARE_CLASSIC_BLOCKS_PER_SECT_HIGH) +
134  nfc::MIFARE_CLASSIC_16BLOCK_SECT_START;
135  }
136  return block_num / nfc::MIFARE_CLASSIC_BLOCKS_PER_SECT_LOW;
137 }
138 
140  std::vector<uint8_t> blank_buffer(
141  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
142  std::vector<uint8_t> trailer_buffer(
143  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
144 
145  auto status = nfc::STATUS_OK;
146 
147  for (int block = 0; block < 64; block += 4) {
148  if (this->auth_mifare_classic_block_(block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY) != nfc::STATUS_OK) {
149  continue;
150  }
151  if (block != 0) {
152  if (this->write_mifare_classic_block_(block, blank_buffer) != nfc::STATUS_OK) {
153  ESP_LOGE(TAG, "Unable to write block %u", block);
154  status = nfc::STATUS_FAILED;
155  }
156  }
157  if (this->write_mifare_classic_block_(block + 1, blank_buffer) != nfc::STATUS_OK) {
158  ESP_LOGE(TAG, "Unable to write block %u", block + 1);
159  status = nfc::STATUS_FAILED;
160  }
161  if (this->write_mifare_classic_block_(block + 2, blank_buffer) != nfc::STATUS_OK) {
162  ESP_LOGE(TAG, "Unable to write block %u", block + 2);
163  status = nfc::STATUS_FAILED;
164  }
165  if (this->write_mifare_classic_block_(block + 3, trailer_buffer) != nfc::STATUS_OK) {
166  ESP_LOGE(TAG, "Unable to write block %u", block + 3);
167  status = nfc::STATUS_FAILED;
168  }
169  }
170 
171  return status;
172 }
173 
175  std::vector<uint8_t> empty_ndef_message(
176  {0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
177  std::vector<uint8_t> blank_block(
178  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
179  std::vector<uint8_t> block_1_data(
180  {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1});
181  std::vector<uint8_t> block_2_data(
182  {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1});
183  std::vector<uint8_t> block_3_trailer(
184  {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
185  std::vector<uint8_t> ndef_trailer(
186  {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
187 
188  if (this->auth_mifare_classic_block_(0, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY) != nfc::STATUS_OK) {
189  ESP_LOGE(TAG, "Unable to authenticate block 0 for formatting");
190  return nfc::STATUS_FAILED;
191  }
192  if (this->write_mifare_classic_block_(1, block_1_data) != nfc::STATUS_OK) {
193  return nfc::STATUS_FAILED;
194  }
195  if (this->write_mifare_classic_block_(2, block_2_data) != nfc::STATUS_OK) {
196  return nfc::STATUS_FAILED;
197  }
198  if (this->write_mifare_classic_block_(3, block_3_trailer) != nfc::STATUS_OK) {
199  return nfc::STATUS_FAILED;
200  }
201 
202  ESP_LOGD(TAG, "Sector 0 formatted with NDEF");
203 
204  auto status = nfc::STATUS_OK;
205 
206  for (int block = 4; block < 64; block += 4) {
207  if (this->auth_mifare_classic_block_(block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY) != nfc::STATUS_OK) {
208  return nfc::STATUS_FAILED;
209  }
210  if (block == 4) {
211  if (this->write_mifare_classic_block_(block, empty_ndef_message) != nfc::STATUS_OK) {
212  ESP_LOGE(TAG, "Unable to write block %u", block);
213  status = nfc::STATUS_FAILED;
214  }
215  } else {
216  if (this->write_mifare_classic_block_(block, blank_block) != nfc::STATUS_OK) {
217  ESP_LOGE(TAG, "Unable to write block %u", block);
218  status = nfc::STATUS_FAILED;
219  }
220  }
221  if (this->write_mifare_classic_block_(block + 1, blank_block) != nfc::STATUS_OK) {
222  ESP_LOGE(TAG, "Unable to write block %u", block + 1);
223  status = nfc::STATUS_FAILED;
224  }
225  if (this->write_mifare_classic_block_(block + 2, blank_block) != nfc::STATUS_OK) {
226  ESP_LOGE(TAG, "Unable to write block %u", block + 2);
227  status = nfc::STATUS_FAILED;
228  }
229  if (this->write_mifare_classic_block_(block + 3, ndef_trailer) != nfc::STATUS_OK) {
230  ESP_LOGE(TAG, "Unable to write trailer block %u", block + 3);
231  status = nfc::STATUS_FAILED;
232  }
233  }
234  return status;
235 }
236 
237 uint8_t PN7160::write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &write_data) {
238  nfc::NciMessage rx;
239  nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {XCHG_DATA_OID, nfc::MIFARE_CMD_WRITE, block_num});
240 
241  ESP_LOGVV(TAG, "Write XCHG_DATA_REQ 1: %s", nfc::format_bytes(tx.get_message()).c_str());
242  if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
243  ESP_LOGE(TAG, "Sending XCHG_DATA_REQ failed");
244  return nfc::STATUS_FAILED;
245  }
246  // write command part two
247  tx.set_payload({XCHG_DATA_OID});
248  tx.get_message().insert(tx.get_message().end(), write_data.begin(), write_data.end());
249 
250  ESP_LOGVV(TAG, "Write XCHG_DATA_REQ 2: %s", nfc::format_bytes(tx.get_message()).c_str());
251  if (this->transceive_(tx, rx, NFCC_TAG_WRITE_TIMEOUT) != nfc::STATUS_OK) {
252  ESP_LOGE(TAG, "MFC XCHG_DATA timed out waiting for XCHG_DATA_RSP during block write");
253  return nfc::STATUS_FAILED;
254  }
255 
256  if ((!rx.message_type_is(nfc::NCI_PKT_MT_DATA)) || (!rx.simple_status_response_is(XCHG_DATA_OID)) ||
257  (rx.get_message()[4] != nfc::MIFARE_CMD_ACK)) {
258  ESP_LOGE(TAG, "MFC write block failed - block 0x%02x", block_num);
259  ESP_LOGV(TAG, "Write response: %s", nfc::format_bytes(rx.get_message()).c_str());
260  return nfc::STATUS_FAILED;
261  }
262 
263  return nfc::STATUS_OK;
264 }
265 
266 uint8_t PN7160::write_mifare_classic_tag_(const std::shared_ptr<nfc::NdefMessage> &message) {
267  auto encoded = message->encode();
268 
269  uint32_t message_length = encoded.size();
270  uint32_t buffer_length = nfc::get_mifare_classic_buffer_size(message_length);
271 
272  encoded.insert(encoded.begin(), 0x03);
273  if (message_length < 255) {
274  encoded.insert(encoded.begin() + 1, message_length);
275  } else {
276  encoded.insert(encoded.begin() + 1, 0xFF);
277  encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
278  encoded.insert(encoded.begin() + 3, message_length & 0xFF);
279  }
280  encoded.push_back(0xFE);
281 
282  encoded.resize(buffer_length, 0);
283 
284  uint32_t index = 0;
285  uint8_t current_block = 4;
286 
287  while (index < buffer_length) {
288  if (nfc::mifare_classic_is_first_block(current_block)) {
289  if (this->auth_mifare_classic_block_(current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY) != nfc::STATUS_OK) {
290  return nfc::STATUS_FAILED;
291  }
292  }
293 
294  std::vector<uint8_t> data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_CLASSIC_BLOCK_SIZE);
295  if (this->write_mifare_classic_block_(current_block, data) != nfc::STATUS_OK) {
296  return nfc::STATUS_FAILED;
297  }
298  index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
299  current_block++;
300 
301  if (nfc::mifare_classic_is_trailer_block(current_block)) {
302  // Skipping as cannot write to trailer
303  current_block++;
304  }
305  }
306  return nfc::STATUS_OK;
307 }
308 
310  nfc::NciMessage rx;
311  nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {XCHG_DATA_OID, nfc::MIFARE_CMD_HALT, 0});
312 
313  ESP_LOGVV(TAG, "Halt XCHG_DATA_REQ: %s", nfc::format_bytes(tx.get_message()).c_str());
314  if (this->transceive_(tx, rx, NFCC_TAG_WRITE_TIMEOUT) != nfc::STATUS_OK) {
315  ESP_LOGE(TAG, "Sending halt XCHG_DATA_REQ failed");
316  return nfc::STATUS_FAILED;
317  }
318  return nfc::STATUS_OK;
319 }
320 
321 } // namespace pn7160
322 } // namespace esphome
uint8_t sect_to_auth_(uint8_t block_num)
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
uint8_t read_mifare_classic_block_(uint8_t block_num, std::vector< uint8_t > &data)
void set_ndef_message(std::unique_ptr< NdefMessage > ndef_message)
Definition: nfc_tag.h:48
bool decode_mifare_classic_tlv(std::vector< uint8_t > &data, uint32_t &message_length, uint8_t &message_start_index)
Definition: nfc.cpp:55
bool message_type_is(uint8_t message_type) const
Definition: nci_message.cpp:71
uint8_t write_mifare_classic_block_(uint8_t block_num, std::vector< uint8_t > &data)
uint8_t write_mifare_classic_tag_(const std::shared_ptr< nfc::NdefMessage > &message)
uint8_t read_mifare_classic_tag_(nfc::NfcTag &tag)
std::vector< uint8_t > & get_message()
Definition: nci_message.cpp:67
uint8_t status
Definition: bl0942.h:74
bool simple_status_response_is(uint8_t response) const
bool mifare_classic_is_first_block(uint8_t block_num)
Definition: nfc.cpp:91
uint8_t transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, uint16_t timeout=NFCC_DEFAULT_TIMEOUT, bool expect_notification=true)
Definition: pn7160.cpp:1098
bool message_length_is(uint8_t message_length, bool recompute=false)
Definition: nci_message.cpp:78
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::string format_bytes(std::vector< uint8_t > &bytes)
Definition: nfc.cpp:22
uint8_t auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key)