ESPHome 2026.5.0b1
Loading...
Searching...
No Matches
pn532_mifare_classic.cpp
Go to the documentation of this file.
1#include <array>
2#include <memory>
3
4#include "pn532.h"
5#include "esphome/core/log.h"
6
7namespace esphome::pn532 {
8
9static const char *const TAG = "pn532.mifare_classic";
10
11std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(nfc::NfcTagUid &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
65bool 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 char data_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
81 ESP_LOGVV(TAG, " Block %d: %s", block_num, nfc::format_bytes_to(data_buf, data));
82 return true;
83}
84
85bool PN532::auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, uint8_t key_num, 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
109 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> BLANK_BUFFER = {
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
111 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> 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.data(), BLANK_BUFFER.size())) {
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.data(), BLANK_BUFFER.size())) {
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.data(), BLANK_BUFFER.size())) {
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.data(), TRAILER_BUFFER.size())) {
135 ESP_LOGE(TAG, "Unable to write block %d", block + 3);
136 error = true;
137 }
138 }
139
140 return !error;
141}
142
144 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> EMPTY_NDEF_MESSAGE = {
145 0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
146 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> BLANK_BLOCK = {
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
148 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> BLOCK_1_DATA = {
149 0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1};
150 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> BLOCK_2_DATA = {
151 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1};
152 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> BLOCK_3_TRAILER = {
153 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
154 static constexpr std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> 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.data(), BLOCK_1_DATA.size()))
162 return false;
163 if (!this->write_mifare_classic_block_(2, BLOCK_2_DATA.data(), BLOCK_2_DATA.size()))
164 return false;
165 if (!this->write_mifare_classic_block_(3, BLOCK_3_TRAILER.data(), BLOCK_3_TRAILER.size()))
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.data(), EMPTY_NDEF_MESSAGE.size())) {
176 ESP_LOGE(TAG, "Unable to write block %d", block);
177 }
178 } else {
179 if (!this->write_mifare_classic_block_(block, BLANK_BLOCK.data(), BLANK_BLOCK.size())) {
180 ESP_LOGE(TAG, "Unable to write block %d", block);
181 }
182 }
183 if (!this->write_mifare_classic_block_(block + 1, BLANK_BLOCK.data(), BLANK_BLOCK.size())) {
184 ESP_LOGE(TAG, "Unable to write block %d", block + 1);
185 }
186 if (!this->write_mifare_classic_block_(block + 2, BLANK_BLOCK.data(), BLANK_BLOCK.size())) {
187 ESP_LOGE(TAG, "Unable to write block %d", block + 2);
188 }
189 if (!this->write_mifare_classic_block_(block + 3, NDEF_TRAILER.data(), NDEF_TRAILER.size())) {
190 ESP_LOGE(TAG, "Unable to write trailer block %d", block + 3);
191 }
192 }
193 return true;
194}
195
196bool PN532::write_mifare_classic_block_(uint8_t block_num, const uint8_t *data, size_t len) {
197 std::vector<uint8_t> cmd({
198 PN532_COMMAND_INDATAEXCHANGE,
199 0x01, // One card
200 nfc::MIFARE_CMD_WRITE,
201 block_num,
202 });
203 cmd.insert(cmd.end(), data, data + len);
204 if (!this->write_command_(cmd)) {
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
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 if (!this->write_mifare_classic_block_(current_block, encoded.data() + index, nfc::MIFARE_CLASSIC_BLOCK_SIZE)) {
247 return false;
248 }
249 index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
250 current_block++;
251
252 if (nfc::mifare_classic_is_trailer_block(current_block)) {
253 // Skipping as cannot write to trailer
254 current_block++;
255 }
256 }
257 return true;
258}
259
260} // namespace esphome::pn532
bool auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key)
bool format_mifare_classic_mifare_(nfc::NfcTagUid &uid)
virtual bool read_response(uint8_t command, std::vector< uint8_t > &data)=0
bool format_mifare_classic_ndef_(nfc::NfcTagUid &uid)
bool write_mifare_classic_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message)
std::unique_ptr< nfc::NfcTag > read_mifare_classic_tag_(nfc::NfcTagUid &uid)
bool read_mifare_classic_block_(uint8_t block_num, std::vector< uint8_t > &data)
bool write_command_(const std::vector< uint8_t > &data)
Definition pn532.cpp:243
bool write_mifare_classic_block_(uint8_t block_num, const uint8_t *data, size_t len)
const char * message
Definition component.cpp:35
bool decode_mifare_classic_tlv(std::vector< uint8_t > &data, uint32_t &message_length, uint8_t &message_start_index)
Definition nfc.cpp:50
char * format_bytes_to(char *buffer, std::span< const uint8_t > bytes)
Format bytes to buffer with ' ' separator (e.g., "04 11 22 33"). Returns buffer for inline use.
Definition nfc.cpp:14
bool mifare_classic_is_trailer_block(uint8_t block_num)
Definition nfc.cpp:102
uint32_t get_mifare_classic_buffer_size(uint32_t message_length)
Definition nfc.cpp:81
bool mifare_classic_is_first_block(uint8_t block_num)
Definition nfc.cpp:94
std::string size_t len
static void uint32_t