textual
[RRG-proxmark3.git] / client / src / nfc / ndef.c
blob2bc841a233893bea275639e558e167b4102e8da8
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2019 Merlok
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // NFC Data Exchange Format (NDEF) functions
9 //-----------------------------------------------------------------------------
11 #include "ndef.h"
13 #include <string.h>
15 #include "ui.h"
16 #include "util.h" // sprint_hex...
17 #include "crypto/asn1utils.h"
18 #include "crypto/libpcrypto.h"
19 #include "ecp.h"
20 #include "commonutil.h" // ARRAYLEN
21 #include "pm3_cmd.h"
23 #define STRBOOL(p) ((p) ? "1" : "0")
25 #define NDEF_WIFIAPPL "application/vnd.wfa"
26 #define NDEF_BLUEAPPL "application/vnd.bluetooth"
27 #define NDEF_VCARDTEXT "text/vcard"
29 static const char *TypeNameFormat_s[] = {
30 "Empty Record",
31 "Well Known Record",
32 "MIME Media Record",
33 "Absolute URI Record",
34 "External Record",
35 "Unknown Record",
36 "Unchanged Record",
37 "n/a"
40 static const char *ndefSigType_s[] = {
41 "Not present", // No signature present
42 "RSASSA_PSS_SHA_1 (1024)", // PKCS_1
43 "RSASSA_PKCS1_v1_5_WITH_SHA_1 (1024)", // PKCS_1
44 "DSA-1024",
45 "ECDSA-P192",
46 "RSASSA-PSS-2048",
47 "RSASSA-PKCS1-v1_5-2048",
48 "DSA-2048",
49 "ECDSA-P224",
50 "ECDSA-K233",
51 "ECDSA-B233",
52 "ECDSA-P256",
53 "n/a"
56 static const char *ndefCertificateFormat_s[] = {
57 "X_509",
58 "X9_68 (M2M)",
59 "n/a"
62 static const char *URI_s[] = {
63 "", // 0x00
64 "http://www.", // 0x01
65 "https://www.", // 0x02
66 "http://", // 0x03
67 "https://", // 0x04
68 "tel:", // 0x05
69 "mailto:", // 0x06
70 "ftp://anonymous:anonymous@", // 0x07
71 "ftp://ftp.", // 0x08
72 "ftps://", // 0x09
73 "sftp://", // 0x0A
74 "smb://", // 0x0B
75 "nfs://", // 0x0C
76 "ftp://", // 0x0D
77 "dav://", // 0x0E
78 "news:", // 0x0F
79 "telnet://", // 0x10
80 "imap:", // 0x11
81 "rtsp://", // 0x12
82 "urn:", // 0x13
83 "pop:", // 0x14
84 "sip:", // 0x15
85 "sips:", // 0x16
86 "tftp:", // 0x17
87 "btspp://", // 0x18
88 "btl2cap://", // 0x19
89 "btgoep://", // 0x1A
90 "tcpobex://", // 0x1B
91 "irdaobex://", // 0x1C
92 "file://", // 0x1D
93 "urn:epc:id:", // 0x1E
94 "urn:epc:tag:", // 0x1F
95 "urn:epc:pat:", // 0x20
96 "urn:epc:raw:", // 0x21
97 "urn:epc:", // 0x22
98 "urn:nfc:" // 0x23
101 static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen);
102 static int ndefDecodePayload(NDEFHeader_t *ndef);
104 static uint16_t ndefTLVGetLength(uint8_t *data, size_t *indx) {
105 uint16_t len = 0;
106 if (data[0] == 0xff) {
107 len = (data[1] << 8) + data[2];
108 *indx += 3;
109 } else {
110 len = data[0];
111 *indx += 1;
114 return len;
117 static int ndefDecodeHeader(uint8_t *data, size_t datalen, NDEFHeader_t *header) {
118 header->Type = NULL;
119 header->Payload = NULL;
120 header->ID = NULL;
122 header->MessageBegin = data[0] & 0x80;
123 header->MessageEnd = data[0] & 0x40;
124 header->ChunkFlag = data[0] & 0x20;
125 header->ShortRecordBit = data[0] & 0x10;
126 header->IDLenPresent = data[0] & 0x08;
127 header->TypeNameFormat = data[0] & 0x07;
128 header->len = 1 + 1 + (header->ShortRecordBit ? 1 : 4) + (header->IDLenPresent ? 1 : 0); // header + typelen + payloadlen + idlen
129 if (header->len > datalen)
130 return PM3_ESOFT;
132 header->TypeLen = data[1];
133 header->Type = data + header->len;
135 header->PayloadLen = (header->ShortRecordBit ? (data[2]) : ((data[2] << 24) + (data[3] << 16) + (data[4] << 8) + data[5]));
137 if (header->IDLenPresent) {
138 header->IDLen = (header->ShortRecordBit ? (data[3]) : (data[6]));
139 header->ID = data + header->len + header->TypeLen;
140 } else {
141 header->IDLen = 0;
144 header->Payload = header->Type + header->TypeLen + header->IDLen;
146 header->RecLen = header->len + header->TypeLen + header->PayloadLen + header->IDLen;
148 if (header->RecLen > datalen)
149 return PM3_ESOFT;
151 return PM3_SUCCESS;
154 static int ndefPrintHeader(NDEFHeader_t *header) {
155 PrintAndLogEx(INFO, _CYAN_("Header info"));
157 PrintAndLogEx(SUCCESS, " %s ....... Message begin", STRBOOL(header->MessageBegin));
158 PrintAndLogEx(SUCCESS, " %s ...... Message end", STRBOOL(header->MessageEnd));
159 PrintAndLogEx(SUCCESS, " %s ..... Chunk flag", STRBOOL(header->ChunkFlag));
160 PrintAndLogEx(SUCCESS, " %s .... Short record bit", STRBOOL(header->ShortRecordBit));
161 PrintAndLogEx(SUCCESS, " %s ... ID Len present", STRBOOL(header->IDLenPresent));
162 PrintAndLogEx(SUCCESS, "");
164 PrintAndLogEx(SUCCESS, " Header length...... %zu", header->len);
165 PrintAndLogEx(SUCCESS, " Type length........ %zu", header->TypeLen);
166 PrintAndLogEx(SUCCESS, " Payload length..... %zu", header->PayloadLen);
167 PrintAndLogEx(SUCCESS, " ID length.......... %zu", header->IDLen);
168 PrintAndLogEx(SUCCESS, " Record length...... %zu", header->RecLen);
170 PrintAndLogEx(SUCCESS, " Type name format... [ 0x%02x ] " _YELLOW_("%s"), header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]);
171 return PM3_SUCCESS;
174 static const char *get_curve_name(mbedtls_ecp_group_id grp_id) {
175 switch (grp_id) {
176 case MBEDTLS_ECP_DP_NONE:
177 return "";
178 case MBEDTLS_ECP_DP_SECP192R1:
179 return "SECP192R1"; // Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1
180 case MBEDTLS_ECP_DP_SECP224R1:
181 return "SECP224R1"; // Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1
182 case MBEDTLS_ECP_DP_SECP256R1:
183 return "SECP256R1"; // Domain parameters for the 256-bit curve defined by FIPS 186-4 and SEC1
184 case MBEDTLS_ECP_DP_SECP384R1:
185 return "SECP384R1"; // Domain parameters for the 384-bit curve defined by FIPS 186-4 and SEC1
186 case MBEDTLS_ECP_DP_SECP521R1:
187 return "SECP521R1"; // Domain parameters for the 521-bit curve defined by FIPS 186-4 and SEC1
188 case MBEDTLS_ECP_DP_BP256R1:
189 return "BP256R1"; // Domain parameters for 256-bit Brainpool curve
190 case MBEDTLS_ECP_DP_BP384R1:
191 return "BP384R1"; // Domain parameters for 384-bit Brainpool curve
192 case MBEDTLS_ECP_DP_BP512R1:
193 return "BP512R1"; // Domain parameters for 512-bit Brainpool curve
194 case MBEDTLS_ECP_DP_CURVE25519:
195 return "CURVE25519"; // Domain parameters for Curve25519
196 case MBEDTLS_ECP_DP_SECP192K1:
197 return "SECP192K1"; // Domain parameters for 192-bit "Koblitz" curve
198 case MBEDTLS_ECP_DP_SECP224K1:
199 return "SECP224K1"; // Domain parameters for 224-bit "Koblitz" curve
200 case MBEDTLS_ECP_DP_SECP256K1:
201 return "SECP256K1"; // Domain parameters for 256-bit "Koblitz" curve
202 case MBEDTLS_ECP_DP_CURVE448:
203 return "CURVE448"; // Domain parameters for Curve448
204 case MBEDTLS_ECP_DP_SECP128R1:
205 return "SECP128R1"; // Domain parameters for the 128-bit curve used for NXP originality check
206 default :
207 return "";
209 return "";
212 typedef struct {
213 mbedtls_ecp_group_id grp_id;
214 uint8_t keylen;
215 const char *desc;
216 const char *value;
217 } PACKED ndef_publickey_t;
219 static int ndef_print_signature(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t sign_len) {
221 const ndef_publickey_t ndef_public_keys[] = {
222 { MBEDTLS_ECP_DP_SECP256R1, 65, "Minecraft Earth", "04760200b60315f31ff7951d0892b87930c34967dfbf57763afc775fc56a22b601f7b8fd9e47519524505322435b07d0782463f39400a39a9dbc06bab2225c082a"},
225 uint8_t i;
226 int reason = 0;
227 bool is_valid = false;
228 for (i = 0; i < ARRAYLEN(ndef_public_keys); i++) {
230 int dl = 0;
231 uint8_t key[ndef_public_keys[i].keylen];
232 param_gethex_to_eol(ndef_public_keys[i].value, 0, key, ndef_public_keys[i].keylen, &dl);
234 int res = ecdsa_signature_r_s_verify(ndef_public_keys[i].grp_id, key, data, data_len, signature, sign_len, false);
235 is_valid = (res == 0);
236 if (is_valid) {
237 reason = 1;
238 break;
241 // try with sha256
242 res = ecdsa_signature_r_s_verify(ndef_public_keys[i].grp_id, key, data, data_len, signature, sign_len, true);
243 is_valid = (res == 0);
244 if (is_valid) {
245 reason = 2;
246 break;
250 PrintAndLogEx(NORMAL, "");
251 PrintAndLogEx(INFO, "--- " _CYAN_("NDEF Signature"));
252 if (is_valid == false || i == ARRAYLEN(ndef_public_keys)) {
253 PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32));
254 PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
255 return PM3_ESOFT;
258 PrintAndLogEx(INFO, " IC signature public key name: %s", ndef_public_keys[i].desc);
259 PrintAndLogEx(INFO, "IC signature public key value: %s", ndef_public_keys[i].value);
260 PrintAndLogEx(INFO, " Elliptic curve parameters: %s", get_curve_name(ndef_public_keys[i].grp_id));
261 PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32));
262 PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
263 switch (reason) {
264 case 1:
265 PrintAndLogEx(INFO, " Params used: signature, plain");
266 break;
267 case 2:
268 PrintAndLogEx(INFO, " Params used: signature, SHA256");
269 break;
271 return PM3_SUCCESS;
274 static int ndefDecodeSig1(uint8_t *sig, size_t siglen) {
275 size_t indx = 1;
277 uint8_t sigType = sig[indx] & 0x7f;
278 bool sigURI = sig[indx] & 0x80;
280 PrintAndLogEx(SUCCESS, "\tsignature type: %s", ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA]));
281 PrintAndLogEx(SUCCESS, "\tsignature uri: %s", (sigURI ? "present" : "not present"));
283 size_t intsiglen = (sig[indx + 1] << 8) + sig[indx + 2];
284 // ecdsa 0x04
285 if (sigType == stECDSA_P192 || sigType == stECDSA_P256) {
286 indx += 3;
287 int slen = 24;
288 if (sigType == stECDSA_P256)
289 slen = 32;
290 PrintAndLogEx(SUCCESS, "\tsignature [%zu]: %s", intsiglen, sprint_hex_inrow(&sig[indx], intsiglen));
292 uint8_t rval[300] = {0};
293 uint8_t sval[300] = {0};
294 int res = ecdsa_asn1_get_signature(&sig[indx], intsiglen, rval, sval);
295 if (res == PM3_SUCCESS) {
296 PrintAndLogEx(SUCCESS, "\t\tr: %s", sprint_hex(rval + 32 - slen, slen));
297 PrintAndLogEx(SUCCESS, "\t\ts: %s", sprint_hex(sval + 32 - slen, slen));
300 indx += intsiglen;
302 if (sigURI) {
303 size_t intsigurilen = (sig[indx] << 8) + sig[indx + 1];
304 indx += 2;
305 PrintAndLogEx(SUCCESS, "\tsignature uri [%zu]: %.*s", intsigurilen, (int)intsigurilen, &sig[indx]);
306 indx += intsigurilen;
309 uint8_t certFormat = (sig[indx] >> 4) & 0x07;
310 uint8_t certCount = sig[indx] & 0x0f;
311 bool certURI = sig[indx] & 0x80;
313 PrintAndLogEx(SUCCESS, "\tcertificate format: %s", ((certFormat < sfNA) ? ndefCertificateFormat_s[certFormat] : ndefCertificateFormat_s[sfNA]));
314 PrintAndLogEx(SUCCESS, "\tcertificates count: %d", certCount);
316 // print certificates
317 indx++;
318 for (int i = 0; i < certCount; i++) {
319 size_t intcertlen = (sig[indx + 1] << 8) + sig[indx + 2];
320 indx += 2;
322 PrintAndLogEx(SUCCESS, "\tcertificate %d [%zu]: %s", i + 1, intcertlen, sprint_hex_inrow(&sig[indx], intcertlen));
323 indx += intcertlen;
326 // have certificate uri
327 if ((indx <= siglen) && certURI) {
328 size_t inturilen = (sig[indx] << 8) + sig[indx + 1];
329 indx += 2;
330 PrintAndLogEx(SUCCESS, "\tcertificate uri [%zu]: %.*s", inturilen, (int)inturilen, &sig[indx]);
333 return PM3_SUCCESS;
336 // https://github.com/nfcpy/ndeflib/blob/master/src/ndef/signature.py#L292
337 static int ndefDecodeSig2(uint8_t *sig, size_t siglen) {
338 size_t indx = 1;
340 uint8_t sigType = sig[indx] & 0x7f;
341 bool sigURI = sig[indx] & 0x80;
342 indx++;
344 uint8_t hashType = sig[indx];
345 indx++;
347 PrintAndLogEx(SUCCESS, "\tsignature type :\t" _GREEN_("%s"), ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA]));
348 PrintAndLogEx(SUCCESS, "\tsignature uri :\t\t%s", (sigURI ? "present" : "not present"));
349 PrintAndLogEx(SUCCESS, "\thash type :\t\t%s", ((hashType == 0x02) ? _GREEN_("SHA-256") : _RED_("unknown")));
351 size_t intsiglen = (sig[indx] << 8) + sig[indx + 1];
352 indx += 2;
354 if (sigURI) {
355 indx += 2;
356 PrintAndLogEx(SUCCESS, "\tsignature uri [%zu]: %.*s", intsiglen, (int)intsiglen, &sig[indx]);
357 indx += intsiglen;
358 } else {
359 PrintAndLogEx(SUCCESS, "\tsignature [%zu]: %s", intsiglen, sprint_hex_inrow(&sig[indx], intsiglen));
360 if (sigType == stECDSA_P192 || sigType == stECDSA_P256) {
361 int slen = intsiglen / 2;
362 if (slen == 24 || slen == 32) {
363 PrintAndLogEx(SUCCESS, "\tsignature : " _GREEN_("ECDSA-%d"), slen * 8);
364 PrintAndLogEx(SUCCESS, "\t\tr: %s", sprint_hex(&sig[indx], slen));
365 PrintAndLogEx(SUCCESS, "\t\ts: %s", sprint_hex(&sig[indx + slen], slen));
367 ndef_print_signature(NULL, 0, NULL, 0);
369 } else {
370 PrintAndLogEx(INFO, "\tsignature: unknown type");
372 indx += intsiglen;
375 uint8_t certFormat = (sig[indx] >> 4) & 0x07;
376 uint8_t certCount = sig[indx] & 0x0f;
377 bool certURI = sig[indx] & 0x80;
379 PrintAndLogEx(SUCCESS, "\tcertificate format : " _GREEN_("%s"), ((certFormat < sfNA) ? ndefCertificateFormat_s[certFormat] : ndefCertificateFormat_s[sfNA]));
380 PrintAndLogEx(SUCCESS, "\tcertificates count : %d", certCount);
382 // print certificates
383 indx++;
384 for (int i = 0; i < certCount; i++) {
385 size_t intcertlen = (sig[indx + 1] << 8) + sig[indx + 2];
386 indx += 2;
388 PrintAndLogEx(SUCCESS, "\tcertificate %d [%zu]: %s", i + 1, intcertlen, sprint_hex_inrow(&sig[indx], intcertlen));
389 indx += intcertlen;
392 // have certificate uri
393 if ((indx <= siglen) && certURI) {
394 size_t inturilen = (sig[indx] << 8) + sig[indx + 1];
395 indx += 2;
396 PrintAndLogEx(SUCCESS, "\tcertificate uri [%zu]: %.*s", inturilen, (int)inturilen, &sig[indx]);
399 return PM3_SUCCESS;
402 static int ndefDecodeSig(uint8_t *sig, size_t siglen) {
403 PrintAndLogEx(SUCCESS, "\tsignature version : \t" _GREEN_("0x%02x"), sig[0]);
404 if (sig[0] != 0x01 && sig[0] != 0x20) {
405 PrintAndLogEx(ERR, "signature version unknown.");
406 return PM3_ESOFT;
409 if (sig[0] == 0x01)
410 return ndefDecodeSig1(sig, siglen);
412 if (sig[0] == 0x20)
413 return ndefDecodeSig2(sig, siglen);
415 return PM3_ESOFT;
418 static int ndefDecodePayloadDeviceInfo(uint8_t *payload, size_t len) {
419 if (payload == NULL)
420 return PM3_EINVARG;
421 if (len < 1)
422 return PM3_EINVARG;
424 PrintAndLogEx(INFO, _CYAN_("Device information"));
425 uint8_t *p = payload;
426 p++;
427 uint8_t n = *(p++);
428 PrintAndLogEx(INFO, "Vendor........ " _YELLOW_("%.*s"), n, p);
429 p += n + 1;
430 n = *(p++);
431 PrintAndLogEx(INFO, "Model......... " _YELLOW_("%.*s"), n, p);
432 p += n + 1;
433 n = *(p++);
434 PrintAndLogEx(INFO, "Unique name... " _YELLOW_("%.*s"), n, p);
435 p += n + 1;
436 n = *(p++);
437 //uuid string
438 // record.uuid_string = '123e4567-e89b-12d3-a456-426655440000'
439 // 8-4-4-4-12
440 char uuid[37] = {0};
441 sprintf(uuid, "%s-", sprint_hex_inrow(p, 4));
442 p += 4;
443 sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2));
444 p += 2;
445 sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2));
446 p += 2;
447 sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2));
448 p += 2;
449 sprintf(uuid + strlen(uuid), "%s", sprint_hex_inrow(p, 6));
450 p += 6;
451 PrintAndLogEx(INFO, "UUID.......... " _YELLOW_("%s"), uuid);
452 p++;
453 n = *(p++);
454 PrintAndLogEx(INFO, "Version....... " _YELLOW_("%.*s"), n, p);
455 PrintAndLogEx(NORMAL, "");
456 return PM3_SUCCESS;
459 static int ndefDecodePayloadSmartPoster(uint8_t *ndef, size_t ndeflen, bool print, bool verbose) {
460 if (print) {
461 PrintAndLogEx(INFO, _YELLOW_("Well Known Record - Smartposter {"));
464 NDEFHeader_t NDEFHeader = {0};
465 int res = ndefDecodeHeader(ndef, ndeflen, &NDEFHeader);
466 if (res != PM3_SUCCESS) {
467 PrintAndLogEx(FAILED, "decode header failed..");
468 return res;
471 if (verbose) {
472 ndefPrintHeader(&NDEFHeader);
475 if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) {
476 ndefDecodePayload(&NDEFHeader);
479 if (NDEFHeader.TypeLen) {
480 PrintAndLogEx(INFO, "Type data");
481 print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1);
483 if (NDEFHeader.IDLen) {
484 PrintAndLogEx(INFO, "ID data");
485 print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1);
487 if (NDEFHeader.PayloadLen) {
488 PrintAndLogEx(INFO, "Payload data");
489 print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1);
491 // recursive
492 if (NDEFHeader.MessageEnd == false) {
493 ndefDecodePayloadSmartPoster(ndef + NDEFHeader.RecLen, ndeflen - NDEFHeader.RecLen, false, false);
496 if (print) {
497 PrintAndLogEx(INFO, _YELLOW_("}"));
499 return PM3_SUCCESS;
502 static int ndefDecodeMime_wifi(NDEFHeader_t *ndef) {
503 PrintAndLogEx(INFO, _CYAN_("WiFi details"));
504 if (ndef->PayloadLen > 1) {
505 PrintAndLogEx(INFO, ">>> decorder, to be implemented <<<");
507 return PM3_SUCCESS;
510 static int ndefDecodeMime_vcard(NDEFHeader_t *ndef) {
511 PrintAndLogEx(INFO, _CYAN_("VCARD details"));
512 if (ndef->PayloadLen > 1) {
513 PrintAndLogEx(INFO, "Data... " _YELLOW_("%.*s"), (int)ndef->PayloadLen, ndef->Payload);
514 PrintAndLogEx(INFO, ">>> decorder, to be implemented <<<");
516 return PM3_SUCCESS;
519 static int ndefDecodeMime_bt(NDEFHeader_t *ndef) {
520 PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type );
521 if (ndef->PayloadLen > 1) {
522 uint16_t ooblen = (ndef->Payload[1] << 8 | ndef->Payload[0]);
523 PrintAndLogEx(INFO, "OOB data len.... %u", ooblen);
524 PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(ndef->Payload + 2, 6));
525 // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit
526 switch (ndef->Payload[8]) {
527 case 0x02:
528 PrintAndLogEx(INFO, "Optional Data... incomplete list 16-bit UUID's");
529 break;
530 case 0x03:
531 PrintAndLogEx(INFO, "Optional Data... complete list 16-bit UUID's");
532 break;
533 case 0x04:
534 PrintAndLogEx(INFO, "Optional Data... incomplete list 32-bit UUID's");
535 break;
536 case 0x05:
537 PrintAndLogEx(INFO, "Optional Data... complete list 32-bit UUID's");
538 break;
539 case 0x06:
540 PrintAndLogEx(INFO, "Optional Data... incomplete list 128-bit UUID's");
541 break;
542 case 0x07:
543 PrintAndLogEx(INFO, "Optional Data... complete list 128-bit UUID's");
544 break;
545 default:
546 PrintAndLogEx(INFO, "Optional Data... [ %02x ]", ndef->Payload[8]);
547 break;
549 // Let's check payload[9]. If 0x08 then SHORT_NAME or if 0x09 then COMPLETE_NAME
550 if (ndef->Payload[9] == 0x08 ) {
551 PrintAndLogEx(INFO, "Short name...... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10);
552 } else if (ndef->Payload[9] == 0x09 ) {
553 PrintAndLogEx(INFO, "Complete name... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10);
554 } else {
555 PrintAndLogEx(INFO, "[ %02x ]", ndef->Payload[9]);
557 PrintAndLogEx(NORMAL, "");
559 return PM3_SUCCESS;
562 static int ndefDecodePayload(NDEFHeader_t *ndef) {
564 PrintAndLogEx(INFO, "");
565 switch (ndef->TypeNameFormat) {
566 case tnfEmptyRecord:
567 PrintAndLogEx(INFO, "Empty Record");
568 if (ndef->TypeLen != 0 || ndef->IDLen != 0 || ndef->PayloadLen != 0) {
569 PrintAndLogEx(FAILED, "unexpected data in TNF_EMPTY record");
570 break;
572 break;
573 case tnfWellKnownRecord:
575 if (!strncmp((char *)ndef->Type, "T", ndef->TypeLen)) {
576 PrintAndLogEx(INFO, _CYAN_("Text"));
577 uint8_t utf8 = (ndef->Payload[0] >> 7);
578 uint8_t lc_len = ndef->Payload[0] & 0x3F;
579 PrintAndLogEx(INFO,
580 " UTF %d... " _GREEN_("%.*s") ", " _GREEN_("%.*s"),
581 (utf8 == 0) ? 8 : 16,
582 lc_len,
583 ndef->Payload + 1,
584 (int)ndef->PayloadLen - 1 - lc_len,
585 ndef->Payload + 1 + lc_len
589 if (!strncmp((char *)ndef->Type, "U", ndef->TypeLen)) {
590 PrintAndLogEx(INFO, _CYAN_("URL"));
591 PrintAndLogEx(INFO
592 , " uri... " _GREEN_("%s%.*s")
593 , (ndef->Payload[0] <= 0x23 ? URI_s[ndef->Payload[0]] : "[err]")
594 , (int)(ndef->PayloadLen - 1)
595 , &ndef->Payload[1]
599 if (!strncmp((char *)ndef->Type, "Sig", ndef->TypeLen)) {
600 PrintAndLogEx(INFO, _CYAN_("Signature"));
601 ndefDecodeSig(ndef->Payload, ndef->PayloadLen);
604 if (!strncmp((char *)ndef->Type, "Sp", ndef->TypeLen)) {
605 ndefDecodePayloadSmartPoster(ndef->Payload, ndef->PayloadLen, true, false);
608 if (!strncmp((char *)ndef->Type, "Di", ndef->TypeLen)) {
609 ndefDecodePayloadDeviceInfo(ndef->Payload, ndef->PayloadLen);
612 if (!strncmp((char *)ndef->Type, "Hc", ndef->TypeLen)) {
613 PrintAndLogEx(INFO, _CYAN_("Handover carrier"));
614 PrintAndLogEx(INFO, "- decoder to be impl -");
617 if (!strncmp((char *)ndef->Type, "Hr", ndef->TypeLen)) {
618 PrintAndLogEx(INFO, _CYAN_("Handover request"));
619 PrintAndLogEx(INFO, "- decoder to be impl -");
622 if (!strncmp((char *)ndef->Type, "Hs", ndef->TypeLen)) {
623 PrintAndLogEx(INFO, _CYAN_("Handover select"));
624 PrintAndLogEx(INFO, "- decoder to be impl -");
627 if (!strncmp((char *)ndef->Type, "ac", ndef->TypeLen)) {
628 PrintAndLogEx(INFO, _CYAN_("Alternative carrier"));
629 PrintAndLogEx(INFO, "- decoder to be impl -");
631 break;
632 case tnfMIMEMediaRecord:
633 PrintAndLogEx(INFO, "MIME Media Record");
635 if (str_startswith((const char *)ndef->Type, NDEF_WIFIAPPL)) {
636 ndefDecodeMime_wifi(ndef);
638 if (str_startswith((const char *)ndef->Type, NDEF_VCARDTEXT)) {
639 ndefDecodeMime_vcard(ndef);
641 if (str_startswith((const char *)ndef->Type, NDEF_BLUEAPPL)) {
642 ndefDecodeMime_bt(ndef);
645 break;
646 case tnfAbsoluteURIRecord:
647 PrintAndLogEx(INFO, "Absolute URI Record");
648 PrintAndLogEx(INFO, " payload : %.*s", (int)ndef->PayloadLen, ndef->Payload);
649 break;
650 case tnfExternalRecord:
651 PrintAndLogEx(INFO, "External Record");
652 PrintAndLogEx(INFO, "- decoder to be impl -");
653 break;
654 case tnfUnknownRecord:
655 PrintAndLogEx(INFO, "Unknown Record");
656 PrintAndLogEx(INFO, "- decoder to be impl -");
657 break;
658 case tnfUnchangedRecord:
659 PrintAndLogEx(INFO, "Unchanged Record");
660 PrintAndLogEx(INFO, "- decoder to be impl -");
661 break;
663 return PM3_SUCCESS;
666 static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) {
667 NDEFHeader_t NDEFHeader = {0};
668 int res = ndefDecodeHeader(ndefRecord, ndefRecordLen, &NDEFHeader);
669 if (res != PM3_SUCCESS)
670 return res;
672 ndefPrintHeader(&NDEFHeader);
673 PrintAndLogEx(INFO, "");
674 PrintAndLogEx(INFO, _CYAN_("Payload info"));
676 if (NDEFHeader.TypeLen) {
677 PrintAndLogEx(INFO, "Type data");
678 print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1);
680 if (NDEFHeader.IDLen) {
681 PrintAndLogEx(INFO, "ID data");
682 print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1);
684 if (NDEFHeader.PayloadLen) {
685 PrintAndLogEx(INFO, "Payload data");
686 print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1);
688 if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) {
689 ndefDecodePayload(&NDEFHeader);
692 return PM3_SUCCESS;
695 int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) {
696 bool firstRec = true;
697 size_t len = 0;
698 size_t counter = 0;
700 while (len < ndefRecordLen) {
701 counter++;
703 NDEFHeader_t NDEFHeader = {0};
704 int res = ndefDecodeHeader(&ndefRecord[len], ndefRecordLen - len, &NDEFHeader);
705 if (res != PM3_SUCCESS)
706 return res;
708 if (firstRec) {
709 if (!NDEFHeader.MessageBegin) {
710 PrintAndLogEx(ERR, "NDEF first record have MessageBegin = false!");
711 return PM3_ESOFT;
713 firstRec = false;
716 if (NDEFHeader.MessageEnd && len + NDEFHeader.RecLen != ndefRecordLen) {
717 PrintAndLogEx(ERR, "NDEF records have wrong length. Must be %zu, calculated %zu", ndefRecordLen, len + NDEFHeader.RecLen);
718 return PM3_ESOFT;
721 PrintAndLogEx(NORMAL, "");
722 PrintAndLogEx(SUCCESS, _CYAN_("Record") " " _YELLOW_("%zu"), counter);
723 PrintAndLogEx(INFO, "-----------------------------------------------------");
724 ndefRecordDecodeAndPrint(&ndefRecord[len], NDEFHeader.RecLen);
726 len += NDEFHeader.RecLen;
728 if (NDEFHeader.MessageEnd)
729 break;
732 return PM3_SUCCESS;
735 // http://apps4android.org/nfc-specifications/NFCForum-TS-Type-2-Tag_1.1.pdf
736 int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
738 size_t indx = 0;
740 PrintAndLogEx(NORMAL, "");
741 PrintAndLogEx(INFO, "--- " _CYAN_("NDEF parsing") " ----------------");
742 while (indx < ndefLen) {
743 switch (ndef[indx]) {
744 case 0x00: {
745 indx++;
746 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
747 PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF NULL block") " ---");
748 if (len)
749 PrintAndLogEx(WARNING, "NDEF NULL block size must be 0, got %d bytes", len);
750 indx += len;
751 break;
753 case 0x01: {
754 indx++;
755 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
756 PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Lock Control") " ---");
757 if (len != 3) {
758 PrintAndLogEx(WARNING, "NDEF Lock Control block size must be 3 instead of %d.", len);
759 } else {
760 uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f;
761 uint8_t byte_offset = ndef[indx] & 0x0f;
762 uint8_t Size = ndef[indx + 1];
763 uint8_t BytesLockedPerLockBit = (ndef[indx + 2] >> 4) & 0x0f;
764 uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
765 PrintAndLogEx(SUCCESS, " Pages addr (number of pages)... %d", pages_addr);
766 PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %d", byte_offset);
767 PrintAndLogEx(SUCCESS, "Size in bits of the lock area %d. bytes approx %d", Size, Size / 8);
768 PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page);
769 PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit");
770 PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit is able to lock: %d", BytesLockedPerLockBit);
772 indx += len;
773 break;
775 case 0x02: {
776 indx++;
777 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
778 PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Memory Control") " ---");
779 if (len != 3) {
780 PrintAndLogEx(WARNING, "NDEF Memory Control block size must be 3 instead of %d.", len);
781 } else {
782 uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f;
783 uint8_t byte_offset = ndef[indx] & 0x0f;
784 uint8_t Size = ndef[indx + 1];
785 uint8_t bytes_per_page = ndef[indx + 2] & 0x0f;
786 PrintAndLogEx(SUCCESS, " Pages addr (number of pages) : %d", pages_addr);
787 PrintAndLogEx(SUCCESS, "Byte offset (number of bytes) : %d", byte_offset);
788 PrintAndLogEx(SUCCESS, "Size in bits of the reserved area : %d. bytes approx: %d", Size, Size / 8);
789 PrintAndLogEx(SUCCESS, " Number of bytes / page : %d", bytes_per_page);
791 indx += len;
792 break;
794 case 0x03: {
795 indx++;
796 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
797 PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Message") " ---");
798 if (len == 0) {
799 PrintAndLogEx(SUCCESS, "Found NDEF message w zero length");
800 } else {
801 PrintAndLogEx(SUCCESS, "Found NDEF message (%d bytes)", len);
803 int res = NDEFRecordsDecodeAndPrint(&ndef[indx], len);
804 if (res != PM3_SUCCESS)
805 return res;
808 indx += len;
809 break;
811 case 0xfd: {
812 indx++;
813 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
814 PrintAndLogEx(SUCCESS, "--- " _CYAN_("Proprietary info") " ---");
815 PrintAndLogEx(SUCCESS, " Can't decode, skipping %d bytes", len);
816 indx += len;
817 break;
819 case 0xfe: {
820 PrintAndLogEx(SUCCESS, "NDEF Terminator detected");
821 return PM3_SUCCESS;
823 default: {
824 if (verbose)
825 PrintAndLogEx(ERR, "unknown tag 0x%02x", ndef[indx]);
827 return PM3_ESOFT;
831 return PM3_SUCCESS;