-
Notifications
You must be signed in to change notification settings - Fork 135
/
Copy pathmifare-classic.js
181 lines (140 loc) · 6.6 KB
/
mifare-classic.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
"use strict";
// #############
// Example: MIFARE Classic
// - should work well with any compatible PC/SC card reader
// - what is covered:
// - authentication
// - reading data from card
// - writing data to card
// - what is NOT covered yet:
// - using sector trailers to update access rights
// #############
// ## Note about the card's data structure
//
// ### MIFARE Classic EV1 1K
// - 1024 × 8 bit EEPROM memory
// - 16 sectors of 4 blocks
// - see https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf
//
// ### MIFARE Classic EV1 4K
// - 4096 × 8 bit EEPROM memory
// - 32 sectors of 4 blocks and 8 sectors of 16 blocks
// - see https://www.nxp.com/docs/en/data-sheet/MF1S70YYX_V1.pdf
//
// One block contains 16 bytes.
// Don't forget specify the blockSize argument blockSize=16 in reader.read and reader.write calls.
// The smallest amount of data to write is one block. You can write only the entire blocks (card limitation).
//
// sector 0
// block 0 - manufacturer data (read only)
// block 1 - data block
// block 2 - data block
// block 3 - sector trailer 0
// bytes 00-05: Key A (default 0xFFFFFFFFFFFF) (6 bytes)
// bytes 06-09: Access Bits (default 0xFF0780) (4 bytes)
// bytes 10-15: Key B (optional) (default 0xFFFFFFFFFFFF) (6 bytes)
// sector 1:
// block 4 - data block
// block 5 - data block
// block 6 - data block
// block 7 - sector trailer 1
// sector 2:
// block 8 - data block
// block 9 - data block
// block 10 - data block
// block 11 - sector trailer 2
// ... and so on ...
import { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } from '../src/index';
import pretty from './pretty-logger';
const nfc = new NFC(); // const nfc = new NFC(pretty); // optionally you can pass logger to see internal debug logs
nfc.on('reader', async reader => {
pretty.info(`device attached`, reader);
reader.on('card', async card => {
// MIFARE Classic is ISO/IEC 14443-3 tag
// skip other standards
if (card.type !== TAG_ISO_14443_3) {
return;
}
pretty.info(`card detected`, reader, card);
// Reading and writing data from/to MIFARE Classic cards (e.g. MIFARE 1K) ALWAYS requires authentication!
// How does the MIFARE Classic authentication work?
// 1. You authenticate to a specific sector using a specific key (key + keyType).
// 2. After the successful authentication, you are granted permissions according to the access conditions
// for the given key (access conditions are specified in the trailer section of each sector).
// Depending on the access conditions, you can read from / write to the blocks of this sector.
// 3. If you want to access data in another sectors, you have to authenticate to that sector.
// Then you can access the data from the block within that sector (only from that sector).
// summary: MIFARE Classic will only grant permissions based on the last authentication attempt.
// Consequently, if multiple reader.authenticate(...) commands are used,
// only the last one has an effect on all subsequent read/write operations.
// reader.authenticate(blockNumber, keyType, key, obsolete = false)
// - blockNumber - the number of any block withing the sector we want to authenticate
// - keyType - type of key - either KEY_TYPE_A or KEY_TYPE_B
// - key - 6 bytes - a Buffer instance, an array of bytes, or 12-chars HEX string
// - obsolete - (default - false for PC/SC V2.07) use true for PC/SC V2.01
// Don't forget to fill YOUR keys and types! (default ones are stated below)
const key = 'FFFFFFFFFFFF'; // key must be a 12-chars HEX string, an instance of Buffer, or array of bytes
const keyType = KEY_TYPE_A;
try {
// we want to authenticate sector 1
// authenticating one block within the sector will authenticate all blocks within that sector
// so in our case, we choose block 4 that is within the sector 1, all blocks (4, 5, 6, 7)
// will be authenticated with the given key
await reader.authenticate(4, keyType, key);
// Note: writing might require to authenticate with a different key (based on the sector access conditions)
pretty.info(`sector 1 successfully authenticated`, reader);
} catch (err) {
pretty.error(`error when authenticating block 4 within the sector 1`, reader, err);
return;
}
// example reading 16 bytes (one block) assuming containing 32bit integer
// !!! note that we don't need 16 bytes - 32bit integer takes only 4 bytes !!!
try {
// reader.read(blockNumber, length, blockSize = 4, packetSize = 16)
// - blockNumber - memory block number where to start reading
// - length - how many bytes to read
// - blockSize - 4 for MIFARE Ultralight, 16 for MIFARE Classic
// ! Caution! length must be divisible by blockSize
// ! Caution! MIFARE Classic cards have sector trailers
// containing access bits instead of data, each last block in sector is sector trailer
// (e.g. block 3, 7, 11, 14)
// see memory structure above or https://github.com/pokusew/nfc-pcsc/issues/16#issuecomment-304989178
const data = await reader.read(4, 16, 16); // blockSize=16 must specified for MIFARE Classic cards
pretty.info(`data read`, reader, data);
const payload = data.readInt32BE(0);
pretty.info(`data converted`, reader, payload);
} catch (err) {
pretty.error(`error when reading data`, reader, err);
}
// example write 16 bytes containing 32bit integer
// !!! note that we don't need 16 bytes - 32bit integer takes just 4 bytes !!!
try {
// reader.write(blockNumber, data, blockSize = 4, packetSize = 16)
// - blockNumber - memory block number where to start writing
// - data - what to write
// - blockSize - 4 for MIFARE Ultralight, 16 for MIFARE Classic
// ! Caution! data.length must be divisible by blockSize
// ! Caution! MIFARE Classic cards have sector trailers
// containing access bits instead of data, each last block in sector is sector trailer
// (e.g. block 3, 7, 11, 14)
// ee memory structure above or https://github.com/pokusew/nfc-pcsc/issues/16#issuecomment-304989178
const data = Buffer.allocUnsafe(16);
data.fill(0);
const randomNumber = Math.round(Math.random() * 1000);
data.writeInt32BE(randomNumber, 0);
await reader.write(4, data, 16); // blockSize=16 must specified for MIFARE Classic cards
pretty.info(`data written`, reader, randomNumber, data);
} catch (err) {
pretty.error(`error when writing data`, reader, err);
}
});
reader.on('error', err => {
pretty.error(`an error occurred`, reader, err);
});
reader.on('end', () => {
pretty.info(`device removed`, reader);
});
});
nfc.on('error', err => {
pretty.error(`an error occurred`, err);
});