1

Has anyone managed to get Mutual Authentication with an ultralight C card working using this chip? I understand the Authentication procedure but looking at the command set in the chips manual and trying a few things I don't think it is actually possible but thought I would post here before abanding the chip-set and moving back to using a CJS encoder for verification

Thanks in advance

watersa8
  • 13
  • 2

2 Answers2

1

Yes, it is possible to perform mutual authentication with Mifare Ultralight C. Use the following procedure:

STEP 1: Send start authentication command. (1A 00) to card

STEP 2: Card generates an 8 byte random number RndB. This random number is DES/3DES enciphered with the diversified key, denoted by ek(RndB), and is then transmitted to the terminal.

STEP 3 The terminal runs a DES/3DES deciphering operation on the received ek(RndB) and thus retrieves RndB. RndB is then rotated left by 8 bits (first byte is moved to the end of RndB), yielding RndB’. Now the terminal itself generates an 8 byte random number RndA. This RndA is concatenated with RndB’ and enciphered using DES/3DES (The ecryption of the two blocks is chained using the Cipher Block Chaining (CBC) send mode). This token ek(RndA + RndB’) is sent to the card.

STEP 4: The card runs an DES/3DES decipherment on the received token and thus gains RndA + RndB’. The card can now verify the sent RndB’ by comparing it with the RndB’ obtained by rotating the original RndB left by 8 bits internally. A successful verification proves to the card that the card and the terminal possess the same secret (key). If the verification fails, the card stops the authentication procedure and returns an error message. As the card also received the random number RndA, generated by the terminal, it can perform a rotate left operation by 8 bits on RndA to gain RndA’, which is enciphered again, resulting in ek(RndA’). This token is sent to the terminal.

STEP 5: The terminal runs a DES/3DES decipherment on the received ek(RndA’) and thus gains RndA’ for comparison with the terminal-internally rotated RndA’. If the comparison fails, the terminal exits the procedure and may halt the card.

STEP 6: The card sets the authentication state as ‘Authenticated’

Same can be found in this NXP datasheet

Lewis Munene
  • 134
  • 7
  • Thanks for the reply, I read somewhere else that it was possible and resolved it after hours and hours of checking code when I realised i'd set the number of bytes on one of my read responses to the wrong value so was leaving data in the buffer. A simple mistake which lead to a giant headache. – watersa8 Mar 27 '17 at 16:01
  • Can you post your code for newbs, if this thread necromancy isn't too big of a headache? – Motomotes Apr 10 '18 at 21:59
  • @Motes, see below answer. – Lewis Munene May 02 '18 at 08:53
  • Many thanks! Small catch: according to your examples in another answer, [NXP's MF0ICU2 data sheet](https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf) and my own experiments, at steps 3-4 terminal **en**crypts `RndA + RndB’` while card **de**crypts it. – gmk57 Feb 15 '22 at 15:17
  • Sure. Correcting that in my answer. Not sure why I wrote it like that but what you've said is right – Lewis Munene Feb 16 '22 at 10:18
1

Sample C source code:

typedef unsigned char byte;

int mutual_authentication( const byte *diversifiedKey )
{
    byte cmd[256], response[256];
    int cmdLen, responseLen;

    //Send 1A00
    cmd[0] = 0x1A;
    cmd[1] = 0x00;
    cmdLen = 2;
    int ret = send_command( cmd, cmdLen, response, &responseLen );

    //Get ekRndB
    byte ekRndB[8], rndB[8];

    memcpy(ekRndB, response, 8);
    //Decrypt ekRndB with diversifiedKey
    des_ISO_decrypt( diversifiedKey, ekRndB, rndB, 8 );

    //PCD Generates RndA
    byte randA[8] = "\x33,\x54,\x2A,\x87,\x21,\x00,\x77,\x98";//random numbers
    byte rndARndBComplement[16], ekRndARndBComplement[16];

    // Append RndA and RndB' ( RndB' is generated by rotating RndB one byte to the left )
    // after the status byte.
    memcpy(&rndARndBComplement[0],rndA,8);
    memcpy(&rndARndBComplement[8],&rndB[1],7);  // bytes 1 to 7
    rndARndBComplement[15] = rndB[0];           // byte 0

    // Apply the DES send operation to the 16 argument bytes before sending the second frame to the PICC
    des_ISO_encrypt(diversifiedKey, rndARndBComplement, ekRndARndBComplement, 16);

    cmd[0] = 0xAF;
    memcpy(&cmd[1], ekRndARndBComplement, 16);
    cmdLen = 17;

    ret = send_command( cmd, cmdLen, response, &responseLen );

    byte ekRndAComplement[8], rndAComplement[8], finalOutput[8];

    memcpy(&ekRndAComplement[0], &response[1], 8);
    des_ISO_decrypt(diversifiedKey, ekRndAComplement, rndAComplement, 8);

    memcpy(&finalOutput[1], &RndAComplement[0], 7);
    finalOutput[0] = rndAComplement[7];

    //compare the received RndA with the one we originally had
    return memcmp(&finalOutput[0], &rndA[0], 8);
}

Note: You should have your own implementation of send_command() (depending on your card reader), des_ISO_decrypt() and des_ISO_encrypt() (depending on your DES library).

Lewis Munene
  • 134
  • 7