1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtNfc module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "targetemulator_p.h" |
41 | |
42 | #include <QtCore/QSettings> |
43 | #include <QtCore/QDateTime> |
44 | |
45 | #include <QtCore/QDebug> |
46 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | TagBase::TagBase() |
50 | : lastAccess(0) |
51 | { |
52 | } |
53 | |
54 | TagBase::~TagBase() |
55 | { |
56 | } |
57 | |
58 | static inline quint8 blockByteToAddress(quint8 block, quint8 byte) |
59 | { |
60 | return ((block & 0x0f) << 3) | (byte & 0x07); |
61 | } |
62 | |
63 | NfcTagType1::NfcTagType1() |
64 | : hr0(0x11), hr1(0x00), memory(120, '\0') |
65 | { |
66 | // Locked blocks |
67 | memory[(0x0e << 3) | 0x00] = 0x01; |
68 | memory[(0x0e << 3) | 0x01] = 0x60; |
69 | } |
70 | |
71 | NfcTagType1::~NfcTagType1() |
72 | { |
73 | } |
74 | |
75 | void NfcTagType1::load(QSettings *settings) |
76 | { |
77 | settings->beginGroup(QStringLiteral("TagType1" )); |
78 | |
79 | hr0 = settings->value(QStringLiteral("HR0" ), defaultValue: 0x11).toUInt(); |
80 | |
81 | if (!(hr0 & 0x10)) { |
82 | settings->endGroup(); |
83 | return; |
84 | } |
85 | |
86 | hr1 = settings->value(QStringLiteral("HR1" ), defaultValue: 0x00).toUInt(); |
87 | |
88 | memory = settings->value(QStringLiteral("Data" )).toByteArray(); |
89 | |
90 | //quint8 nmn = memory.at(8); |
91 | |
92 | quint8 vno = memory.at(i: 9); |
93 | if (vno != 0x10) |
94 | qWarning(msg: "Only NFC TagType1 v1.0 behavior is supported." ); |
95 | |
96 | quint8 tms = memory.at(i: 10); |
97 | if (memory.length() != 8 * (tms + 1)) |
98 | qWarning(msg: "Static memory size does not match TMS value." ); |
99 | |
100 | quint8 rwa = memory.at(i: 11); |
101 | switch (rwa >> 4) { |
102 | case 0: |
103 | // Unrestricted read access tag |
104 | break; |
105 | default: |
106 | // tag with unknown read attributes |
107 | ; |
108 | } |
109 | |
110 | switch (rwa & 0x0f) { |
111 | case 0: |
112 | // Unrestricted write access tag |
113 | break; |
114 | case 0x0f: |
115 | // Read only tag |
116 | break; |
117 | default: |
118 | // tag with unknown write attributes |
119 | ; |
120 | } |
121 | |
122 | //quint16 lock = (quint8(memory[blockByteToAddress(0x0e, 1)]) << 8) | |
123 | // quint8(memory[blockByteToAddress(0x0e, 0)]); |
124 | |
125 | settings->endGroup(); |
126 | } |
127 | |
128 | QByteArray NfcTagType1::uid() const |
129 | { |
130 | lastAccess = QDateTime::currentMSecsSinceEpoch(); |
131 | |
132 | return memory.left(len: 7); |
133 | } |
134 | |
135 | quint8 NfcTagType1::readData(quint8 block, quint8 byte) |
136 | { |
137 | return memory.at(i: (block << 3) | byte); |
138 | } |
139 | |
140 | QByteArray NfcTagType1::processCommand(const QByteArray &command) |
141 | { |
142 | lastAccess = QDateTime::currentMSecsSinceEpoch(); |
143 | |
144 | QByteArray response; |
145 | |
146 | bool tagType1 = (hr0 & 0xf0) == 0x10; |
147 | bool dynamic = (hr0 & 0x0f) != 0x01; |
148 | |
149 | if (command.length() == 9) { |
150 | // static memory model command |
151 | quint8 opcode = command.at(i: 0); |
152 | quint8 address = command.at(i: 1); |
153 | quint8 data = command.at(i: 2); |
154 | QByteArray uid = command.mid(index: 3, len: 4); |
155 | |
156 | // check checksum |
157 | if (qChecksum(s: command.constData(), len: command.length(), standard: Qt::ChecksumItuV41) != 0) |
158 | return QByteArray(); |
159 | |
160 | // check UID |
161 | if (uid != memory.left(len: 4)) |
162 | return QByteArray(); |
163 | |
164 | switch (opcode) { |
165 | case 0x00: // RALL |
166 | response.append(c: hr0); |
167 | response.append(c: hr1); |
168 | response.append(a: memory.left(len: 120)); |
169 | break; |
170 | case 0x01: // READ |
171 | response.append(c: address); |
172 | if (address & 0x80) |
173 | response.append(c: char(0x00)); |
174 | else |
175 | response.append(c: memory.at(i: address)); |
176 | break; |
177 | case 0x53: { // WRITE-E |
178 | quint8 block = address >> 3; |
179 | if (block == 0x00 || block == 0x0d || block == 0x0e) // locked blocks |
180 | break; |
181 | |
182 | quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00); |
183 | if ((0x01 << block) & lock) // locked blocks |
184 | break; |
185 | |
186 | // FIXME: Test dynamic lock bytes |
187 | |
188 | memory[address] = data; |
189 | |
190 | response.append(c: address); |
191 | response.append(c: data); |
192 | break; |
193 | } |
194 | case 0x1a: { // WRITE-NE |
195 | quint8 block = address >> 3; |
196 | if (block == 0x00 || block == 0x0d) // locked blocks |
197 | break; |
198 | |
199 | quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00); |
200 | if ((0x01 << block) & lock) // locked blocks |
201 | break; |
202 | |
203 | |
204 | // FIXME: Test dynamic lock bytes |
205 | |
206 | memory[address] = memory.at(i: address) | data; |
207 | |
208 | response.append(c: address); |
209 | response.append(c: memory.at(i: address)); |
210 | break; |
211 | } |
212 | case 0x78: // RID |
213 | response.append(c: hr0); |
214 | response.append(c: hr1); |
215 | response.append(a: memory.left(len: 4)); |
216 | break; |
217 | } |
218 | } else if (tagType1 && dynamic && command.length() == 16) { |
219 | // dynamic memory model command |
220 | quint8 opcode = command.at(i: 0); |
221 | quint8 address = command.at(i: 1); |
222 | QByteArray data = command.mid(index: 2, len: 8); |
223 | QByteArray uid = command.mid(index: 10, len: 4); |
224 | |
225 | // check checksum |
226 | if (qChecksum(s: command.constData(), len: command.length(), standard: Qt::ChecksumItuV41) != 0) |
227 | return QByteArray(); |
228 | |
229 | // check UID |
230 | if (uid != memory.left(len: 4)) |
231 | return QByteArray(); |
232 | |
233 | switch (opcode) { |
234 | case 0x10: // RSEG |
235 | response.append(c: address); |
236 | response.append(a: memory.mid(index: 128 * (address >> 4), len: 128)); |
237 | break; |
238 | case 0x02: // READ8 |
239 | response.append(c: address); |
240 | response.append(a: memory.mid(index: 8 * address, len: 8)); |
241 | break; |
242 | case 0x54: { // WRITE-E8 |
243 | // locked blocks |
244 | if (address == 0x00 || address == 0x0d || address == 0x0e || address == 0x0f) |
245 | break; |
246 | |
247 | quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00); |
248 | if (address <= 0x0e && ((0x01 << address) & lock)) // locked blocks |
249 | break; |
250 | |
251 | // FIXME: Test dynamic lock bytes |
252 | |
253 | memory.replace(index: address * 8, len: 8, s: data); |
254 | |
255 | response.append(c: address); |
256 | response.append(a: memory.mid(index: address * 8, len: 8)); |
257 | break; |
258 | } |
259 | case 0x1b: // WRITE-NE8 |
260 | // locked blocks |
261 | if (address == 0x00 || address == 0x0d || address == 0x0e || address == 0x0f) |
262 | break; |
263 | |
264 | quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00); |
265 | if (address <= 0x0e && ((0x01 << address) & lock)) // locked blocks |
266 | break; |
267 | |
268 | // FIXME: Test dynamic lock bytes |
269 | |
270 | for (int i = 0; i < 8; ++i) |
271 | memory[address * 8 + i] = memory.at(i: address * 8 + i) | data.at(i); |
272 | |
273 | response.append(c: address); |
274 | response.append(a: memory.mid(index: address * 8, len: 8)); |
275 | break; |
276 | } |
277 | } |
278 | |
279 | if (!response.isEmpty()) { |
280 | quint16 crc = qChecksum(s: response.constData(), len: response.length(), standard: Qt::ChecksumItuV41); |
281 | response.append(c: quint8(crc & 0xff)); |
282 | response.append(c: quint8(crc >> 8)); |
283 | } |
284 | |
285 | return response; |
286 | } |
287 | |
288 | |
289 | NfcTagType2::NfcTagType2() |
290 | : memory(64, 0x00), currentSector(0), expectPacket2(false) |
291 | { |
292 | } |
293 | |
294 | NfcTagType2::~NfcTagType2() |
295 | { |
296 | } |
297 | |
298 | void NfcTagType2::load(QSettings *settings) |
299 | { |
300 | settings->beginGroup(QStringLiteral("TagType2" )); |
301 | |
302 | memory = settings->value(QStringLiteral("Data" )).toByteArray(); |
303 | |
304 | settings->endGroup(); |
305 | } |
306 | |
307 | QByteArray NfcTagType2::uid() const |
308 | { |
309 | lastAccess = QDateTime::currentMSecsSinceEpoch(); |
310 | |
311 | return memory.left(len: 3) + memory.mid(index: 4, len: 4); |
312 | } |
313 | |
314 | #define NACK QByteArray("\x05") |
315 | #define ACK QByteArray("\x0a") |
316 | |
317 | QByteArray NfcTagType2::processCommand(const QByteArray &command) |
318 | { |
319 | lastAccess = QDateTime::currentMSecsSinceEpoch(); |
320 | |
321 | QByteArray response; |
322 | |
323 | // check checksum |
324 | if (qChecksum(s: command.constData(), len: command.length(), standard: Qt::ChecksumItuV41) != 0) |
325 | return QByteArray(); |
326 | |
327 | if (expectPacket2) { |
328 | expectPacket2 = false; |
329 | quint8 sector = command.at(i: 0); |
330 | if (sector * 1024 > memory.length()) |
331 | return NACK; |
332 | else { |
333 | currentSector = sector; |
334 | return QByteArray(); |
335 | } |
336 | } |
337 | |
338 | quint8 opcode = command.at(i: 0); |
339 | |
340 | switch (opcode) { |
341 | case 0x30: { // READ BLOCK |
342 | quint8 block = command.at(i: 1); |
343 | int absoluteBlock = currentSector * 256 + block; |
344 | |
345 | response.append(a: memory.mid(index: absoluteBlock * 4, len: 16)); |
346 | if (response.length() != 16) |
347 | response.append(a: QByteArray(16 - response.length(), '\0')); |
348 | |
349 | break; |
350 | } |
351 | case 0xa2: { // WRITE BLOCK |
352 | quint8 block = command.at(i: 1); |
353 | int absoluteBlock = currentSector * 256 + block; |
354 | |
355 | // locked blocks |
356 | if (absoluteBlock == 0 || absoluteBlock == 1) |
357 | return NACK; |
358 | |
359 | const QByteArray data = command.mid(index: 2, len: 4); |
360 | |
361 | memory.replace(index: absoluteBlock * 4, len: 4, s: data); |
362 | |
363 | return ACK; |
364 | } |
365 | case 0xc2: // SECTOR SELECT - Packet 1 |
366 | if (memory.length() > 1024) { |
367 | expectPacket2 = true; |
368 | return ACK; |
369 | } |
370 | |
371 | return NACK; |
372 | default: |
373 | qDebug() << "Unknown opcode for Tag Type 2" << hex << opcode; |
374 | qDebug() << "command:" << command.toHex(); |
375 | |
376 | return NACK; |
377 | ; |
378 | } |
379 | |
380 | if (!response.isEmpty()) { |
381 | quint16 crc = qChecksum(s: response.constData(), len: response.length(), standard: Qt::ChecksumItuV41); |
382 | response.append(c: quint8(crc & 0xff)); |
383 | response.append(c: quint8(crc >> 8)); |
384 | } |
385 | |
386 | return response; |
387 | } |
388 | |
389 | QT_END_NAMESPACE |
390 | |