3

I've a NFC reader along with MIFARE Classic 1K card. I've a Visual C# winforms project. Right now I'm able to connect to the reader and detect the card and get it's UUID. The problem I'm facing is while writing and reading data. I searched a lot on internet, found some solution even tested the demo code provided with the SDK... nothing's working.

Let me describe the workflow and code I'm using for writing, authenticating a block, sending APDU and reading the block.

Following is the code for writing data to block 5.

String tmpStr = Text;
            int indx;
            if (authenticateBlock(Block))
            {
                ClearBuffers();
                SendBuff[0] = 0xFF;                             // CLA
                SendBuff[1] = 0xD6;                             // INS
                SendBuff[2] = 0x00;                             // P1
                SendBuff[3] = (byte)int.Parse(Block);           // P2 : Starting Block No.
                SendBuff[4] = (byte)int.Parse("16");            // P3 : Data length
                SendBuff[5] = 0xFF;
                SendBuff[6] = 0xFF;
                SendBuff[7] = 0xFF;
                SendBuff[8] = 0xFF;
                SendBuff[9] = 0xFF;
                SendBuff[10] = 0xFF;

for (indx = 0; indx <= (tmpStr).Length - 1; indx++)
                    {
                        SendBuff[indx + 5] = (byte)tmpStr[indx];
                    }
                    SendLen = SendBuff[4] + 5;
                    RecvLen = 0x02;

                    retCode = SendAPDUandDisplay(2);

                    if (retCode != Card.SCARD_S_SUCCESS)
                    {
                        MessageBox.Show("fail write");

                    }
                    else
                    {
                        MessageBox.Show("write success");
                    }
                }
                else
                {
                    MessageBox.Show("FailAuthentication");
                }

                CloseCardConnection();

The function SendAPDUandDisplay is as below

private int SendAPDUandDisplay(int reqType)
        {
            int indx;
            string tmpStr = "";

            pioSendRequest.dwProtocol = Aprotocol;
            pioSendRequest.cbPciLength = 8;

            //Display Apdu In
            for (indx = 0; indx <= SendLen - 1; indx++)
            {
                tmpStr = tmpStr + " " + string.Format("{0:X2}", SendBuff[indx]);
            }

            retCode = Card.SCardTransmit(hCard, ref pioSendRequest, ref SendBuff[0],
                                 SendLen, ref pioSendRequest, ref RecvBuff[0], ref RecvLen);

            if (retCode != Card.SCARD_S_SUCCESS)
            {
                return retCode;
            }

            else
            {
                try
                {
                    tmpStr = "";
                    switch (reqType)
                    {
                        case 0:
                            for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            if ((tmpStr).Trim() != "90 00")
                            {
                                //MessageBox.Show("Return bytes are not acceptable.");
                                return -202;
                            }

                            break;

                        case 1:

                            for (indx = (RecvLen - 2); indx <= (RecvLen - 1); indx++)
                            {
                                tmpStr = tmpStr + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            if (tmpStr.Trim() != "90 00")
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            else
                            {
                                tmpStr = "ATR : ";
                                for (indx = 0; indx <= (RecvLen - 3); indx++)
                                {
                                    tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                                }
                            }

                            break;

                        case 2:

                            for (indx = 0; indx <= (RecvLen - 1); indx++)
                            {
                                tmpStr = tmpStr + " " + string.Format("{0:X2}", RecvBuff[indx]);
                            }

                            break;
                    }
                }
                catch (IndexOutOfRangeException)
                {
                    return -200;
                }
            }
            return retCode;
        }

Function authenticateBlock is as following

private bool authenticateBlock(String block)
        {
            ClearBuffers();
            /*SendBuff[0] = 0xFF;                         // CLA
            SendBuff[2] = 0x00;                         // P1: same for all source types 
            SendBuff[1] = 0x82;                         // INS: for stored key input
            SendBuff[3] = 0x00;                         // P2 : Memory location;  P2: for stored key input
            SendBuff[4] = 0x05;                         // P3: for stored key input
            SendBuff[5] = 0x01;                         // Byte 1: version number
            SendBuff[6] = 0x00;                         // Byte 2
            SendBuff[7] = (byte)int.Parse(block);       // Byte 3: sectore no. for stored key input
            SendBuff[8] = 0x60;                         // Byte 4 : Key A for stored key input
            SendBuff[9] = (byte)int.Parse("1");         // Byte 5 : Session key for non-volatile memory
            */

            SendBuff[0] = 0xD4;
            SendBuff[1] = 0x4A;
            SendBuff[2] = 0x01;
            SendBuff[3] = 0x00;
            SendBuff[4] = (byte) int.Parse(block);
            SendBuff[5] = 0xFF;
            SendBuff[6] = 0xFF;
            SendBuff[7] = 0xFF;
            SendBuff[8] = 0xFF;
            SendBuff[9] = 0xFF;
            SendBuff[10] = 0xFF;


            /*SendLen = 0x0A;
            RecvLen = 0x02;*/

            SendLen = 4;
            RecvLen = 255;


            retCode = SendAPDUandDisplay(2);

            if (retCode != Card.SCARD_S_SUCCESS)
            {
                //MessageBox.Show("FAIL Authentication!");
                return false;
            }

            return true;
        }

One strange thing to notice here is that whatever values I set in sendBuff this function always returns true value and the write data code "The very first code block" returns write success message

But after executing the write data code when I read that very block "5" in my case, there is nothing present there. My read block code returns an empty string and when I try to double check if data was written and my faulty code couldn't read I use an external software to verify that was the value added or not, that software also does not show the data that I wrote and got that write success message.

Ok following is the code I'm using to read block 5.

public string readBlock(String Block)
        {
            string tmpStr = "";
            int indx;

            if (authenticateBlock(Block))
            {
                ClearBuffers();
                /*
                SendBuff[0] = 0xFF; // CLA 
                SendBuff[1] = 0xB0;// INS
                SendBuff[2] = 0x00;// P1
                SendBuff[3] = (byte)int.Parse(Block);// P2 : Block No.
                SendBuff[4] = (byte)int.Parse("16");// Le
                */

                SendBuff[0] = 0xD4;
                SendBuff[1] = 0x40;
                SendBuff[2] = 0x01;
                SendBuff[3] = 0x30;
                SendBuff[4] = byte.Parse(Block.ToString(), System.Globalization.NumberStyles.HexNumber);
                SendBuff[5] = 0xFF;
                SendBuff[6] = 0xFF;
                SendBuff[7] = 0xFF;
                SendBuff[8] = 0xFF;
                SendBuff[9] = 0xFF;
                SendBuff[10] = 0xFF;

                //SendLen = 5;
                //RecvLen = SendBuff[4] + 2;

                SendLen = 5;
                RecvLen = 255;

                retCode = SendAPDUandDisplay(2);

                if (retCode == -200)
                {
                    return "outofrangeexception";
                }

                if (retCode == -202)
                {
                    return "BytesNotAcceptable";
                }

                if (retCode != Card.SCARD_S_SUCCESS)
                {
                    return "FailRead";
                }

                // Display data in text format
                for (indx = 0; indx <= RecvLen - 1; indx++)
                {
                    tmpStr = tmpStr + Convert.ToChar(RecvBuff[indx]);
                }

                return (tmpStr);
            }
            else
            {
                return "FailAuthentication";
            }
        }

Please Note that the read block method is called after checking that is a reader connected connected, if so then I call the readblock method and it returns an empty string

I've tried several values as you would see in comments but nothing seems to help, it's been 3 long days and I'm still stuck here.

Can someone please help me figure where I'm doing it wrong and what values should I send in order to authenticate the block?

Please do me a favour that if anyone gets to knows the problem in my code or want to correctify the values I'm setting in sendBuff[] then please quote them in C# code so I can use exactly the solution you want me to implement

Any sincere help would be highly regarded, thanks in advance.

Ibrahim Iqbal
  • 600
  • 3
  • 23
  • Are you sure you are using the correct SDK? It seems you are using something for reading SmartCards and not NFC tokens. Which SDK are you using? – S.Spieker Jan 21 '20 at 10:12
  • @S.Spieker I was provided ACR122U SDK, it's drivers and samples from which I utilized Visual C# one. You can view the product at it's vendor's site at this link https://www.nxp.com/products/rfid-nfc/mifare-hf/mifare-classic:MC_41863 – Ibrahim Iqbal Jan 21 '20 at 11:30
  • There does not seem to be something publicly available. So even guessing is quite hard. – S.Spieker Jan 21 '20 at 11:50
  • @S.Spieker do you have any prior experience with NFC smart card contactless reading and writing ? – Ibrahim Iqbal Jan 21 '20 at 11:54
  • Check out Feig's OBIDISC4NET-SDK – Felix Arnold Feb 26 '20 at 16:21

2 Answers2

0

I have only experimented with mifare 1k, using Arduino.

In this instance, after detecting the card, and retrieving the UUID, It needs to select the card before reading/writing. Are you doing this select card step?

Tony Weston
  • 317
  • 2
  • 9
  • Yes I'm get the UUID of reader and then detecting the card. The problem is while sending APDU bytes while Authenticating a block, reading and writing ... All these operations need to send some APDU bytes. I guess I just need to know those 16 bytes sent as APDU while authenticating the block. – Ibrahim Iqbal Jan 21 '20 at 14:29
0

As its a S50 1K Classic the 'bytemap' is different and the process cycle while ostensibly the same you need to check that its a S50 before continuing by getting the ATR/ATS and parsing it to retrieve the switch setting. With contact its ATR, contactless ATS but is technically the same thing. Under PCSC this is asking for the readerchangestate when asking the reader is a card present, done before sending an APDU. You can also get other settings at the same time. For MFS50 you need to perform a 'S'elect then a 'L'ogin using the sector key then read that sectors first three of 4 blocks but ignore most of the first sector block - the keys are in the fourth block along with other control bytes. The 'UID' is returned on 'S'elect, success or fail on 'L'ogin, data or 'E'rror on reading the blocks in the sector. For 14443 sector 0 the first block is occupied by the 'manufacturing' data which depends on the construct can have 4, 7 or 12 bytes as the UID with from a data point of view embedded CRC and check bytes, so cannot be used as a UID - it is a data map. Lights, C, ultralights, EV1's have a different 'bytemap' and may or may not have 'L'ogins at card or sector. These are 14443 types, there is also a 15693 type which has just data in the sectors. Some Chinese 14443 have writable manufacturing blocks, but usually its a mix of static, settings and OTP (once bit set cannot unset, used for authentication and NFC verification of size!).

Classic 1K: ident UID: 66908648, requires sector key log in (A read, B 
read/write)    
Sector 0:
6690864838880400468f76594d100612
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff078069ffffffffffff
...
Sector 15:
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
ffffffffffffff0780bcffffffffffff

Mifare ultralight: UID 0489d802a44080, might require sector login but key held elsewhere.
Sector 0:
0489D8DD
02A44080
66480900
00010001
.
Sector 1:
00000000
00000000
00000000
000000EE

15693: UID D89A1F24500104E0
Sector 0:
50555955
Sector 1:
48485353
Sector 2:
59435300
Sector 3:
00000000
...
Sector 15:
00000000

So, get the ATR/ATS and work out what card you have, then deal with it accordingly. Oh, and use the belt, braces and a piece of string approach - after writing to a card read it again to compare the written to what is expected. 15693 require sector complete writes else nothing gets written in that sector. Will that be Type 2 NFC/NDEF - there are other standards. Have cannibalized a Zebra printer to encode and print NTAG201's bullseyes on the fly.

Steve
  • 1