Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

A .NET MiFare Helper Class

4.00/5 (6 votes)
8 Jan 2011CPOL3 min read 72.9K   6.9K  
This article introduces a .NET class that makes the use of MiFare card easier.

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:

  1. Read/write of MiFare card data blocks
  2. Set up of read/write permissions and card keys
  3. 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:

C#
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:

  1. 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.
    C#
    MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
    
    // read a block of data. The MiFARECard object will take care to login into
    // the proper sectors
    Byte[] data = card.GetData(sector, datablock, 64);
    
    // change data here..
    
    // write data into MiFARECard class internal members
    card.SetData(sector, datablock, data);
    
    // write changes on card
    card.Flush();
  2. 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.
    C#
    MiFARECard card = new MiFARECard(new FileCardReader("test.txt"));
    
    // load the sector of interest
    Sector s = card.GetSector(sector);
    
    // read data from the datablock
    Byte[] data = s.GetData(datablock);
    
    // changes data. Because GetData do not make copies of internal data,
    // any changes to the data array is automatically reflected into the sector
    for (int i=0; i<data.Length; i++)
      data[i] = (byte)0x11;
    
    // write changes back to the card
    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.

C#
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:

  1. GetSectors(int appId): This method returns a list of the sectors that are currently in use by the given application.
  2. 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:

    C#
    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();
    }
    
    // get data from reserved sector
    Sector s = card.GetSector(appSector);

History

  • 5th January, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)