All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diozero.devices.MFRC522 Maven / Gradle / Ivy

There is a newer version: 1.4.1
Show newest version
package com.diozero.devices;

/*
 * #%L
 * Organisation: diozero
 * Project:      Device I/O Zero - Core
 * Filename:     MFRC522.java  
 * 
 * This file is part of the diozero project. More information about this project
 * can be found at http://www.diozero.com/
 * %%
 * Copyright (C) 2016 - 2021 diozero
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */


import java.io.Closeable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.tinylog.Logger;

import com.diozero.api.DigitalOutputDevice;
import com.diozero.api.SpiConstants;
import com.diozero.api.SpiDevice;
import com.diozero.util.Hex;
import com.diozero.util.SleepUtil;

/**
 * 

Datasheet
* Example Python code
* MiFare Classic Universal toolKit (MFCUK)
* Work-in-progress!

*

Wiring:

*
    *
  • SDA: SPI0 CE0 (GPIO8)
  • *
  • SCK: SPI0 SCLK (GPIO11)
  • *
  • MOSI: SPI0 MOSI (GPIO10)
  • *
  • MISO: SPI0 MISO (GPIO9)
  • *
  • IRQ: Not connected
  • *
  • GND: GND
  • *
  • RST: Any free GPIO (GPIO25)
  • *
  • 3v3: 3v3
  • *
* *

Java port from MFRC522 Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT that was created by Miguel Balboa (circuitito.com).

* *

There are three hardware components involved:

*
    *
  1. The controller, e.g. Arduino, Raspberry Pi
  2. *
  3. The PCD (Proximity C oupling Device), e.g. NXP MFRC522 Contactless Reader
  4. *
  5. The PICC (Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, e.g. Mifare or NTAG203.
  6. *
* *

The microcontroller and card reader uses SPI for communication. * The protocol is described in the MFRC522 datasheet.

* *

The card reader and the tags communicate using a 13.56MHz electromagnetic field. * The protocol is defined in http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf:

*
    *
  • ISO/IEC 14443-3 Identification cards
  • *
  • Contactless integrated circuit cards
  • *
  • Proximity cards
  • *
  • Part 3: Initialization and anticollision"
  • *
  • Chapter 6, Type A ? Initialization and anticollision
  • *
* *

If only the PICC UID is wanted, the above documents have all the information needed.
* To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected.
* The MIFARE Classic and Ultralight chips and protocols are described in the datasheets:

* * *

MIFARE Classic 1K (MF1S503x)

*

Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes.
* The blocks are numbered 0-63.
* Block 3 in each sector is the Sector Trailer. See sections 8.6 and 8.7:

*
 *   Bytes 0-5:   Key A
 *   Bytes 6-8:   Access Bits
 *   Bytes 9:     User data
 *   Bytes 10-15: Key B (or user data)
 * 
*

Block 0 is read-only manufacturer data, sometimes as follows:

*
 * 0 1 2 3 |4  | 5 |6   |7 8 9 A B C D E F
 * UID     |BCC|SAK|ATAQ|Manufacturer data
 * 
*

To access a block, an authentication using a key from the block's sector must be performed first.
* Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11).
* All keys are set to FFFFFFFFFFFFh at chip delivery.
* Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked.
* To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use setAccessBits() to calculate the bit patterns.

* *

MIFARE Classic 4K (MF1S703x)

*

Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes.
* The blocks are numbered 0-255.
* The last block in each sector is the Sector Trailer like above.

* *

MIFARE Classic Mini (MF1 IC S20)

*

Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes.
* The blocks are numbered 0-19.
* The last block in each sector is the Sector Trailer like above.

* *

MIFARE Ultralight (MF0ICU1)

*

Has 16 pages of 4 bytes = 64 bytes.
* Pages 0 + 1 is used for the 7-byte UID.
* Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2).
* Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0.
* Pages 4-15 are read/write unless blocked by the lock bytes in page 2.

* *

MIFARE Ultralight C (MF0ICU2)

*

Has 48 pages of 4 bytes = 192 bytes.
* Pages 0 + 1 is used for the 7-byte UID.
* Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2).
* Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0.
* Pages 4-39 are read/write unless blocked by the lock bytes in page 2.
* Page 40 Lock bytes
* Page 41 16 bit one way counter
* Pages 42-43 Authentication configuration
* Pages 44-47 Authentication key

* *

Access conditions for sector trailer

*
 *   KeyA  Bits  KeyB
 *   R  W  R  W  R  W
 * 0 -  A  A  -  A  A
 * 1 -  A  A  A  A  A Default
 * 2 -  -  A  -  A  -
 * 3 -  B  AB B  -  B
 * 4 -  B  AB -  -  B
 * 5 -  -  AB B  -  -
 * 6 -  -  AB -  -  -
 * 7 -  -  AB -  -  -
 * 
* *

Access conditions for data blocks

*
 *     R   W   +  -X
 * 0  AB  AB  AB  AB Data  Default
 * 1  AB  --  --  AB Value
 * 2  AB  --  --  -- Data
 * 3   B   B  --  -- Data
 * 4  AB   B  --  -- Data
 * 5   B  --  --  -- Data
 * 6  AB   B   B  AB Value
 * 7  --  --  --  -- Data
 * 
* *

Workflow

*
    *
  1. Call ISO_Request() to check to see if a tag is in range and if so get its ATQA.
  2. *
  3. Upon success call ISO_Anticollision() which returns the UID of the active tag in range.
  4. *
  5. Upon success call ISO_Select(UID) which selects the active tag in range by its UID and returns its SAK.
  6. *
  7. If the card needs authentication call ISO_Authenticate(blockAddr, keyId, key, UID) to authenticate access to a block.
  8. *
  9. Call ISO_StopCrypto() when you have finished talking to a card which requires authentication.
  10. *
*/ public class MFRC522 implements Closeable { public static final byte[] DEFAULT_KEY = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; // Firmware data for self-test // Reference values based on firmware version // Hint: if needed, you can remove unused self-test data to save flash memory // // Version 0.0 (0x90) // Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 self-test public static final byte[] MFRC522_firmware_referenceV0_0 = { 0x00, (byte) 0x87, (byte) 0x98, 0x0f, 0x49, (byte) 0xFF, 0x07, 0x19, (byte) 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, (byte) 0xAD, (byte) 0xCA, 0x7F,(byte) 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, 0x47, (byte) 0x9A, 0x37, 0x61, (byte) 0xE7, (byte) 0xE2, (byte) 0xC6, 0x2E, 0x75, 0x5A, (byte) 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, 0x32, (byte) 0xFF, 0x58, 0x3B, 0x7C, (byte) 0xE9, 0x00, (byte) 0x94, (byte) 0xB4, 0x4A, 0x59, 0x5B, (byte) 0xFD, (byte) 0xC9, 0x29, (byte) 0xDF, 0x35, (byte) 0x96, (byte) 0x98, (byte) 0x9E, 0x4F, 0x30, 0x32, (byte) 0x8D }; // Version 1.0 (0x91) // NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 self-test public static final byte[] MFRC522_firmware_referenceV1_0 = { 0x00, (byte) 0xC6, 0x37, (byte) 0xD5, 0x32, (byte) 0xB7, 0x57, 0x5C, (byte) 0xC2, (byte) 0xD8, 0x7C, 0x4D, (byte) 0xD9, 0x70, (byte) 0xC7, 0x73, 0x10, (byte) 0xE6, (byte) 0xD2, (byte) 0xAA, 0x5E, (byte) 0xA1, 0x3E, 0x5A, 0x14, (byte) 0xAF, 0x30, 0x61, (byte) 0xC9, 0x70, (byte) 0xDB, 0x2E, 0x64, 0x22, 0x72, (byte) 0xB5, (byte) 0xBD, 0x65, (byte) 0xF4, (byte) 0xEC, 0x22, (byte) 0xBC, (byte) 0xD3, 0x72, 0x35, (byte) 0xCD, (byte) 0xAA, 0x41, 0x1F, (byte) 0xA7, (byte) 0xF3, 0x53, 0x14, (byte) 0xDE, 0x7E, 0x02, (byte) 0xD9, 0x0F, (byte) 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 }; // Version 2.0 (0x92) // NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 self-test public static final byte[] MFRC522_firmware_referenceV2_0 = { 0x00, (byte) 0xEB, (byte) 0x66, (byte) 0xBA, 0x57, (byte) 0xBF, 0x23, (byte) 0x95, (byte) 0xD0, (byte) 0xE3, 0x0D, 0x3D, 0x27, (byte) 0x89, 0x5C, (byte) 0xDE, (byte) 0x9D, 0x3B, (byte) 0xA7, 0x00, 0x21, 0x5B, (byte) 0x89, (byte) 0x82, 0x51, 0x3A, (byte) 0xEB, 0x02, 0x0C, (byte) 0xA5, 0x00, 0x49, 0x7C, (byte) 0x84, 0x4D, (byte) 0xB3, (byte) 0xCC, (byte) 0xD2, 0x1B, (byte) 0x81, 0x5D, 0x48, 0x76, (byte) 0xD5, 0x71, 0x61, 0x21, (byte) 0xA9, (byte) 0x86, (byte) 0x96, (byte) 0x83, 0x38, (byte) 0xCF, (byte) 0x9D, 0x5B, 0x6D, (byte) 0xDC, 0x15, (byte) 0xBA, 0x3E, 0x7D, (byte) 0x95, 0x3B, 0x2F }; // Clone // Fudan Semiconductor FM17522 (0x88) public static final byte[] FM17522_firmware_reference = { 0x00, (byte) 0xD6, 0x78, (byte) 0x8C, (byte) 0xE2, (byte) 0xAA, 0x0C, 0x18, 0x2A, (byte) 0xB8, 0x7A, 0x7F, (byte) 0xD3, (byte) 0x6A, (byte) 0xCF, 0x0B, (byte) 0xB1, 0x37, 0x63, 0x4B, 0x69, (byte) 0xAE, (byte) 0x91, (byte) 0xC7, (byte) 0xC3, (byte) 0x97, (byte) 0xAE, 0x77, (byte) 0xF4, 0x37, (byte) 0xD7, (byte) 0x9B, 0x7C, (byte) 0xF5, 0x3C, 0x11, (byte) 0x8F, 0x15, (byte) 0xC3, (byte) 0xD7, (byte) 0xC1, 0x5B, 0x00, 0x2A, (byte) 0xD0, 0x75, (byte) 0xDE, (byte) 0x9E, 0x51, 0x64, (byte) 0xAB, 0x3E, (byte) 0xE9, 0x15, (byte) 0xB5, (byte) 0xAB, 0x56, (byte) 0x9A, (byte) 0x98, (byte) 0x82, 0x26, (byte) 0xEA, 0x2A, 0x62 }; // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. public static final byte MF_ACK = 0xA; // A Mifare Crypto1 key is 6 bytes. public static final byte MF_KEY_SIZE = 6; // AddicoreRFID error codes public static final byte MI_OK = 0; public static final byte MI_NOTAGERR = 1; public static final byte MI_ERR = 2; private static final int SPI_CLOCK_FREQUENCY = 1_000_000; private SpiDevice device; private DigitalOutputDevice resetPin; private boolean logReadsAndWrites = false; public MFRC522(int chipSelect, int resetGpio) { this(SpiConstants.DEFAULT_SPI_CONTROLLER, chipSelect, resetGpio); } public MFRC522(int controller, int chipSelect, int resetGpio) { this(controller, chipSelect, new DigitalOutputDevice(resetGpio, true, false)); } public MFRC522(int controller, int chipSelect, DigitalOutputDevice resetPin) { device = SpiDevice.builder(chipSelect).setController(controller).setFrequency(SPI_CLOCK_FREQUENCY).build(); this.resetPin = resetPin; init(); } @Override public void close() { if (device != null) { device.close(); } } public void setLogReadsAndWrites(boolean logReadsAndWrites) { this.logReadsAndWrites = logReadsAndWrites; } ///////////////////////////////////////////////////////////////////////////////////// // Basic interface functions for communicating with the MFRC522 ///////////////////////////////////////////////////////////////////////////////////// private void writeRegister(PcdRegister register, PcdCommand command) { writeRegister(register, command.getValue()); } private void writeRegister(PcdRegister register, byte value) { if (logReadsAndWrites) { Logger.debug("(0x{}, 0x{})", Integer.toHexString(register.getValue() & 0xff), Integer.toHexString(value & 0xff)); } byte[] tx = new byte[2]; // Address Format: 0XXXXXX0, the left most "0" indicates a write tx[0] = register.getAddress(); tx[1] = value; device.write(tx); } private void writeRegister(PcdRegister register, byte[] values) { writeRegister(register, values, values.length); } private void writeRegister(PcdRegister register, byte[] values, int length) { if (logReadsAndWrites) { Logger.debug("(0x{}, 0x{}, {} bytes)", Integer.toHexString(register.getValue() & 0xff), Hex.encodeHexString(values), Integer.valueOf(length)); } byte[] tx = new byte[length+1]; // Address Format: 0XXXXXX0, the left most "0" indicates a write tx[0] = register.getAddress(); System.arraycopy(values, 0, tx, 1, length); device.write(tx); } private byte readRegister(PcdRegister register) { byte[] tx = new byte[2]; // Address Format: 1XXXXXX0, the first "1" indicates a read tx[0] = (byte) (register.getAddress() | 0x80); tx[1] = (byte) 0; byte[] rx = device.writeAndRead(tx); if (logReadsAndWrites) { Logger.debug("(0x{}): 0x{}", Integer.toHexString(register.getValue() & 0xff), Integer.toHexString(rx[1] & 0xff)); } return rx[1]; } private byte[] readRegister(PcdRegister register, int count, byte rxAlign) { if (count == 0) { return null; } // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. byte address = (byte) (register.getAddress() | 0x80); byte[] tx = new byte[count+1]; int i; for (i=0; i fTimer = 1999 // total time delay = ((TPrescaler * 2 + 1) * (TReloadVal + 1)) / 13.56 MHz // 203430 / 13560000 = 0.015 = 0.015 // Timer: TPrescaler*TReloadVal/6.78MHz = 24ms /* writeRegister(T_MODE_REG, (byte) 0x8D); // Tauto=1; f(Timer) = 6.78MHz/TPreScaler writeRegister(T_PRESCALER_REG, (byte) 0x3E); // TModeReg[3..0] + TPrescalerReg // 30 writeRegister(T_RELOAD_REG_MSB, (byte) 0); writeRegister(T_RELOAD_REG_LSB, (byte) 0x1E); writeRegister(TX_ASK_REG, (byte) 0x40); // 100%ASK writeRegister(MODE_REG, (byte) 0x3D); // CRC Initial value 0x6363 ??? */ // TPrescaler: 000010101001 = 169; 13_560_000 / 169 -> fTimer = 80236 // TReload: 11111101000 = 2024 // ((169 * 2 + 1) * (2024 + 1)) / 13.56 MHz = 0.050 // joan: //self._PCDWrite(self._TModeReg, 0x80) //self._PCDWrite(self._TPrescalerReg, 0xA9) //self._PCDWrite(self._TReloadRegH, 0x03) //self._PCDWrite(self._TReloadRegL, 0xe8) //self._PCDWrite(self._TxASKReg, 0x40) //#self._PCDWrite(self._ModeReg, 0x3D) //self._PCDWrite(self._ModeReg, 0x29) // OLD CODE - END // When communicating with a PICC we need a timeout if something goes wrong. // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. writeRegister(PcdRegister.T_MODE_REG, (byte) 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds writeRegister(PcdRegister.T_PRESCALER_REG, (byte) 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25us. writeRegister(PcdRegister.T_RELOAD_REG_MSB, (byte) 0x03); // Reload timer with 0x03E8 = 1000, ie 25ms before timeout. writeRegister(PcdRegister.T_RELOAD_REG_LSB, (byte) 0xE8); writeRegister(PcdRegister.TX_ASK_REG, (byte) 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting writeRegister(PcdRegister.MODE_REG, (byte) 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) setAntennaOn(true); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) } /** * Perform soft reset of AddicoreRFID Module */ private void reset() { writeRegister(PcdRegister.COMMAND_REG, PcdCommand.SOFT_RESET); // The datasheet does not mention how long the SoftRest command takes to complete. // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74us. Let us be generous: 50ms. SleepUtil.sleepMillis(50); while ((readRegister(PcdRegister.COMMAND_REG) & (1<<4)) != 0) { // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. } } /** * Open antennas, each time you start or shut down the natural barrier * between the transmitter should be at least 1ms interval * * @param on * on/off value */ public void setAntennaOn(boolean on) { if (on) { byte value = readRegister(PcdRegister.TX_CONTROL_REG); if ((value & 0x03) != 0x03) { writeRegister(PcdRegister.TX_CONTROL_REG, (byte) (value | 0x03)); } } else { clearBitMask(PcdRegister.TX_CONTROL_REG, (byte) 0x03); } } /** * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. * * @return Value of the RxGain, scrubbed to the 3 bits used. */ public AntennaGain getAntennaGain() { return AntennaGain.forValue((byte) (readRegister(PcdRegister.RF_CONFIG_REG) & (0x07<<4))); } /** * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. * @param gain New antenna gain value */ public void setAntennaGain(AntennaGain gain) { if (getAntennaGain() != gain) { // only bother if there is a change clearBitMask(PcdRegister.RF_CONFIG_REG, (byte) (0x07<<4)); // clear needed to allow 000 pattern setBitMask(PcdRegister.RF_CONFIG_REG, gain.getValue()); // only set RxGain[2:0] bits } } /** * Performs a self-test of the MFRC522 * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf * * @return Whether or not the test passed. Or false if no firmware reference is available. */ public boolean performSelfTest() { Logger.debug("Self test - START"); // This follows directly the steps outlined in 16.1.1 // 1. Perform a soft reset. reset(); // 2. Clear the internal buffer by writing 25 bytes of 00h byte[] ZEROES = new byte[25]; writeRegister(PcdRegister.FIFO_LEVEL_REG, (byte) 0x80); // flush the FIFO buffer writeRegister(PcdRegister.FIFO_DATA_REG, ZEROES); // write 25 bytes of 00h to FIFO writeRegister(PcdRegister.COMMAND_REG, PcdCommand.MEM); // transfer to internal buffer // 3. Enable self-test writeRegister(PcdRegister.AUTO_TEST_REG, (byte) 0x09); // 4. Write 00h to FIFO buffer writeRegister(PcdRegister.FIFO_DATA_REG, (byte) 0x00); // 5. Start self-test by issuing the CalcCRC command writeRegister(PcdRegister.COMMAND_REG, PcdCommand.CALC_CRC); // 6. Wait for self-test to complete byte n; for (int i=0; i<0xFF; i++) { // The datasheet does not specify exact completion condition except // that FIFO buffer should contain 64 bytes. // While selftest is initiated by CalcCRC command // it behaves differently from normal CRC computation, // so one can't reliably use DivIrqReg to check for completion. // It is reported that some devices does not trigger CRCIRq flag // during selftest. n = readRegister(PcdRegister.FIFO_LEVEL_REG); if (n >= 64) { break; } } writeRegister(PcdRegister.COMMAND_REG, PcdCommand.IDLE); // Stop calculating CRC for new content in the FIFO. // 7. Read out resulting 64 bytes from the FIFO buffer. //byte[] result = readRegister(FIFO_DATA_REG, 64); byte[] result = new byte[64]; for (int i=0; i The last two bytes of the response is assumed to be a * CRC_A that must be validated. * @return STATUS_OK on success, STATUS_??? otherwise. */ private Response transceiveData(byte[] sendData, byte validBits, byte rxAlign, boolean checkCRC) { byte waitIRq = 0x30; // RxIRq and IdleIRq return communicateWithPICC(PcdCommand.TRANSCEIVE, waitIRq, sendData, validBits, rxAlign, checkCRC); } private Response communicateWithPICC(PcdCommand command, byte waitIRq, byte[] sendData) { return communicateWithPICC(command, waitIRq, sendData, (byte) 0, (byte) 0, false); } private Response communicateWithPICC(PcdCommand command, byte waitIRq, byte[] sendData, byte validBits) { return communicateWithPICC(command, waitIRq, sendData, validBits, (byte) 0, false); } /** * Transfers data to the MFRC522 FIFO, executes a command, waits for * completion and transfers data back from the FIFO. CRC validation can only * be done if backData and backLen are specified. * * @param command * The command to execute. One of the PCD_Command enums. * @param waitIRq * The bits in the ComIrqReg register that signals successful * completion of the command. * @param sendData * Data to transfer to the FIFO. * @param validBits * The number of valid bits in the last byte. 0 for 8 valid bits. * @param rxAlign * Defines the bit position in backData[0] for the first bit * received. Default 0. * @param checkCRC * True => The last two bytes of the response is assumed to be a * CRC_A that must be validated. * @return response */ private Response communicateWithPICC(PcdCommand command, byte waitIRq, byte[] sendData, byte validBits, byte rxAlign, boolean checkCRC) { // Prepare values for BitFramingReg byte tx_last_bits = validBits; byte bit_framing = (byte) ((rxAlign << 4) + tx_last_bits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] writeRegister(PcdRegister.COMMAND_REG, PcdCommand.IDLE); // Stop any active command. writeRegister(PcdRegister.COM_IRQ_REG, (byte) 0x7f); // Clear all seven interrupt request bits writeRegister(PcdRegister.FIFO_LEVEL_REG, (byte) 0x80); // FlushBuffer=1, FIFO Initialisation writeRegister(PcdRegister.FIFO_DATA_REG, sendData); // Write sendData to the FIFO writeRegister(PcdRegister.BIT_FRAMING_REG, bit_framing); // Bit adjustments writeRegister(PcdRegister.COMMAND_REG, command); // Execute the command if (command == PcdCommand.TRANSCEIVE) { setBitMask(PcdRegister.BIT_FRAMING_REG, (byte) 0x80); // StartSend=1, transmission of data starts } // Wait for the command to complete. // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer // automatically starts when the PCD stops transmitting. long start_ms = System.currentTimeMillis(); boolean timeout = true; do { byte n = readRegister(PcdRegister.COM_IRQ_REG); if ((n & waitIRq) != 0) { // One of the interrupts that signal success has been set. timeout = false; break; } // Timer interrupt - nothing received in 25ms if ((n & 0x01) != 0) { Logger.debug("timer interrupt, n: 0x" + Integer.toHexString(n & 0xff)); break; } } while ((System.currentTimeMillis() - start_ms) < 200); // 35.7ms and nothing happend. Communication with the MFRC522 might be down. if (timeout) { Logger.debug("Timed out waiting for interrupt"); return new Response(StatusCode.TIMEOUT); } // StartSend=0 //clearBitMask(PcdRegister.BIT_FRAMING_REG, (byte) 0x80); // Stop now if any errors except collisions were detected. // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr byte error_reg_val = readRegister(PcdRegister.ERROR_REG); // BufferOvfl ParityErr ProtocolErr if ((error_reg_val & 0x13) != 0) { Logger.error("*** Error reg val: 0x" + Integer.toHexString(error_reg_val & 0xff)); return new Response(StatusCode.ERROR); } int back_len = 0; byte valid_bits = 0; // If the caller wants data back, get it from the MFRC522. byte[] back_data = new byte[0]; // TODO Check this is equivalent //if (backData && backLen) { // FIXME Also TRANSCEIVE when called from haltA() if (command != PcdCommand.MF_AUTHENT) { // Number of bytes in the FIFO back_len = readRegister(PcdRegister.FIFO_LEVEL_REG) & 0xff; Logger.debug("FIFO Level: " + back_len); // Get received data from FIFO /* back_data = new byte[back_len]; for (int index=0; index 0 && checkCRC) { Logger.debug("Checking CRC"); // In this case a MIFARE Classic NAK is not OK. if (back_len == 1 && valid_bits == 4) { Logger.error("*** MIFARE Classic NAK is not ok"); return new Response(StatusCode.MIFARE_NACK); } // We need at least the CRC_A value and all 8 bits of the last byte must be received. if (back_len < 2 || valid_bits != 0) { Logger.error("*** CRC was wrong"); return new Response(StatusCode.CRC_WRONG); } // Verify CRC_A - do our own calculation and store the control in controlBuffer. byte[] control_buffer = calculateCRC(back_data, back_len-2); if (control_buffer == null) { Logger.error("*** Control buffer from PCD_CalculateCRC was null"); return new Response(StatusCode.TIMEOUT); } if ((back_data[back_len-2] != control_buffer[0]) || (back_data[back_len-1] != control_buffer[1])) { Logger.error("*** CRC was wrong"); return new Response(StatusCode.CRC_WRONG); } } Logger.debug("ok"); return new Response(StatusCode.OK, back_data, back_len, valid_bits); } /** * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * * @param bufferATQA The buffer to store the ATQA (Answer to request) in * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode requestA(byte[] bufferATQA) { return requestAOrWakeUpA(PiccCommand.REQUEST_A, bufferATQA); } /** * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * * @param bufferATQA The buffer to store the ATQA (Answer to request) in * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode wakeupA(byte[] bufferATQA) { return requestAOrWakeUpA(PiccCommand.WAKE_UP_A, bufferATQA); } /** * Transmits REQA or WUPA commands. * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. * * @param command The command to send - PICC_CMD_REQA or PICC_CMD_WUPA * @param bufferATQA The buffer to store the ATQA (Answer to request) in * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode requestAOrWakeUpA(PiccCommand command, byte[] bufferATQA) { // The ATQA response is 2 bytes long. if (bufferATQA == null || bufferATQA.length != 2) { return StatusCode.NO_ROOM; } // ValuesAfterColl=1 => Bits received after collision are cleared. clearBitMask(PcdRegister.COLL_REG, (byte) 0x80); // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] byte valid_bits = 7; Response response = transceiveData(new byte[] { command.getValue() }, valid_bits); valid_bits = response.getValidBits(); if (response.getStatus() != StatusCode.OK) { return response.getStatus(); } byte[] back_data = response.getBackData(); int back_len = response.getBackLen(); // ATQA must be exactly 16 bits. if (back_len != 2 || response.getValidBits() != 0) { return StatusCode.ERROR; } System.arraycopy(back_data, 0, bufferATQA, 0, back_len); return StatusCode.OK; } public UID select() { return select((byte) 0); } /** * Transmits SELECT/ANTICOLLISION commands to select a single PICC. * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). * On success: * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.) * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. * * A PICC UID consists of 4, 7 or 10 bytes. * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: * UID size Number of UID bytes Cascade levels Example of PICC * ======== =================== ============== =============== * single 4 1 MIFARE Classic * double 7 2 MIFARE Ultralight * triple 10 3 Not currently in use? * * @param validBits The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. * @return UID object or null if there was an error. */ public UID select(byte validBits) { byte[] buffer = new byte[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A // Description of buffer structure: // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. // Byte 3: UID-data // Byte 4: UID-data // Byte 5: UID-data // Byte 6: BCC Block Check Character - XOR of bytes 2-5 // Byte 7: CRC_A // Byte 8: CRC_A // The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level. // // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) // UID size Cascade level Byte2 Byte3 Byte4 Byte5 // ======== ============= ===== ===== ===== ===== // 4 bytes 1 uid0 uid1 uid2 uid3 // 7 bytes 1 CT uid0 uid1 uid2 // 2 uid3 uid4 uid5 uid6 // 10 bytes 1 CT uid0 uid1 uid2 // 2 CT uid3 uid4 uid5 // 3 uid6 uid7 uid8 uid9 // Sanity checks if (validBits > 80) { Logger.error("*** Error: validBits ({}) was > 80", Byte.valueOf(validBits)); return null; } // Prepare MFRC522 clearBitMask(PcdRegister.COLL_REG, (byte) 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. // Repeat Cascade Level loop until we have a complete UID. boolean uid_complete = false; boolean use_cascade_tag; byte cascade_level = 1; List uid_bytes = new ArrayList<>(); byte uid_sak = 0; byte uid_index; // The first index in uid->uidByte[] that is used in the current Cascade Level. int current_level_known_bits; // The number of known UID bits in the current Cascade Level. int response_buffer_offset; while (!uid_complete) { Logger.debug("uid_complete: " + uid_complete + ", cascade_level: " + cascade_level); // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. switch (cascade_level) { case 1: buffer[0] = PiccCommand.SEL_CL1.getValue(); uid_index = 0; use_cascade_tag = (validBits != 0) && (uid_bytes.size() > 4); // When we know that the UID has more than 4 bytes break; case 2: buffer[0] = PiccCommand.SEL_CL2.getValue(); uid_index = 3; use_cascade_tag = (validBits != 0) && (uid_bytes.size() > 7); // When we know that the UID has more than 7 bytes break; case 3: buffer[0] = PiccCommand.SEL_CL3.getValue(); uid_index = 6; use_cascade_tag = false; // Never used in CL3. break; default: Logger.error("*** Error: invalid cascade_level ()", Byte.valueOf(cascade_level)); return null; } // How many UID bits are known in this Cascade Level? current_level_known_bits = validBits - (8 * uid_index); if (current_level_known_bits < 0) { current_level_known_bits = 0; } // Copy the known bits from uid->uidByte[] to buffer[] int index = 2; // destination index in buffer[] if (use_cascade_tag) { buffer[index++] = PiccCommand.CASCADE_TAG.getValue(); } // The number of bytes needed to represent the known bits for this level. int bytesToCopy = current_level_known_bits / 8 + (((current_level_known_bits % 8) != 0) ? 1 : 0); if (bytesToCopy != 0) { // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag int maxBytes = use_cascade_tag ? 3 : 4; if (bytesToCopy > maxBytes) { bytesToCopy = maxBytes; } for (int count=0; count= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. Logger.debug("SELECT: current_level_known_bits={}", Integer.valueOf(current_level_known_bits)); buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes // Calculate BCC - Block Check Character buffer[6] = (byte) (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]); // Calculate CRC_A //result = PCD_CalculateCRC(buffer, 7, &buffer[7]); byte[] crc = calculateCRC(buffer, 7); if (crc == null) { Logger.error("*** Error calculating CRC"); return null; } // Note C++ code stores CRC in the last 2 bytes of buffer System.arraycopy(crc, 0, buffer, 7, crc.length); tx_last_bits = 0; // 0 => All 8 bits are valid. buffer_used = 9; // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) //responseBuffer = &buffer[6]; //responseLength = 3; response_buffer_offset = 6; response_length = crc.length + 1; } else { // This is an ANTICOLLISION. Logger.debug("ANTICOLLISION: current_level_known_bits={}", Integer.valueOf(current_level_known_bits)); tx_last_bits = current_level_known_bits % 8; int count = current_level_known_bits / 8; // Number of whole bytes in the UID part. index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs buffer[1] = (byte) ((index << 4) + tx_last_bits); // NVB - Number of Valid Bits buffer_used = index + ((tx_last_bits != 0) ? 1 : 0); // Store response in the unused part of buffer //responseBuffer = &buffer[index]; //responseLength = sizeof(buffer) - index; response_buffer_offset = index; response_length = buffer.length - index; } // Set bit adjustments // Having a separate variable is overkill. But it makes the next line easier to read. int rx_align = tx_last_bits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] writeRegister(PcdRegister.BIT_FRAMING_REG, (byte) ((rx_align << 4) + tx_last_bits)); Logger.debug("tx_last_bits: " + tx_last_bits); // Transmit the buffer and receive the response. byte[] tx_data = new byte[buffer_used]; System.arraycopy(buffer, 0, tx_data, 0, buffer_used); Response response = transceiveData(tx_data, (byte) tx_last_bits, (byte) rx_align); //result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); if (response.getBackData() == null) { Logger.error("*** got no back data from transcieveData, aborting"); return null; } System.arraycopy(response.getBackData(), 0, buffer, response_buffer_offset, response.getBackLen()); response_buffer = response.getBackData(); response_length = response.getBackLen(); tx_last_bits = response.getValidBits(); if (response.getStatus() == StatusCode.COLLISION) { // More than one PICC in the field => collision. byte valueOfCollReg = readRegister(PcdRegister.COLL_REG); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] if ((valueOfCollReg & 0x20) != 0) { // CollPosNotValid Logger.error("*** valueOfCollReg ({}) has bit 0x20 set", Byte.valueOf(valueOfCollReg)); return null; // Without a valid collision position we cannot continue } int collisionPos = valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32. if (collisionPos == 0) { collisionPos = 32; } if (collisionPos <= current_level_known_bits) { // No progress - should not happen Logger.error("*** collisionPos ({}) is <= current_level_known_bits ({})", Integer.valueOf(collisionPos), Integer.valueOf(current_level_known_bits)); //return StatusCode.INTERNAL_ERROR; return null; } // Choose the PICC with the bit set. current_level_known_bits = collisionPos; int count = (current_level_known_bits - 1) % 8; // The bit to modify index = 1 + (current_level_known_bits / 8) + (count != 0 ? 1 : 0); // First byte is index 0. buffer[index] |= (1 << count); } else if (response.getStatus() != StatusCode.OK) { Logger.error("*** Invalid response from PCD_TransceiveData: {}", response.getStatus()); //return response.getStatus(); return null; } else { // STATUS_OK if (current_level_known_bits >= 32) { // This was a SELECT. select_done = true; // No more anticollision // We continue below outside the while. } else { // This was an ANTICOLLISION. // We now have all 32 bits of the UID in this Cascade Level current_level_known_bits = 32; // Run loop again to do the SELECT. } } } // End of while (!selectDone) // We do not check the CBB - it was constructed by us above. // Copy the found UID bytes from buffer[] to uid->uidByte[] index = (buffer[2] == PiccCommand.CASCADE_TAG.getValue()) ? 3 : 2; // source index in buffer[] bytesToCopy = (buffer[2] == PiccCommand.CASCADE_TAG.getValue()) ? 3 : 4; for (int count=0; countsize //uid.setSize(3 * cascade_level + 1); if (uid.getSize() != (3 * cascade_level + 1)) { Logger.error("*** Expected UID size to be " + (3 * cascade_level + 1) + " but was " + uid.getSize()); } return uid; } /** * Instructs a PICC in state ACTIVE(*) to go to state HALT. * * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode haltA() { byte[] buffer = new byte[4]; // Build command buffer buffer[0] = PiccCommand.HALT_A.getValue(); buffer[1] = 0; // Calculate CRC_A byte[] crc = calculateCRC(buffer); if (crc == null) { return StatusCode.TIMEOUT; } System.arraycopy(crc, 0, buffer, 2, crc.length); // Send the command. // The standard says: // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the // HLTA command, this response shall be interpreted as 'not acknowledge'. // We interpret that this way: Only STATUS_TIMEOUT is a success. Response response = transceiveData(buffer); StatusCode result = response.getStatus(); if (result == StatusCode.TIMEOUT) { return StatusCode.OK; } if (result == StatusCode.OK) { // That is ironically NOT ok in this case ;-) return StatusCode.ERROR; } return result; } /** * Executes the MFRC522 MFAuthent command. * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1. * For use with MIFARE Classic PICCs. * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. * * All keys are set to FFFFFFFFFFFFh at chip delivery. * * @param authKeyA PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B * @param blockAddr The block number. See numbering in the comments in the .h file. * @param key Crypto1 key to use (6 bytes) * @param uid Pointer to Uid struct. The first 4 bytes of the UID is used. * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. */ public StatusCode authenticate(boolean authKeyA, byte blockAddr, byte[] key, UID uid) { Logger.debug("blockAddr: " + blockAddr); // Build command buffer byte[] sendData = new byte[12]; sendData[0] = authKeyA ? PiccCommand.MF_AUTH_KEY_A.getValue() : PiccCommand.MF_AUTH_KEY_B.getValue(); sendData[1] = blockAddr; System.arraycopy(key, 0, sendData, 2, key.length); /* for (byte i=0; i> 8); buffer[2] = (byte) ((data & 0xFF0000) >> 16); buffer[3] = (byte) ((data & 0xFF000000) >> 24); return mifareTransceive(buffer, true); // Adds CRC_A and accept timeout as success. } // End MIFARE_TwoStepHelper() /** * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. * * @param blockAddr The block (0-0xff) number. * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode mifareTransfer(byte blockAddr) { // Tell the PICC we want to transfer the result into block blockAddr. byte[] cmdBuffer = { PiccCommand.MF_TRANSFER.getValue(), blockAddr }; return mifareTransceive(cmdBuffer); // Adds CRC_A and checks that the response is MF_ACK. } // End MIFARE_Transfer() /** * Helper routine to read the current value from a Value Block. * * Only for MIFARE Classic and only for blocks in "value block" mode, that * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing * the block must be authenticated before calling this function. * * @param blockAddr The block (0x00-0xff) number. * @return Integer value or null if error. */ public Integer mifareGetValue(byte blockAddr) { // Read the block byte[] buffer = mifareRead(blockAddr); if (buffer == null) { return null; } // Extract the value return Integer.valueOf(((buffer[3] & 0xff)<<24) | ((buffer[2] & 0xff)<<16) | ((buffer[1] & 0xff)<<8) | (buffer[0] & 0xff)); } // End MIFARE_GetValue() /** * Helper routine to write a specific value into a Value Block. * * Only for MIFARE Classic and only for blocks in "value block" mode, that * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing * the block must be authenticated before calling this function. * * @param blockAddr The block (0x00-0xff) number. * @param value New value of the Value Block. * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode mifareSetValue(byte blockAddr, int value) { byte[] buffer = new byte[16]; // Translate the int32_t into 4 bytes; repeated 2x in value block buffer[0] = buffer[ 8] = (byte) (value & 0xFF); buffer[1] = buffer[ 9] = (byte) ((value & 0xFF00) >> 8); buffer[2] = buffer[10] = (byte) ((value & 0xFF0000) >> 16); buffer[3] = buffer[11] = (byte) ((value & 0xFF000000) >> 24); // Inverse 4 bytes also found in value block buffer[4] = (byte) ~buffer[0]; buffer[5] = (byte) ~buffer[1]; buffer[6] = (byte) ~buffer[2]; buffer[7] = (byte) ~buffer[3]; // Address 2x with inverse address 2x buffer[12] = buffer[14] = blockAddr; buffer[13] = buffer[15] = (byte) ~blockAddr; // Write the whole data block return mifareWrite(blockAddr, buffer); } // End MIFARE_SetValue() /* * Authenticate with a NTAG216. * * Only for NTAG216. First implemented by Gargantuanman. * * @param passWord password. * @param pACK result success???. * @return STATUS_OK on success, STATUS_??? otherwise. */ /* public StatusCode ntag216Auth(byte[] passWord, byte pACK[]) //Authenticate with 32bit password { // TODO: Fix cmdBuffer length and rxlength. They really should match. // (Better still, rxlength should not even be necessary.) StatusCode result; byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. cmdBuffer[0] = 0x1B; //Comando de autentificacion for (byte i = 0; i<4; i++) cmdBuffer[i+1] = passWord[i]; result = PCD_CalculateCRC(cmdBuffer, 5, &cmdBuffer[5]); if (result!=STATUS_OK) { return result; } // Transceive the data, store the reply in cmdBuffer[] byte waitIRq = 0x30; // RxIRq and IdleIRq // byte cmdBufferSize = sizeof(cmdBuffer); byte validBits = 0; byte rxlength = 5; result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, 7, cmdBuffer, &rxlength, &validBits); pACK[0] = cmdBuffer[0]; pACK[1] = cmdBuffer[1]; if (result!=STATUS_OK) { return result; } return STATUS_OK; } // End PCD_NTAG216_AUTH() */ ///////////////////////////////////////////////////////////////////////////////////// // Support functions ///////////////////////////////////////////////////////////////////////////////////// public StatusCode mifareTransceive(byte[] sendData) { return mifareTransceive(sendData, false); } /** * Wrapper for MIFARE protocol communication. * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. * * @param sendData Data to transfer to the FIFO. Do NOT include the CRC_A. * @param acceptTimeout A timeout is also success * @return STATUS_OK on success, STATUS_??? otherwise. */ public StatusCode mifareTransceive(byte[] sendData, boolean acceptTimeout) { // Sanity check if (sendData == null || sendData.length != 16) { return StatusCode.INVALID; } /* C++ Code: byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. // Copy sendData[] to cmdBuffer[] and add CRC_A memcpy(cmdBuffer, sendData, sendLen); result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); if (result != STATUS_OK) { return result; } sendLen += 2; */ byte[] crc = calculateCRC(sendData); if (crc == null) { return StatusCode.TIMEOUT; } // Transceive the data byte waitIRq = 0x30; // RxIRq and IdleIRq byte validBits = 0; byte[] cmdBuffer = new byte[sendData.length+2]; System.arraycopy(sendData, 0, cmdBuffer, 0, sendData.length); System.arraycopy(crc, 0, cmdBuffer, sendData.length, crc.length); //result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); Response result = communicateWithPICC(PcdCommand.TRANSCEIVE, waitIRq, cmdBuffer, validBits); validBits = result.getValidBits(); if (acceptTimeout && result.getStatus() == StatusCode.TIMEOUT) { return StatusCode.OK; } if (result.getStatus() != StatusCode.OK) { return result.getStatus(); } // The PICC must reply with a 4 bit ACK if (result.getBackData().length != 1 || validBits != 4) { return StatusCode.ERROR; } if (result.getBackData()[0] != MF_ACK) { return StatusCode.MIFARE_NACK; } return StatusCode.OK; } // End PCD_MIFARE_Transceive() /** * Translates the SAK (Select Acknowledge) to a PICC type. * * @param sak The SAK byte returned from PICC_Select(). * @return PICC_Type */ public static PiccType getPiccType(byte sak) { // http://www.nxp.com/documents/application_note/AN10833.pdf // 3.2 Coding of Select Acknowledge (SAK) // ignore 8-bit (iso14443 starts with LSBit = bit 1) // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) return PiccType.forId(sak &= 0x7F); } // End PICC_GetType() /** * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1). */ /* public void mifareSetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) ) { byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); accessBitBuffer[2] = c3 << 4 | c2; } // End MIFARE_SetAccessBits() */ /** * Performs the "magic sequence" needed to get Chinese UID changeable * Mifare cards to allow writing to sector 0, where the card UID is stored. * * Note that you do not need to have selected the card through REQA or WUPA, * this sequence works immediately when the card is in the reader vicinity. * This means you can use this method even on "bricked" cards that your reader does * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). * * Of course with non-bricked devices, you're free to select them before calling this function. * @return Status */ public boolean mifareOpenUidBackdoor() { // Magic sequence: // > 50 00 57 CD (HALT + CRC) // > 40 (7 bits only) // < A (4 bits only) // > 43 // < A (4 bits only) // Then you can write to sector 0 without authenticating haltA(); // 50 00 57 CD byte[] cmd = { 0x40 }; byte validBits = 7; /* Our command is only 7 bits. After receiving card response, this will contain amount of valid response bits. */ Response resp = transceiveData(cmd, validBits, (byte) 0, false); // 40 byte[] response = resp.getBackData(); int received = resp.getBackLen(); validBits = resp.getValidBits(); if (resp.getStatus() != StatusCode.OK) { Logger.error( "Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one? Error: {}", resp.getStatus()); return false; } if (received != 1 || response[0] != 0x0A) { Logger.error("Got bad response on backdoor 0x40 command: 0x{} ({} valid bits)", Integer.toHexString(response[0]), Integer.valueOf(validBits)); return false; } cmd = new byte[] { 0x43 }; validBits = 8; resp = transceiveData(cmd, validBits, (byte) 0, false); // 43 response = resp.getBackData(); received = resp.getBackLen(); validBits = resp.getValidBits(); if (resp.getStatus() != StatusCode.OK) { Logger.error("Error in communication at command 0x43, after successfully executing 0x40. Error: {}", resp.getStatus()); return false; } if (received != 1 || response[0] != 0x0A) { Logger.error("Got bad response on backdoor 0x43 command: 0x{} ({} valid bits)", Integer.toHexString(response[0]), Integer.valueOf(validBits)); return false; } // You can now write to sector 0 without authenticating! return true; } // End MIFARE_OpenUidBackdoor() /** * Reads entire block 0, including all manufacturer data, and overwrites * that block with the new UID, a freshly calculated BCC, and the original * manufacturer data. * * Make sure to have selected the card before this function is called. * @param newUid New UID. * @param uid Current UID * @param authKey Authentication key * @return Status */ public boolean mifareSetUid(byte[] newUid, UID uid, byte[] authKey) { // UID + BCC byte can not be larger than 16 together if (newUid == null || newUid.length == 0 || newUid.length > 15) { Logger.error("New UID buffer empty, size 0, or size > 15 given"); return false; } // Authenticate for reading StatusCode status = authenticate(true, (byte) 1, authKey, uid); if (status != StatusCode.OK) { if (status == StatusCode.TIMEOUT) { // We get a read timeout if no card is selected yet, so let's select one // Wake the card up again if sleeping //byte atqa_answer[2]; //byte atqa_size = 2; //wakeupA(atqa_answer, &atqa_size); if (! isNewCardPresent() || readCardSerial() == null) { Logger.error("No card was previously selected, and none are available. Failed to set UID."); return false; } status = authenticate(true, (byte) 1, authKey, uid); if (status != StatusCode.OK) { // We tried, time to give up Logger.error("Failed to authenticate to card for reading, could not set UID: {}", status); return false; } } else { Logger.error("PCD_Authenticate() failed: {}", status); return false; } } // Read block 0 byte[] block0_buffer = mifareRead((byte) 0); if (block0_buffer == null) { Logger.error("MIFARE_Read() failed: {}. Are you sure your KEY A for sector 0 is 0x{}?", status, Hex.encodeHexString(authKey)); return false; } // Write new UID to the data we just read, and calculate BCC byte byte bcc = 0; for (int i=0; i=0; i--) { dumpMifareClassicSectorToConsole(uid, key, (byte) i); } } haltA(); // Halt the PICC before stopping the encrypted session. stopCrypto1(); } // End PICC_DumpMifareClassicToSerial() /** * Dumps memory contents of a sector of a MIFARE Classic PICC. * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. * @param uid UID returned from a successful select(). * @param key Key A for the sector. * @param sector The sector to dump, 0..39. */ public void dumpMifareClassicSectorToConsole(UID uid, byte[] key, byte sector) { int firstBlock; // Address of lowest address to dump actually last block dumped) byte no_of_blocks; // Number of blocks in sector boolean isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. // The access bits are stored in a peculiar fashion. // There are four groups: // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. // The four CX bits are stored together in a nible cx and an inverted nible cx_. int c1, c2, c3; // Nibbles int c1_, c2_, c3_; // Inverted nibbles int[] g = new int[4]; // Access bits for each of the four groups. byte group; // 0-3 - active group for access bits boolean firstInGroup; // True for the first block dumped in the group // Determine position and size of sector. if (sector < 32) { // Sectors 0..31 has 4 blocks each no_of_blocks = 4; firstBlock = sector * no_of_blocks; } else if (sector < 40) { // Sectors 32-39 has 16 blocks each no_of_blocks = 16; firstBlock = 128 + (sector - 32) * no_of_blocks; } else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. return; } // Dump blocks, highest address first. byte[] buffer; byte blockAddr; isSectorTrailer = true; // True if one of the inverted nibbles did not match boolean invertedError = false; // Avoid "unassiged variable" warning. for (byte blockOffset=(byte) (no_of_blocks - 1); blockOffset>=0; blockOffset--) { blockAddr = (byte) (firstBlock + blockOffset); // Sector number - only on first line if (isSectorTrailer) { if (sector < 10) { System.out.print(" "); // Pad with spaces } else { System.out.print(" "); // Pad with spaces } System.out.print(sector); System.out.print(" "); } else { System.out.print(" "); } // Block number if (blockAddr < 10) { System.out.print(" "); // Pad with spaces } else { if (blockAddr < 100) { System.out.print(" "); // Pad with spaces } else { System.out.print(" "); // Pad with spaces } } System.out.print(blockAddr); System.out.print(" "); // Establish encrypted communications before reading the first block if (isSectorTrailer) { StatusCode status = authenticate(true, (byte) firstBlock, key, uid); if (status != StatusCode.OK) { System.out.println("PCD_Authenticate() failed: " + status); return; } } // Read block buffer = mifareRead(blockAddr); if (buffer == null) { System.out.print("MIFARE_Read() failed"); continue; } // Dump data for (byte index = 0; index < 16; index++) { int val = buffer[index] & 0xff; if (val < 0x10) { System.out.print(" 0"); } else { System.out.print(" "); } System.out.print(Integer.toHexString(val & 0xff)); if ((index % 4) == 3) { System.out.print(" "); } } // Parse sector trailer data if (isSectorTrailer) { c1 = (buffer[7] & 0xff) >> 4; c2 = (buffer[8] & 0xF); c3 = ((buffer[8] & 0xff) >> 4); c1_ = (buffer[6] & 0xF); c2_ = ((buffer[6] & 0xff) >> 4); c3_ = (buffer[7] & 0xF); invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); isSectorTrailer = false; } // Which access group is this block in? if (no_of_blocks == 4) { group = blockOffset; firstInGroup = true; } else { group = (byte) (blockOffset / 5); firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); } if (firstInGroup) { // Print access bits System.out.print(" [ "); System.out.print((g[group] >> 2) & 1); System.out.print(" "); System.out.print((g[group] >> 1) & 1); System.out.print(" "); System.out.print((g[group] >> 0) & 1); System.out.print(" ] "); if (invertedError) { System.out.print(" Inverted access bits did not match! "); } } if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block int value = ((buffer[3] & 0xff)<<24) | ((buffer[2] & 0xff)<<16) | ((buffer[1] & 0xff)<<8) | (buffer[0] & 0xff); System.out.print(String.format(" Value=0x%02x Adr=0x%02x", Integer.valueOf(value), Byte.valueOf(buffer[12]))); } System.out.println(); } return; } // End PICC_DumpMifareClassicSectorToSerial() /** * Dumps memory contents of a MIFARE Ultralight PICC. */ public void dumpMifareUltralightToConsole() { int i; System.out.println("Page 0 1 2 3"); // Try the mpages of the original Ultralight. Ultralight C has more pages. for (byte page=0; page<16; page+=4) { // Read returns data for 4 pages at a time. // Read pages byte[] buffer = mifareRead(page); if (buffer == null) { System.out.println("MIFARE_Read() failed: buffer was null"); break; } // Dump data for (byte offset=0; offset<4; offset++) { i = page + offset; if (i < 10) { System.out.print(" "); // Pad with spaces } else { System.out.print(" "); // Pad with spaces } System.out.print(i); System.out.print(" "); for (byte index=0; index<4; index++) { i = 4 * offset + index; if (buffer[i] < 0x10) { System.out.print(" 0"); } else { System.out.print(" "); } System.out.print(Integer.toHexString(buffer[i] & 0xff)); } System.out.println(); } } } // End PICC_DumpMifareUltralightToSerial() public static class Response { private StatusCode status; private byte[] backData; private int backLen; private byte validBits; public Response(StatusCode status) { this.status = status; } public Response(StatusCode status, byte[] backData) { this.status = status; this.backData = backData; } public Response(StatusCode status, byte[] backData, int backLen, byte validBits) { this.status = status; this.backData = backData; this.backLen = backLen; this.validBits = validBits; } public StatusCode getStatus() { return status; } public byte[] getBackData() { return backData; } public int getBackLen() { return backLen; } public byte getValidBits() { return validBits; } @Override public String toString() { return "Response [status=" + status + ", backData=" + Arrays.toString(backData) + ", backLen=" + backLen + ", validBits=" + validBits + "]"; } } public static enum StatusCode { OK(0) , // Success ERROR(1) , // Error in communication COLLISION(2) , // Collission detected TIMEOUT(3) , // Timeout in communication. NO_ROOM(4) , // A buffer is not big enough. INTERNAL_ERROR(5) , // Internal error in the code. Should not happen ;-) INVALID(6) , // Invalid argument. CRC_WRONG(7) , // The CRC_A does not match MIFARE_NACK(0xff) ; // A MIFARE PICC responded with NAK. private byte code; private StatusCode(int code) { this((byte) code); } private StatusCode(byte code) { this.code = code; } public byte getCode() { return code; } } private static enum PcdRegister { // Registers //Reserved00(0x00), COMMAND_REG(0x01), // starts and stops command execution COM_INT_EN_REG(0x02), // enable and disable interrupt request control bits DIV_INT_EN_REG(0x03), // enable and disable interrupt request control bits COM_IRQ_REG(0x04), // interrupt request bits DIV_IRQ_REG(0x05), // interrupt request bits ERROR_REG(0x06), // error bits showing the error status of the last command executed STATUS1_REG(0x07), // communication status bits STATUS2_REG(0x08), // receiver and transmitter status bits FIFO_DATA_REG(0x09), // input and output of 64 byte FIFO buffer FIFO_LEVEL_REG(0x0A), // number of bytes stored in the FIFO buffer WATER_LEVEL_REG(0x0B), // level for FIFO underflow and overflow warning CONTROL_REG(0x0C), // miscellaneous control registers BIT_FRAMING_REG(0x0D), // adjustments for bit-oriented frames COLL_REG(0x0E), // bit position of the first bit-collision detected on the RF interface //RESERVED_01(0x0F), //RESERVED_10(0x10), MODE_REG(0x11), // defines general modes for transmitting and receiving TX_MODE_REG(0x12), // defines transmission data rate and framing RX_MODE_REG(0x13), // defines reception data rate and framing TX_CONTROL_REG(0x14), // controls the logical behavior of the antenna driver pins TX1 and TX2 TX_ASK_REG(0x15), // controls the setting of the transmission modulation TX_SEL_REG(0x16), // selects the internal sources for the antenna driver RX_SEL_REG(0x17), // selects internal receiver settings RX_THRESHOLD_REG(0x18), // selects thresholds for the bit decoder DEMOD_REG(0x19), // defines demodulator settings //Reserved11(0x1A), //Reserved12(0x1B), MIFARE_TX_REG(0x1C), // controls some MIFARE communication transmit parameters MIFARE_RX_REG(0x1D), // controls some MIFARE communication receive parameters //Reserved14(0x1E), SERIAL_SPEED_REG(0x1F), // selects the speed of the serial UART interface //Reserved20(0x20), CRC_RESULT_REG_MSB(0x21), // shows the MSB values of the CRC calculation CRC_RESULT_REG_LSB(0x22), // shows the LSB values of the CRC calculation //Reserved21(0x23), MOD_WIDTH_REG(0x24), // controls the ModWidth setting //Reserved22(0x25), RF_CONFIG_REG(0x26), // configures the receiver gain GS_N_REG(0x27), // selects the conductance of the antenna driver pins TX1 and TX2 for modulation CWGsP_REG(0x28), // defines the conductance of the p-driver output during periods of no modulation ModGsP_REG(0x29), // defines the conductance of the p-driver output during periods of modulation T_MODE_REG(0x2A), // defines settings for the internal timer T_PRESCALER_REG(0x2B), // T_RELOAD_REG_MSB(0x2C), // defines the 16-bit timer reload value T_RELOAD_REG_LSB(0x2D), T_COUNTER_VALUE_REG_MSB(0x2E), // shows the 16-bit timer value T_COUNTER_VALUE_REG_LSB(0x2F), //Reserved30(0x30), TEST_SEL1_REG(0x31), // general test signal configuration TEST_SEL2_REG(0x32), // general test signal configuration and PRBS control TEST_PIN_EN_REG(0x33), // enables pin output driver on pins D1 to D7 TEST_PIN_VALUE_REG(0x34), // defines the values for D1 to D7 when it is used as an I/O bus TEST_BUS_REG(0x35), // shows the status of the internal test bus AUTO_TEST_REG(0x36), // controls the digital self test VERSION_REG(0x37), // shows the software version ANALOG_TEST_REG(0x38), // controls the pins AUX1 and AUX2 TEST_DAC1_REG(0x39), // defines the test value for TestDAC1 TEST_DAC2_REG(0x3A), // defines the test value for TestDAC2 TEST_ADC_REG(0x3B); // shows the value of ADC I and Q channels //Reserved31(0x3C), //Reserved32(0x3D), //Reserved33(0x3E), //Reserved34(0x3F); private byte value; private byte address; private PcdRegister(int value) { this.value = (byte) value; address = (byte) ((value << 1) & 0x7e); } public byte getAddress() { return address; } public byte getValue() { return value; } } private static enum PcdCommand { IDLE(0b0000), // no action, cancels current command execution MEM(0b0001), // stores 25 bytes into the internal buffer GENERATE_RANDOM_ID(0b0010), // generates a 10-byte random ID number CALC_CRC(0b0011), // activates the CRC coprocessor or performs a self test TRANSMIT(0b0100), // transmits data from the FIFO buffer NO_CMD_CHANGE(0b0111), // no command change, can be used to modify the // CommandReg register bits without affecting the command, // for example, the PowerDown bit RECEIVE(0b1000), // activates the receiver circuits TRANSCEIVE(0b1100), // transmits data from FIFO buffer to antenna and automatically // activates the receiver after transmission MF_AUTHENT(0b1110), // performs the MIFARE standard authentication as a reader SOFT_RESET(0b1111); // resets the MFRC522 private byte value; private PcdCommand(byte value) { this.value = value; } private PcdCommand(int value) { this.value = (byte) value; } public byte getValue() { return value; } } // AddicoreRFID Proximity Integrated Circuit Card (PICC) Commands private static enum PiccCommand { // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) REQUEST_A(0x26), // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. MF_READ(0x30), // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. HALT_A(0x50), // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. WAKE_UP_A(0x52), // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. MF_AUTH_KEY_A(0x60), // Perform authentication with key A MF_AUTH_KEY_B(0x61), // Perform authentication with key B CASCADE_TAG(0x88), // Cascade Tag. Not really a command, but used during anti collision. SEL_CL1(0x93), // Anti collision/Select, Cascade Level 1 SEL_CL2(0x95), // Anti collision/Select, Cascade Level 2 SEL_CL3(0x97), // Anti collision/Select, Cascade Level 3 MF_WRITE(0xA0), // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. UL_WRITE(0xA2), // Writes one 4 byte page to the PICC. MF_TRANSFER(0xB0), // Writes the contents of the internal data register to a block. MF_DECREMENT(0xC0), // Decrements the contents of a block and stores the result in the internal data register. MF_INCREMENT(0xC1), // Increments the contents of a block and stores the result in the internal data register. MF_RESTORE(0xC2); // Reads the contents of a block into the internal data register. private byte value; private PiccCommand(int value) { this((byte) value); } private PiccCommand(byte value) { this.value = value; } public byte getValue() { return value; } } public static class UID { /** Number of bytes in the UID. 4, 7 or 10. */ private byte[] uidBytes; /** The SAK (Select acknowledge) byte returned from the PICC after successful selection. */ private byte sak; public UID(List bytes, byte sak) { uidBytes = new byte[bytes.size()]; for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy