]> git.zerfleddert.de Git - proxmark3-svn/blame_incremental - client/mifare/ndef.c
Code improved for less memory
[proxmark3-svn] / client / mifare / ndef.c
... / ...
CommitLineData
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//-----------------------------------------------------------------------------
10
11#include "ndef.h"
12#include "ui.h"
13#include "emv/dump.h"
14#include "crypto/asn1utils.h"
15#include "util.h"
16#include <string.h>
17
18#define STRBOOL(p) ((p) ? "+" : "-")
19
20static const char *TypeNameFormat_s[] = {
21 "Empty Record",
22 "Well Known Record",
23 "MIME Media Record",
24 "Absolute URI Record",
25 "External Record",
26 "Unknown Record",
27 "Unchanged Record",
28 "n/a"
29};
30
31static const char *ndefSigType_s[] = {
32 "Not present", // No signature present
33 "RSASSA_PSS_SHA_1", // PKCS_1
34 "RSASSA_PKCS1_v1_5_WITH_SHA_1", // PKCS_1
35 "DSA",
36 "ECDSA",
37 "n/a"
38};
39
40static const char *ndefCertificateFormat_s[] = {
41 "X_509",
42 "X9_68",
43 "n/a"
44};
45
46static const char *URI_s[] = {
47 "", // 0x00
48 "http://www.", // 0x01
49 "https://www.", // 0x02
50 "http://", // 0x03
51 "https://", // 0x04
52 "tel:", // 0x05
53 "mailto:", // 0x06
54 "ftp://anonymous:anonymous@", // 0x07
55 "ftp://ftp.", // 0x08
56 "ftps://", // 0x09
57 "sftp://", // 0x0A
58 "smb://", // 0x0B
59 "nfs://", // 0x0C
60 "ftp://", // 0x0D
61 "dav://", // 0x0E
62 "news:", // 0x0F
63 "telnet://", // 0x10
64 "imap:", // 0x11
65 "rtsp://", // 0x12
66 "urn:", // 0x13
67 "pop:", // 0x14
68 "sip:", // 0x15
69 "sips:", // 0x16
70 "tftp:", // 0x17
71 "btspp://", // 0x18
72 "btl2cap://", // 0x19
73 "btgoep://", // 0x1A
74 "tcpobex://", // 0x1B
75 "irdaobex://", // 0x1C
76 "file://", // 0x1D
77 "urn:epc:id:", // 0x1E
78 "urn:epc:tag:", // 0x1F
79 "urn:epc:pat:", // 0x20
80 "urn:epc:raw:", // 0x21
81 "urn:epc:", // 0x22
82 "urn:nfc:" // 0x23
83};
84
85uint16_t ndefTLVGetLength(uint8_t *data, size_t *indx) {
86 uint16_t len = 0;
87 if (data[0] == 0xff) {
88 len = (data[1] << 8) + data[2];
89 *indx += 3;
90 } else {
91 len = data[0];
92 *indx += 1;
93 }
94
95 return len;
96}
97
98int ndefDecodeHeader(uint8_t *data, size_t datalen, NDEFHeader_t *header) {
99 header->Type = NULL;
100 header->Payload = NULL;
101 header->ID = NULL;
102
103 header->MessageBegin = data[0] & 0x80;
104 header->MessageEnd = data[0] & 0x40;
105 header->ChunkFlag = data[0] & 0x20;
106 header->ShortRecordBit = data[0] & 0x10;
107 header->IDLenPresent = data[0] & 0x08;
108 header->TypeNameFormat = data[0] & 0x07;
109 header->len = 1 + 1 + (header->ShortRecordBit ? 1 : 4) + (header->IDLenPresent ? 1 : 0); // header + typelen + payloadlen + idlen
110 if (header->len > datalen)
111 return 1;
112
113 header->TypeLen = data[1];
114 header->Type = data + header->len;
115
116 header->PayloadLen = (header->ShortRecordBit ? (data[2]) : ((data[2] << 24) + (data[3] << 16) + (data[4] << 8) + data[5]));
117
118 if (header->IDLenPresent) {
119 header->IDLen = (header->ShortRecordBit ? (data[3]) : (data[6]));
120 header->Payload = header->Type + header->TypeLen;
121 } else {
122 header->IDLen = 0;
123 }
124
125 header->Payload = header->Type + header->TypeLen + header->IDLen;
126
127 header->RecLen = header->len + header->TypeLen + header->PayloadLen + header->IDLen;
128
129 if (header->RecLen > datalen)
130 return 3;
131
132 return 0;
133}
134
135int ndefPrintHeader(NDEFHeader_t *header) {
136 PrintAndLogEx(INFO, "Header:");
137
138 PrintAndLogEx(NORMAL, "\tMessage Begin: %s", STRBOOL(header->MessageBegin));
139 PrintAndLogEx(NORMAL, "\tMessage End: %s", STRBOOL(header->MessageEnd));
140 PrintAndLogEx(NORMAL, "\tChunk Flag: %s", STRBOOL(header->ChunkFlag));
141 PrintAndLogEx(NORMAL, "\tShort Record Bit: %s", STRBOOL(header->ShortRecordBit));
142 PrintAndLogEx(NORMAL, "\tID Len Present: %s", STRBOOL(header->IDLenPresent));
143 PrintAndLogEx(NORMAL, "\tType Name Format: [0x%02x] %s", header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]);
144
145 PrintAndLogEx(NORMAL, "\tHeader length : %d", header->len);
146 PrintAndLogEx(NORMAL, "\tType length : %d", header->TypeLen);
147 PrintAndLogEx(NORMAL, "\tPayload length : %d", header->PayloadLen);
148 PrintAndLogEx(NORMAL, "\tID length : %d", header->IDLen);
149 PrintAndLogEx(NORMAL, "\tRecord length : %d", header->RecLen);
150
151 return 0;
152}
153
154int ndefDecodeSig(uint8_t *sig, size_t siglen) {
155 size_t indx = 0;
156 PrintAndLogEx(NORMAL, "\tsignature version: 0x%02x", sig[0]);
157 if (sig[0] != 0x01) {
158 PrintAndLogEx(ERR, "signature version unknown.");
159 return 1;
160 }
161 indx++;
162
163 uint8_t sigType = sig[indx] & 0x7f;
164 bool sigURI = sig[indx] & 0x80;
165
166 PrintAndLogEx(NORMAL, "\tsignature type: %s", ((sigType < stNA) ? ndefSigType_s[sigType] : ndefSigType_s[stNA]));
167 PrintAndLogEx(NORMAL, "\tsignature uri: %s", (sigURI ? "present" : "not present"));
168
169 size_t intsiglen = (sig[indx + 1] << 8) + sig[indx + 2];
170 // ecdsa 0x04
171 if (sigType == stECDSA) {
172 indx += 3;
173 PrintAndLogEx(NORMAL, "\tsignature [%d]: %s", intsiglen, sprint_hex_inrow(&sig[indx], intsiglen));
174
175 uint8_t rval[300] = {0};
176 uint8_t sval[300] = {0};
177 int res = ecdsa_asn1_get_signature(&sig[indx], intsiglen, rval, sval);
178 if (!res) {
179 PrintAndLogEx(NORMAL, "\t\tr: %s", sprint_hex(rval, 32));
180 PrintAndLogEx(NORMAL, "\t\ts: %s", sprint_hex(sval, 32));
181 }
182 }
183 indx += intsiglen;
184
185 if (sigURI) {
186 size_t intsigurilen = (sig[indx] << 8) + sig[indx + 1];
187 indx += 2;
188 PrintAndLogEx(NORMAL, "\tsignature uri [%d]: %.*s", intsigurilen, intsigurilen, &sig[indx]);
189 indx += intsigurilen;
190 }
191
192 uint8_t certFormat = (sig[indx] >> 4) & 0x07;
193 uint8_t certCount = sig[indx] & 0x0f;
194 bool certURI = sig[indx] & 0x80;
195
196 PrintAndLogEx(NORMAL, "\tcertificate format: %s", ((certFormat < sfNA) ? ndefCertificateFormat_s[certFormat] : ndefCertificateFormat_s[sfNA]));
197 PrintAndLogEx(NORMAL, "\tcertificates count: %d", certCount);
198
199 // print certificates
200 indx++;
201 for (int i = 0; i < certCount; i++) {
202 size_t intcertlen = (sig[indx + 1] << 8) + sig[indx + 2];
203 indx += 2;
204
205 PrintAndLogEx(NORMAL, "\tcertificate %d [%d]: %s", i + 1, intcertlen, sprint_hex_inrow(&sig[indx], intcertlen));
206 indx += intcertlen;
207 }
208
209 // have certificate uri
210 if ((indx <= siglen) && certURI) {
211 size_t inturilen = (sig[indx] << 8) + sig[indx + 1];
212 indx += 2;
213 PrintAndLogEx(NORMAL, "\tcertificate uri [%d]: %.*s", inturilen, inturilen, &sig[indx]);
214 indx += inturilen;
215 }
216
217 return 0;
218};
219
220int ndefDecodePayload(NDEFHeader_t *ndef) {
221
222 switch (ndef->TypeNameFormat) {
223 case tnfWellKnownRecord:
224 PrintAndLogEx(INFO, "Well Known Record");
225 PrintAndLogEx(NORMAL, "\ttype: %.*s", ndef->TypeLen, ndef->Type);
226
227 if (!strncmp((char *)ndef->Type, "T", ndef->TypeLen)) {
228 PrintAndLogEx(NORMAL, "\ttext : %.*s", ndef->PayloadLen, ndef->Payload);
229 }
230
231 if (!strncmp((char *)ndef->Type, "U", ndef->TypeLen)) {
232 PrintAndLogEx(NORMAL, "\turi : %s%.*s", (ndef->Payload[0] <= 0x23 ? URI_s[ndef->Payload[0]] : "[err]"), ndef->PayloadLen, &ndef->Payload[1]);
233 }
234
235 if (!strncmp((char *)ndef->Type, "Sig", ndef->TypeLen)) {
236 ndefDecodeSig(ndef->Payload, ndef->PayloadLen);
237 }
238
239 break;
240 case tnfAbsoluteURIRecord:
241 PrintAndLogEx(INFO, "Absolute URI Record");
242 PrintAndLogEx(NORMAL, "\ttype: %.*s", ndef->TypeLen, ndef->Type);
243 PrintAndLogEx(NORMAL, "\tpayload: %.*s", ndef->PayloadLen, ndef->Payload);
244 break;
245 default:
246 break;
247 }
248 return 0;
249}
250
251int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) {
252 NDEFHeader_t NDEFHeader = {0};
253 int res = ndefDecodeHeader(ndefRecord, ndefRecordLen, &NDEFHeader);
254 if (res)
255 return res;
256
257 ndefPrintHeader(&NDEFHeader);
258
259 if (NDEFHeader.TypeLen) {
260 PrintAndLogEx(INFO, "Type data:");
261 dump_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, stdout, 1);
262 }
263 if (NDEFHeader.IDLen) {
264 PrintAndLogEx(INFO, "ID data:");
265 dump_buffer(NDEFHeader.ID, NDEFHeader.IDLen, stdout, 1);
266 }
267 if (NDEFHeader.PayloadLen) {
268 PrintAndLogEx(INFO, "Payload data:");
269 dump_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, stdout, 1);
270 if (NDEFHeader.TypeLen)
271 ndefDecodePayload(&NDEFHeader);
272 }
273
274 return 0;
275}
276
277int ndefRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) {
278 bool firstRec = true;
279 size_t len = 0;
280
281 while (len < ndefRecordLen) {
282 NDEFHeader_t NDEFHeader = {0};
283 int res = ndefDecodeHeader(&ndefRecord[len], ndefRecordLen - len, &NDEFHeader);
284 if (res)
285 return res;
286
287 if (firstRec) {
288 if (!NDEFHeader.MessageBegin) {
289 PrintAndLogEx(ERR, "NDEF first record have MessageBegin=false!");
290 return 1;
291 }
292 firstRec = false;
293 }
294
295 if (NDEFHeader.MessageEnd && len + NDEFHeader.RecLen != ndefRecordLen) {
296 PrintAndLogEx(ERR, "NDEF records have wrong length. Must be %d, calculated %d", ndefRecordLen, len + NDEFHeader.RecLen);
297 return 1;
298 }
299
300 ndefRecordDecodeAndPrint(&ndefRecord[len], NDEFHeader.RecLen);
301
302 len += NDEFHeader.RecLen;
303
304 if (NDEFHeader.MessageEnd)
305 break;
306 }
307
308 return 0;
309}
310
311int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) {
312
313 size_t indx = 0;
314
315 PrintAndLogEx(INFO, "NDEF decoding:");
316 while (indx < ndefLen) {
317 switch (ndef[indx]) {
318 case 0x00: {
319 indx++;
320 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
321 PrintAndLogEx(INFO, "-- NDEF NULL block.");
322 if (len)
323 PrintAndLogEx(WARNING, "NDEF NULL block size must be 0 instead of %d.", len);
324 indx += len;
325 break;
326 }
327 case 0x03: {
328 indx++;
329 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
330 PrintAndLogEx(INFO, "-- NDEF message. len: %d", len);
331
332 int res = ndefRecordsDecodeAndPrint(&ndef[indx], len);
333 if (res)
334 return res;
335
336 indx += len;
337 break;
338 }
339 case 0xfd: {
340 indx++;
341 uint16_t len = ndefTLVGetLength(&ndef[indx], &indx);
342 PrintAndLogEx(INFO, "-- NDEF proprietary info. Skipped %d bytes.", len);
343 indx += len;
344 break;
345 }
346 case 0xfe: {
347 PrintAndLogEx(INFO, "-- NDEF Terminator. Done.");
348 return 0;
349 break;
350 }
351 default: {
352 PrintAndLogEx(ERR, "unknown tag 0x%02x", ndef[indx]);
353 return 1;
354 }
355 }
356 }
357
358 return 0;
359}
Impressum, Datenschutz