1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2019 Merlok
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
7 //-----------------------------------------------------------------------------
8 // NFC Data Exchange Format (NDEF) functions
9 //-----------------------------------------------------------------------------
16 #include "util.h" // sprint_hex...
17 #include "crypto/asn1utils.h"
18 #include "crypto/libpcrypto.h"
20 #include "commonutil.h" // ARRAYLEN
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
[] = {
33 "Absolute URI Record",
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
47 "RSASSA-PKCS1-v1_5-2048",
56 static const char *ndefCertificateFormat_s
[] = {
62 static const char *URI_s
[] = {
64 "http://www.", // 0x01
65 "https://www.", // 0x02
70 "ftp://anonymous:anonymous@", // 0x07
91 "irdaobex://", // 0x1C
93 "urn:epc:id:", // 0x1E
94 "urn:epc:tag:", // 0x1F
95 "urn:epc:pat:", // 0x20
96 "urn:epc:raw:", // 0x21
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
) {
106 if (data
[0] == 0xff) {
107 len
= (data
[1] << 8) + data
[2];
117 static int ndefDecodeHeader(uint8_t *data
, size_t datalen
, NDEFHeader_t
*header
) {
119 header
->Payload
= 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
)
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
;
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
)
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
]);
174 static const char *get_curve_name(mbedtls_ecp_group_id grp_id
) {
176 case MBEDTLS_ECP_DP_NONE
:
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
213 mbedtls_ecp_group_id grp_id
;
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"},
227 bool is_valid
= false;
228 for (i
= 0; i
< ARRAYLEN(ndef_public_keys
); i
++) {
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);
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);
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"));
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"));
265 PrintAndLogEx(INFO
, " Params used: signature, plain");
268 PrintAndLogEx(INFO
, " Params used: signature, SHA256");
274 static int ndefDecodeSig1(uint8_t *sig
, size_t siglen
) {
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];
285 if (sigType
== stECDSA_P192
|| sigType
== stECDSA_P256
) {
288 if (sigType
== stECDSA_P256
)
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
));
303 size_t intsigurilen
= (sig
[indx
] << 8) + sig
[indx
+ 1];
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
318 for (int i
= 0; i
< certCount
; i
++) {
319 size_t intcertlen
= (sig
[indx
+ 1] << 8) + sig
[indx
+ 2];
322 PrintAndLogEx(SUCCESS
, "\tcertificate %d [%zu]: %s", i
+ 1, intcertlen
, sprint_hex_inrow(&sig
[indx
], intcertlen
));
326 // have certificate uri
327 if ((indx
<= siglen
) && certURI
) {
328 size_t inturilen
= (sig
[indx
] << 8) + sig
[indx
+ 1];
330 PrintAndLogEx(SUCCESS
, "\tcertificate uri [%zu]: %.*s", inturilen
, (int)inturilen
, &sig
[indx
]);
336 // https://github.com/nfcpy/ndeflib/blob/master/src/ndef/signature.py#L292
337 static int ndefDecodeSig2(uint8_t *sig
, size_t siglen
) {
340 uint8_t sigType
= sig
[indx
] & 0x7f;
341 bool sigURI
= sig
[indx
] & 0x80;
344 uint8_t hashType
= sig
[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];
356 PrintAndLogEx(SUCCESS
, "\tsignature uri [%zu]: %.*s", intsiglen
, (int)intsiglen
, &sig
[indx
]);
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);
370 PrintAndLogEx(INFO
, "\tsignature: unknown type");
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
384 for (int i
= 0; i
< certCount
; i
++) {
385 size_t intcertlen
= (sig
[indx
+ 1] << 8) + sig
[indx
+ 2];
388 PrintAndLogEx(SUCCESS
, "\tcertificate %d [%zu]: %s", i
+ 1, intcertlen
, sprint_hex_inrow(&sig
[indx
], intcertlen
));
392 // have certificate uri
393 if ((indx
<= siglen
) && certURI
) {
394 size_t inturilen
= (sig
[indx
] << 8) + sig
[indx
+ 1];
396 PrintAndLogEx(SUCCESS
, "\tcertificate uri [%zu]: %.*s", inturilen
, (int)inturilen
, &sig
[indx
]);
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.");
410 return ndefDecodeSig1(sig
, siglen
);
413 return ndefDecodeSig2(sig
, siglen
);
418 static int ndefDecodePayloadDeviceInfo(uint8_t *payload
, size_t len
) {
424 PrintAndLogEx(INFO
, _CYAN_("Device information"));
425 uint8_t *p
= payload
;
428 PrintAndLogEx(INFO
, "Vendor........ " _YELLOW_("%.*s"), n
, p
);
431 PrintAndLogEx(INFO
, "Model......... " _YELLOW_("%.*s"), n
, p
);
434 PrintAndLogEx(INFO
, "Unique name... " _YELLOW_("%.*s"), n
, p
);
438 // record.uuid_string = '123e4567-e89b-12d3-a456-426655440000'
441 sprintf(uuid
, "%s-", sprint_hex_inrow(p
, 4));
443 sprintf(uuid
+ strlen(uuid
), "%s-", sprint_hex_inrow(p
, 2));
445 sprintf(uuid
+ strlen(uuid
), "%s-", sprint_hex_inrow(p
, 2));
447 sprintf(uuid
+ strlen(uuid
), "%s-", sprint_hex_inrow(p
, 2));
449 sprintf(uuid
+ strlen(uuid
), "%s", sprint_hex_inrow(p
, 6));
451 PrintAndLogEx(INFO
, "UUID.......... " _YELLOW_("%s"), uuid
);
454 PrintAndLogEx(INFO
, "Version....... " _YELLOW_("%.*s"), n
, p
);
455 PrintAndLogEx(NORMAL
, "");
459 static int ndefDecodePayloadSmartPoster(uint8_t *ndef
, size_t ndeflen
, bool print
, bool verbose
) {
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..");
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);
492 if (NDEFHeader
.MessageEnd
== false) {
493 ndefDecodePayloadSmartPoster(ndef
+ NDEFHeader
.RecLen
, ndeflen
- NDEFHeader
.RecLen
, false, false);
497 PrintAndLogEx(INFO
, _YELLOW_("}"));
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 <<<");
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 <<<");
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]) {
528 PrintAndLogEx(INFO
, "Optional Data... incomplete list 16-bit UUID's");
531 PrintAndLogEx(INFO
, "Optional Data... complete list 16-bit UUID's");
534 PrintAndLogEx(INFO
, "Optional Data... incomplete list 32-bit UUID's");
537 PrintAndLogEx(INFO
, "Optional Data... complete list 32-bit UUID's");
540 PrintAndLogEx(INFO
, "Optional Data... incomplete list 128-bit UUID's");
543 PrintAndLogEx(INFO
, "Optional Data... complete list 128-bit UUID's");
546 PrintAndLogEx(INFO
, "Optional Data... [ %02x ]", ndef
->Payload
[8]);
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);
555 PrintAndLogEx(INFO
, "[ %02x ]", ndef
->Payload
[9]);
557 PrintAndLogEx(NORMAL
, "");
562 static int ndefDecodePayload(NDEFHeader_t
*ndef
) {
564 PrintAndLogEx(INFO
, "");
565 switch (ndef
->TypeNameFormat
) {
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");
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;
580 " UTF %d... " _GREEN_("%.*s") ", " _GREEN_("%.*s"),
581 (utf8
== 0) ? 8 : 16,
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"));
592 , " uri... " _GREEN_("%s%.*s")
593 , (ndef
->Payload
[0] <= 0x23 ? URI_s
[ndef
->Payload
[0]] : "[err]")
594 , (int)(ndef
->PayloadLen
- 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 -");
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
);
646 case tnfAbsoluteURIRecord
:
647 PrintAndLogEx(INFO
, "Absolute URI Record");
648 PrintAndLogEx(INFO
, " payload : %.*s", (int)ndef
->PayloadLen
, ndef
->Payload
);
650 case tnfExternalRecord
:
651 PrintAndLogEx(INFO
, "External Record");
652 PrintAndLogEx(INFO
, "- decoder to be impl -");
654 case tnfUnknownRecord
:
655 PrintAndLogEx(INFO
, "Unknown Record");
656 PrintAndLogEx(INFO
, "- decoder to be impl -");
658 case tnfUnchangedRecord
:
659 PrintAndLogEx(INFO
, "Unchanged Record");
660 PrintAndLogEx(INFO
, "- decoder to be impl -");
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
)
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
);
695 int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord
, size_t ndefRecordLen
) {
696 bool firstRec
= true;
700 while (len
< ndefRecordLen
) {
703 NDEFHeader_t NDEFHeader
= {0};
704 int res
= ndefDecodeHeader(&ndefRecord
[len
], ndefRecordLen
- len
, &NDEFHeader
);
705 if (res
!= PM3_SUCCESS
)
709 if (!NDEFHeader
.MessageBegin
) {
710 PrintAndLogEx(ERR
, "NDEF first record have MessageBegin = 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
);
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
)
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
) {
740 PrintAndLogEx(NORMAL
, "");
741 PrintAndLogEx(INFO
, "--- " _CYAN_("NDEF parsing") " ----------------");
742 while (indx
< ndefLen
) {
743 switch (ndef
[indx
]) {
746 uint16_t len
= ndefTLVGetLength(&ndef
[indx
], &indx
);
747 PrintAndLogEx(SUCCESS
, "--- " _CYAN_("NDEF NULL block") " ---");
749 PrintAndLogEx(WARNING
, "NDEF NULL block size must be 0, got %d bytes", len
);
755 uint16_t len
= ndefTLVGetLength(&ndef
[indx
], &indx
);
756 PrintAndLogEx(SUCCESS
, "--- " _CYAN_("NDEF Lock Control") " ---");
758 PrintAndLogEx(WARNING
, "NDEF Lock Control block size must be 3 instead of %d.", len
);
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
);
777 uint16_t len
= ndefTLVGetLength(&ndef
[indx
], &indx
);
778 PrintAndLogEx(SUCCESS
, "--- " _CYAN_("NDEF Memory Control") " ---");
780 PrintAndLogEx(WARNING
, "NDEF Memory Control block size must be 3 instead of %d.", len
);
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
);
796 uint16_t len
= ndefTLVGetLength(&ndef
[indx
], &indx
);
797 PrintAndLogEx(SUCCESS
, "--- " _CYAN_("NDEF Message") " ---");
799 PrintAndLogEx(SUCCESS
, "Found NDEF message w zero length");
801 PrintAndLogEx(SUCCESS
, "Found NDEF message (%d bytes)", len
);
803 int res
= NDEFRecordsDecodeAndPrint(&ndef
[indx
], len
);
804 if (res
!= PM3_SUCCESS
)
813 uint16_t len
= ndefTLVGetLength(&ndef
[indx
], &indx
);
814 PrintAndLogEx(SUCCESS
, "--- " _CYAN_("Proprietary info") " ---");
815 PrintAndLogEx(SUCCESS
, " Can't decode, skipping %d bytes", len
);
820 PrintAndLogEx(SUCCESS
, "NDEF Terminator detected");
825 PrintAndLogEx(ERR
, "unknown tag 0x%02x", ndef
[indx
]);