Introduction
MIFare is a technology that allows a contactless transmission of data between a battery-less memory card and a card reader. MiFare is widely used for ticketing (Oyster card at London underground) or for access control. The helper class introduced in this article aims to simplify the use of MiFare memory cards in a .NET application. In particular, the helper class implements the following features:
- Read/write of MiFare card data blocks
- Set up of read/write permissions and card keys
- Application directory (MAD) management
Background
There are several flavors of MiFare memory cards. This article addresses the so-called MiFare classic, which are shipped in two sizes: 1 kilobytes and 4 kilobytes. The 1K memory card is organized in 16 sectors. Each sector is made up of 4 datablocks, and each datablock is 16 bytes in size. The 4th datablock stores read and write permissions for each of the remaining 3 datablocks. The 4th datablock also stores the access keys. According to MiFare specifications, two keys exists, named keyA
and keyB
. Each key can be granted a different set of operations on the datablocks. Datablocks in a sector can be read or written only after the application as successfully logged in into the sector with the proper key. The 4K memory card is organized in a similar way. There are 40 sectors. The first 32 sectors have 4 datablocks, the remaining 8 have 16 datablocks. The first datablock of sector 0 stores some read-only manufacturer information. The other 2 datablocks in sector 0 store the MiFare application directory (MAD). An application directory says which application used each sector on the card. The application is identified by a unique identifier registered to a Mifare authority. MAD in sector 0 handles applications in sectors from 1 to 15. There is another MAD (called MAD2) stored in sector 16 that handle sectors from 17 to 39.
Using the Code
The MiFare
helper class aims to simplify the use of a MiFare in a .NET application.
Instantiation
To instantiate a MiFARECard
class, just call the constructor passing an instance to an object that implements the ICardReader
interface. The ICardReader
interface decouples the MiFareCard
from the physical implementation of the MiFare
card reader. The ICardReader
interface defines the following methods:
MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
The FileCardReader
is a class that implements the ICardReader
interface and simplifies the test of MiFARECard
class. The FileReaderCard
class reads and writes data from a text file on disk. This makes it easy to test the helper class even if a MiFare
reader is not available.
Read/Write of Datablocks
To read data from cards, the MiFareCard
class implements two mechanisms:
- Call the
GetData
/SetData
methods of the MiFARECard
class. This methods allows to write data of any length. The MiFARECard
class will take care to login into the proper sectors are required. Trailer sectors are not overwritten.
MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
Byte[] data = card.GetData(sector, datablock, 64);
card.SetData(sector, datablock, data);
card.Flush();
- Call the
GetSector
method on the MiFare card. This method returns a Sector
object. On this object, you can then call the GetData
/SetData
method to get the content of a given datablock.
MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
Sector s = card.GetSector(sector);
Byte[] data = s.GetData(datablock);
for (int i=0; i<data.Length; i++)
data[i] = (byte)0x11;
card.Flush();
Note that to write back changes on card, call the Flush
method. This method will take care to write on disk only the datablocks that have actually changed.
Set Up Sector Permissions
To set up sector permissions, the Sector
class provides a property named AccessConditions
. The AccessConditions
class exposes a property (DataAreas
) that allows the application to set, for each datablock, the permissions (Never
, KeyA
, KeyB
, KeyAOrB
) for each operation (Read
, Write
, Increment
, Decrement
). AccessConditions
class also allows the application to set keys A and B, and also to set whether the MAD is in use or not.
Sector sector0 = card.GetSector(0);
sector0.Access.DataAreas[0].Read = DataAreaAccessCondition.ConditionEnum.KeyAOrB;
sector0.Access.DataAreas[0].Write = DataAreaAccessCondition.ConditionEnum.KeyB;
sector0.Access.DataAreas[0].Increment = DataAreaAccessCondition.ConditionEnum.Never;
sector0.Access.DataAreas[0].Decrement = DataAreaAccessCondition.ConditionEnum.Never;
sector0.FlushTrailer("A0A1A2A3A4A5", "111111111111");
MAD and MAD2 Managements
To manage MAD and MAD2, the MiFARECard
class implements two methods:
GetSectors(int appId)
: This method returns a list of the sectors that are currently in use by the given application. AddAppId(int appId)
: This method insert reserves a sector to the given application and returns the number of the sector reserved.
For example, the source code for an application that wants to reserve the use of a sector according to MAD specifications should look like this:
MiFARECard card = new MiFARECard();
int[] sectors = card.GetSectors(0x5210);
int appSector;
If ((sectors != null) && (sectors.Length > 0))
{
appSector = sectors[0];
}
else
{
appSector = card.AddAppId(0x5210);
If (appSector == -1)
Throw new NoSectorsAvailableException();
}
Sector s = card.GetSector(appSector);
History
- 5th January, 2011: Initial version