10 Dec 2010

How to access to MIFARE memory

To be able to read/write to/from MIFARE memory there is javacardx.external package specified in JavaCard 2.2.2 standard.
It specifies MemoryAccess interface and Memory class.
First we have to get object of MemoryAccess object like:

oMemAccess = Memory.getMemoryAccessInstance(Memory.MEMORY_TYPE_MIFARE
                                          , null
                                          , (short)0);


Memory.getMemoryAccessInstance() method has the following parameters:
  • memoryType - the desired external memory subsystem. Could be MEMORY_TYPE_MIFARE or MEMORY_TYPE_EXTENDED_STORE.
  • memorySize - the array containing the desired size in bytes, if applicable, in the external memory subsystem. This parameter is ignored for MIFARE memory type.
  • memorySizeOffset - the offset within the memorySize array where the 32 bit memory size number in bytes is specified. This parameter is ignored.
As you can see from above parameters list we need only specify memory type to get access MIFARE memory.
Then to write data to we can use MemoryAccess.writeData() method like:

oMemAccess.writeData(
             dataToWrite                     // the source data byte array
           , (short) 0                       // the byte offset in data buffer
           , (short) dataToWrite.length      // the length of data
           , thisCardPwdArray                // the byte array containing the 
                                             // key (password)
           , (short)0                        // the byte offset into the key 
                                             // array where the key data begins
           , (short) thisCardPwdArray.length // the length in bytes of key
           , (short) (blocknum / 4)          // sector number
           , blocknum)                       // block number

I'd like to mention one point about sector and block numbers. There are 2 type of addressing mode:

  • Absolute mode where block number accepts values 0..63 and sector number will be ignored.
  • Relative mode where block number accepts values 0..4 and sector number must be correctly set according to MIFARE memory layout.
To read data there is the method MemoryAccess.readData():


oMemAccess.readData(
               readBuf                         // destination buffer
             , (short) 0                       // offset in destination buffer
             , thisCardPwdArray                // key (password) array
             , (short)0                        // offset in key array
             , (short) thisCardPwdArray.length // key length
             , (short)secnum                   // sector number
             , (short)blocknum                 // block number
             , DATA_LEN)                       // number of bytes to read


All parameters have the same meaning as in writeData() method.


One additional remark: if the password to access MIFARE memory is incorrect there is no retry mechanism. You have to start from the beginning. The reason why it has implemented like that is absence of key counter like in PIN key.

9 comments:

Anonymous said...

Hi Nodir!

Could you clarify what auth_key is? Spec says it is an 8 byte password but Mifare keys are 6 bytes long...

Nodir Gulyamov said...

@anonymous:
auth_key is MIFARE key. Which spec says it is 8 bytes? JC APU 2.2.2 says:

auth_key - the byte array containing the key(password)

And no restriction to the length. You can specify length by auth_key_blen parameter.

Anonymous said...

MF1ICS50 functional spec. sets the lengths of keys A and B as 6 byte.

Nodir Gulyamov said...

Ok, now I've got your question. The specification which you've mentioned is specifying Key A and Key B. Both are 6 bytes. In JC you have to specify password which has generated based on Key A and Key B. The algorithm has specified in "AN02105 Secure Access to MIFARE Memory on Dual Interface Smart Card ICs" from Philips. Let me know if you don't have it and I'll create separate post about it as algorithm is a bit more complicated to explain in response message.

Anonymous said...

Hi Nodir!

It would be grateful! Thank you ever so much for your help!

// Alexander the anonymous :)

Maxim Chechel said...

Hi, Nodir!

Thanks you for this great blog.

What about transactions when using Mifare. How can i be sure that before i read and write data from/to Mifare. Third part doesn't have any access to mifare?

As far as i see it can be done only with constructor/destructor of memory access object. Or there are some other mechanisms?

I mean that this would work as transaction:

short mifareAccessExample() {
mo = Memory.getMemoryAccessInstance(...);
mo.readData(...);
mo.writeData(...);
}


and here we can get a collision:


short mifareAccessExample() {
myRead();
myWrite();
}

void myRead() {
mo = Memory.getMemoryAccessInstance(...)
;
mo.readData(...);
}

void myWrite() {
mo = Memory.getMemoryAccessInstance(...)
;
mo.writeData(...);
}


What do you think? Or it may depend on mifare implementation on different sim cards?

Nodir Gulyamov said...

Hi Maxim!
You have risen very good point.
As far as I know there is no simple solution for your problem. JavaCard only guarantee transaction during writing, i.e. within one write command all bytes will be written or all will fail.

The problem is more generic and you cannot solve it with isolating Memory Access. There is no protection if given part of mifare memory will be updated by reader.

The only idea which I have is usage of checksums and checking before/after writing.

Maxim Chechel said...

FYI: I asked this question to NXP and they finally said that:
1) It depends on JavaCard OS manufacturer for the given card: JCSystem.beginTransaction() and JCSystem.commitTransaction() may affect on mifare (lock/release)
2) They don't know any of OS manufacturer who done this :)

So it seems like we can't combine readData and writeData in one transaction :(

Nodir Gulyamov said...

Hi Maxim,
NXP is right. Moreover it is good practice to separate reading and writing to 2 different transactions due to transaction buffer on the cards is limited and could be different even within the same JavaCard OS but different chips.