ESPHome 2025.5.0
Loading...
Searching...
No Matches
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
6namespace esphome {
7namespace pn7160 {
8
9static 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
69uint8_t PN7160::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data) {
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
92uint8_t PN7160::auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key) {
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
130uint8_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
237uint8_t PN7160::write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &write_data) {
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
266uint8_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
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 status
Definition bl0942.h:8
bool message_type_is(uint8_t message_type) const
bool simple_status_response_is(uint8_t response) const
std::vector< uint8_t > & get_message()
bool message_length_is(uint8_t message_length, bool recompute=false)
void set_ndef_message(std::unique_ptr< NdefMessage > ndef_message)
Definition nfc_tag.h:48
uint8_t auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key)
uint8_t read_mifare_classic_tag_(nfc::NfcTag &tag)
uint8_t sect_to_auth_(uint8_t block_num)
uint8_t transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, uint16_t timeout=NFCC_DEFAULT_TIMEOUT, bool expect_notification=true)
Definition pn7160.cpp:1098
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_block_(uint8_t block_num, std::vector< uint8_t > &data)
bool decode_mifare_classic_tlv(std::vector< uint8_t > &data, uint32_t &message_length, uint8_t &message_start_index)
Definition nfc.cpp:55
bool mifare_classic_is_trailer_block(uint8_t block_num)
Definition nfc.cpp:99
uint32_t get_mifare_classic_buffer_size(uint32_t message_length)
Definition nfc.cpp:78
bool mifare_classic_is_first_block(uint8_t block_num)
Definition nfc.cpp:91
std::string format_bytes(std::vector< uint8_t > &bytes)
Definition nfc.cpp:22
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7