Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NTAG 21x Authentication #36

Open
bierlair opened this issue Feb 18, 2018 · 6 comments
Open

NTAG 21x Authentication #36

bierlair opened this issue Feb 18, 2018 · 6 comments
Assignees
Labels

Comments

@bierlair
Copy link

bierlair commented Feb 18, 2018

Is there a way to password protect NTAG21X cards / Type TAG_ISO_14443_3 via ACR122U?

I was trying my luck with a previously posted thread about Mifare Ultralight cards, but I end up with 0x6300 errors.

Code:

try {
	// APDU CMD: Update Binary Block
	const packet = new Buffer([
		0xFF, // Class
		0x00,
		0x00,
		0x00,
		0x07, //Length
		0xD4, // inserted
		0x42, // inserted
		0x1B, //PWD_AUTH
		0x11, //my pass
		0x22, //my pass
		0x33, //my pass
		0x44 //my pass
	]);
	var pwdResponse = await reader.transmit(packet, 7);
	console.log('pwd response', pwdResponse);
} catch (err) {
	console.log('error pwd_auth');
}

This works, but any read or write attempt after, does not work anymore:

0|yogaman  | 2018/02/18 19:03:08: Error when reading data: { ReadError: Read operation failed: Status code: 0x6300
0|yogaman  |     at /home/node/backend/node_modules/nfc-pcsc/dist/Reader.js:503:11
0|yogaman  |     at Generator.next (<anonymous>)
0|yogaman  |     at step (/home/node/backend/node_modules/nfc-pcsc/dist/Reader.js:18:191)
0|yogaman  |     at /home/node/backend/node_modules/nfc-pcsc/dist/Reader.js:18:361
0|yogaman  |     at <anonymous> name: 'ReadError', code: 'operation_failed' }

@pokusew pokusew self-assigned this Feb 19, 2018
@pokusew pokusew changed the title NTAG 21x Authentification NTAG 21x Authentication Feb 19, 2018
@pokusew
Copy link
Owner

pokusew commented Feb 19, 2018

Hi @bierlair,

thank you for posting your issue here.

According to the official documentation, it is possible to protect NTAG21x cards with password.
To read/write (based on your card's access config) password protected cards, you need to authenticate using PWD_AUTH command first.

And all these things should be doable using ACR122U and nfc-pcsc library. 😃

I don't know, where the problem might be. Could you please answer the following questions so that we can find why it doesn't work?

  1. What operating system, what reader, and card (I suppose NTAG21x) do you use?

  2. Are you able to read unprotected NTAG21x card?

  3. What's the content of pwdResponse? Can you log it and send it here?
    pwdResponse response should look like the following (7 bytes):

    d5 43 00 XX XX 90 00
    byte 0: d5 prefix for response of Data Exchange Command (see PN533 docs)
    byte 1: 43 prefix for response of Data Exchange Command (see PN533 docs)
    byte 2: Data Exchange Command Status 0x00 is success (see PN533 docs)
    bytes 3-4: Data Exchange Command Response – PACK (2 bytes password acknowledgment) from card
    bytes 5-6: ACR122U success code
    

    Note about PACK: You can change the value. It is stored in the last configuration page of the card, see the docs (8.5 Memory organization)

  4. Just for sure: Make sure you authenticate and read data from card in the same session. You have to authenticate the card every time you attach it.

Hope it helps. 🙂

Looking forward to your reply. I believe we make it work together. 🙂


To be honest, I have never tried to access protected cards before. I don't have any experience with NTAG21x cards (only with Mifare Ultralight EV1, Classic, and DESFire EV1).

Fortunately, according to the documentation, I have been able to make it work on Mifare Ultralight EV1 (it is very similar to NTAG21x cards, it even uses same commands). After I issue PWD_AUTH command, I am able to read the (password-read-protected) memory. 🚀

Here is my code to authenticate card:

reader.on('card', async card => {

  console.log('card detected', card);

  // PASSWORD (4 bytes) (stored on card in page 18)
  const password = Buffer.from([
    0xff,
    0xff,
    0xff,
    0xff,
  ]);

  // PACK (2 bytes) (stored in page 19 as first two bytes)
  // PACK is the response from card in case of successful PWD_AUTH cmd
  const pack = Buffer.from([
    0xab,
    0xcd
  ]);

  try {

    // CMD: PWD_AUTH via Direct Transmit (ACR122U) and Data Exchange (PN533)
    const cmd = Buffer.from([
      0xFF, // Class
      0x00, // Direct Transmit (see ACR122U)
      0x00, // ...
      0x00, // ...
      0x07, // Length of Direct Transmit payload
      // Payload (7 bytes)
      0xd4, // Data Exchange Command (see PN533 docs)
      0x42, // inserted
      0x1b, // PWD_AUTH
      ...password,
    ]);

    console.log('pwd_auth cmd', cmd);

    const response = await reader.transmit(cmd, 7);

    console.log('pwd_auth response', response);
    // pwd_auth response should look like the following (7 bytes)
    // d5 43 00 ab cd 90 00
    // byte 0: d5 prefix for response of Data Exchange Command (see PN533 docs)
    // byte 1: 43 prefix for response of Data Exchange Command (see PN533 docs)
    // byte 2: Data Exchange Command Status 0x00 is success (see PN533 docs)
    // bytes 3-4: Data Exchange Command Response – our PACK (set on card in page 19, in bytes 0-1) from card
    // bytes 5-6: ACR122U success code


  } catch (err) {
    console.error('pwd_auth error', err);
    return;
  }

});

That's how I set up the password protection (don't allow to read content without authentication):

reader.on('card', async card => {

  try {

    // set custom PASSWORD (4 bytes) (stored in page 18)
    await reader.write(19, password);

    // set custom PACK (2 bytes) (stored in page 19 as first two bytes)
    const packPage = await reader.read(19, 4);
    packPage[0] = pack[0];
    packPage[1] = pack[1];
    await reader.write(19, packPage);

    // read current configuration
    const config = await reader.read(16, 8);

    // Configuration page 16
    console.log(config[0]);
    console.log(config[1]);
    console.log(config[2]);
    console.log(config[3]); // AUTH0 (default: 0xff)

    // Configuration page 17
    console.log(config[4]); // ACCESS
    console.log(config[5]); // VCTID (default: 0x05)
    console.log(config[6]);
    console.log(config[7]);

    // Protect everything (start with first data page)
    config[3] = 0x04;

    // set ACCESS bits
    // bit 7: PROT One bit inside the ACCESS byte defining the memory protection
    //          0b ... write access is protected by the password verification
    //          1b ... read and write access is protected by the password verification
    // bit 6: CFGLCK Write locking bit for the user configuration
    //        - 0b ... user configuration open to write access
    //        - 1b ... user configuration permanently locked against write access
    // bits 5-3: reserved
    // bits 2-0: AUTHLIM
    // bit number-76543210
    //            ||||||||
    config[4] = 0b10000000;

    // set custom access rules
    await reader.write(16, config);

  } catch (err) {
    console.error(err);
  }

});

@bierlair
Copy link
Author

bierlair commented Feb 19, 2018

Awesome, this is exactly what I needed.
I was pretty close to completing this myself by reading the technical docs of NTAG21x, but your code snippet accelerated things.

Since I am working with NTAG216, I needed to adjust the page addresses.

The try/catch block for setting the password now looks something like this:

try {

	// set custom PASSWORD (4 bytes) (stored in page 229)
	await reader.write(229, password);

	// set custom PACK (2 bytes) (stored in page 230 as first two bytes)
	const packPage = await reader.read(230, 4);
	packPage[0] = pack[0];
	packPage[1] = pack[1];
	await reader.write(230, packPage);

	// read current configuration
	const config = await reader.read(227, 8);

	// Configuration page 16
	console.log(config[0]);
	console.log(config[1]);
	console.log(config[2]);
	console.log(config[3]); // AUTH0 (default: 0xff)

	// Configuration page 17
	console.log(config[4]); // ACCESS
	console.log(config[5]); // VCTID (default: 0x05)
	console.log(config[6]);
	console.log(config[7]);

	// Protect everything (start with first data page)
	config[3] = 0x04;

	// set ACCESS bits
	// bit 7: PROT One bit inside the ACCESS byte defining the memory protection
	//          0b ... write access is protected by the password verification
	//          1b ... read and write access is protected by the password verification
	// bit 6: CFGLCK Write locking bit for the user configuration
	//        - 0b ... user configuration open to write access
	//        - 1b ... user configuration permanently locked against write access
	// bits 5-3: reserved
	// bits 2-0: AUTHLIM
	// bit number-76543210
	//            ||||||||
	config[4] = 0b10000000;

	// set custom access rules
	await reader.write(227, config);

} catch (err) {
	console.error(err);
}

Thanks again 👍

@pokusew
Copy link
Owner

pokusew commented Feb 21, 2018

@bierlair That's great. 👍

So everything works as expected? Can we close the issue?

BTW I think, it would be useful to have this in the examples. Thank you very much for pointing it out to me. In future, the examples should explain the basic usage and commands of the most popular cards and tags to help people to get started easily. From my experience, I can say, that regarding NFC standards and cards, there is a lack of documentation (or it is poor, hard to find and understand, especially for beginners).


PS Don't forget to star ⭐️ my library, if you find it useful. 😃 Thanks.

@bierlair
Copy link
Author

bierlair commented Feb 21, 2018

Everything works as expected. With this I was actually able to break it down even further.
I would like to detect which kind of NTAG chip I am using, as they all come with different pages for storing passwords and configs.

Here is something quick and dirty that helps identify the NTAG chip. Next step would be to return the appropriate offsets in an object property, such as model.offset = 123

	async getModel(reader) {

		try {

			let cmd = Buffer.from([
				0xFF, // Class
				0x00, // Direct Transmit (see ACR122U)
				0x00, // ...
				0x00, // ...
				0x03, // Length of Direct Transmit payload
				0xd4, // Data Exchange Command (see PN533 docs)
				0x42, // inserted
				0x60, // GET_VERSION
			]);

			let response = await reader.transmit(cmd, 13);

			// console.log('+--- Vendor    :', response[4]);
			// console.log('+--- Type      :', response[5]);
			// console.log('+--- SubType   :', response[6]);
			// console.log('+--- Version 0 :', response[7]);
			// console.log('+--- Version 1 :', response[8]);
			// console.log('+--- Storage   :', response[9]);

			let model = {};
			model.name = null;
			model.page = null;

			if(response[4] === 4 && response[5] === 4) {
				if(response[6] === 1 && response[7] === 1 && response[8] === 0 && response[9] === 11) {
					model.name = "NTAG210";
				} else if(response[6] === 1 && response[7] === 1 && response[8] === 0 && response[9] === 14) {
					model.name = "NTAG212";
				} else if(response[6] === 2 && response[7] === 1 && response[8] === 0 && response[9] === 15) {
					model.name = "NTAG213";
				} else if(response[6] === 4 && response[7] === 1 && response[8] === 0 && response[9] === 15) {
					model.name = "NTAG213F";
				} else if(response[6] === 2 && response[7] === 1 && response[8] === 0 && response[9] === 17) {
					model.name = "NTAG215";
				} else if(response[6] === 2 && response[7] === 1 && response[8] === 0 && response[9] === 19) {
					model.name = "NTAG216";
				} else if(response[6] === 4 && response[7] === 1 && response[8] === 0 && response[9] === 19) {
					model.name = "NTAG216F";
				}
			}
			return model;

		} catch (err) {
			console.error('get_version error', err);
		}

Reference guide for this:

// get version
> FF 00 00 00 03 D4 42 60
// response
< D5 43 00 00 04 04 02 01 00 13 03 90 00


Details:

            Vendor (04 = NXP)
            |
D5 43 00 00 04   04    02        01   00      13    03 90 00
                 |      |         |   |       |
+------------+------+---------+-----------+--------------+
| Chip       | Type | Subtype | Version   | Storage size |
+------------+------+---------+-----------+--------------+
| NTAG210    | 0x04 | 0x01    | 0x01 0x00 | 0x0B         |
| NTAG212    | 0x04 | 0x01    | 0x01 0x00 | 0x0E         |
| NTAG213    | 0x04 | 0x02    | 0x01 0x00 | 0x0F         |
| NTAG213F   | 0x04 | 0x04    | 0x01 0x00 | 0x0F         |
| NTAG215    | 0x04 | 0x02    | 0x01 0x00 | 0x11         |
| NTAG216    | 0x04 | 0x02    | 0x01 0x00 | 0x13         |
| NTAG216F   | 0x04 | 0x04    | 0x01 0x00 | 0x13         |
+------------+------+---------+-----------+--------------+
| NT3H1101   | 0x04 | 0x02    | 0x01 0x01 | 0x13         |
| NT3H1101W0 | 0x04 | 0x05    | 0x02 0x01 | 0x13         |
| NT3H2111W0 | 0x04 | 0x05    | 0x02 0x02 | 0x13         |
| NT3H2101   | 0x04 | 0x02    | 0x01 0x01 | 0x15         |
| NT3H1201W0 | 0x04 | 0x05    | 0x02 0x01 | 0x15         |
| NT3H2211W0 | 0x04 | 0x05    | 0x02 0x02 | 0x15         |
+------------+------+---------+-----------+--------------+
| MF0UL1101  | 0x03 | 0x01    | 0x01 0x00 | 0x0B         |
| MF0ULH1101 | 0x03 | 0x02    | 0x01 0x00 | 0x0B         |
| MF0UL2101  | 0x03 | 0x01    | 0x01 0x00 | 0x0E         |
| MF0ULH2101 | 0x03 | 0x02    | 0x01 0x00 | 0x0E         |
+------------+------+---------+-----------+--------------+

==> NTAG Technical Doc, Table 28

And yes, NFC technical guides are tough to dig through. So good code examples would help beginners to kick off faster.

Cheers :-)

@lokachun
Copy link

@pokusew Hello, I am currently using ACS ACR 1252 1S CL Reader PICC 0 for my Mifare Ultralight. I wish to add a password to my tag so that others can write on it if they enter the password. Could I know what platform are you guys using to code and link with the reader because I am unable to find it. Thank you!

@dcr31000
Copy link

dcr31000 commented Jun 16, 2021

Hello, I want to add a password protection after writing data on NTAG216. I tried @bierlair code sample but the last writing command break my nfc cards...

This in my code:

// PASSWORD (4 bytes) (stored on card in page 18)
const CARD_PASSWORD = Buffer.from([
    0xff,
    0xff,
    0xff,
    0xff,
]);

// PACK (2 bytes) (stored in page 19 as first two bytes)
// PACK is the response from card in case of successful PWD_AUTH cmd
const PACK = Buffer.from([
	0xab,
	0xcd
]);

// set custom PASSWORD (4 bytes) (stored in page 229)
await reader.write(229, CARD_PASSWORD);

// set custom PACK (2 bytes) (stored in page 230 as first two bytes)
const packPage = await reader.read(230, 4);
packPage[0] = PACK[0];
packPage[1] = PACK[1];
await reader.write(230, packPage);

const config = await reader.read(227, 8);
config[3] = 0x04;
config[4] = 0b10000000;

await reader.write(227, config);

Could you please help me to find a solution ? Thanks all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants