-
Notifications
You must be signed in to change notification settings - Fork 135
/
mifare-classic.js
202 lines (155 loc) · 7.35 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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
"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?
// 0. Load key into an arbitrarily reader key slot.
// May be skipped, if the key was already loaded once into non-volatile memory and its key number is known.
// 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.
// 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;
// keyNumber and keyStructure is dependent on your reader
// uncomment/comment/change the following definitions
// for ACR122U:
// const keyNumber = 0; // or 1 (ACR122U has only two volatile (non-persistent) slots)
// const keyStructure = 0; // ACR122U supports only volatile card key, plain transmission
// for OMNIKEY 5422:
const keyNumber = 0; // an integer in range [0, 31] (total 32 MIFARE Classic key slots)
const keyStructure = 0b00100000; // bit 5 set to 1 to indicate non-volatile memory storage
// all OMNIKEY 5422 MIFARE Classic key slots are non-volatile
// that means that the key might be loaded only once
// and the reader will remember it in the future (even after power off, reconnection)
try {
await reader.loadAuthenticationKey(keyStructure, keyNumber, key);
pretty.info(`key ${key} successfully loaded into the slot `, reader);
} catch (err) {
pretty.error(`error when loading key`, reader, err);
return;
}
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(0, 4, keyType, keyNumber);
// 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);
});