Question

Does anyone have a clue how to decrypt the first message sent from the card? I mean after the authentication success and then you send a command (for example 0x51 (GetRealTagUID). It returns 00+random32bits (always different). I try to decrypt it with:

        private byte[] decrypt(byte[] raw, byte[] encrypted, byte[] iv)
            throws Exception {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

And calling it with decrypt(sessionKey, response, iv)

IV = all zeros (16 bytes)

response = that 32randombits after the 0x51 command (just removed the two zeros)

Someone told me, that the IV changes after the first sent command (0x51). How to generate the right IV for decrypting that response? I think the all zeros is wrong, because the decrypted message is always different and it should be always same with the same card.

-EDIT-

After applying your (Michael Roland) instructions, the decrypted response is still just random bits. Here is my code (I think I'm doing something wrong):

            byte[] x = encrypt(sessionKey, iv, iv);

            byte[] rx = rotateBitsLeft(x);

            if ((rx[15] & 0x01) == 0x01)
                rx[15] = (byte) (rx[15] ^ 0x87);

            if ((rx[15] & 0x01) == 0x00)
                rx[15] = (byte) (rx[15] ^ 0x01);

            byte[] crc_k1 = rx;

            byte[] rrx = rotateBitsLeft(rx);

            if ((rrx[15] & 0x01) == 0x01)
                rrx[15] = (byte) (rrx[15] ^ 0x87);

            if ((rrx[15] & 0x01) == 0x00)
                rrx[15] = (byte) (rrx[15] ^ 0x01);

            byte[] crc_k2 = rrx;

            byte[] command = { (byte) 0x51, (byte) 0x80, (byte) 0x00,
                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                    (byte) 0x00 };

            for (int i = 0; i < 16; i++){
                command[i] = (byte) (command[i] ^ crc_k2[i]);
            }

            byte[] iv2 = encrypt(sessionKey, command, iv);

            byte[] RealUID = decrypt(sessionKey, ReadDataParsed, iv2);

            Log.e("RealUID", ByteArrayToHexString(RealUID));

-EDIT3-

Still returning always different values. I think the problem might lie here:

byte[] iv2 = encrypt(sessionKey, command, iv);

What IV to use when creating the new IV for decrypting the response? It is all zeros there.

Was it helpful?

Solution

After authentication, the IV is reset to all-zeros. As you use AES authentication, you then have to calculate the CMAC for every follow-up command (even if CMAC is not actually appended to the command). So the CMAC calculation for your command will lead to correct IV initialization for decoding the response. I.e. the CMAC for the command is equal to the IV for decrypting the response. Similarly, for all further commands, the IV is the last cipher block from the previous encryption/CMAC.


UPDATE:

How to calculate CMAC pad XOR value

  • Encrypt one block of zeros (0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00) with session key (using IV of zeros). -> x[0..15]
  • Rotate x[0..15] one bit to the left. -> rx[0..15]
  • If the last bit (bit 0 in rx[15]) is one: xor rx[15] with 0x86.
  • Store rx[0..15] as crc_k1[0..15].
  • Rotate rx[0..15] one bit to the left. -> rrx[0..15]
  • If the last bit (bit 0 in rrx[15]) is one: xor rrx[15] with 0x86.
  • Store rrx[0..15] as crc_k2[0..15].

How to calculate CMAC

  • You pad the command using 0x80 0x00 0x00 ... to the block size of the cipher (16 bytes for AES). If the command length matches a multiple of the block size, no padding is added.
  • For your command (0x51) this would look like: 0x51 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  • If padding was added, xor the last 16 bytes of the padded command with crc_k2[0..15].
  • If no padding was added, xor the last 16 bytes of the command with crc_k1[0..15].
  • Encrypt (in send mode, i.e. enc(IV xor datablock), cipher text of previous block is new IV) the result with the session key.
  • The ciphertext of the last block is the CMAC and the new IV.

UPDATE 2:

How to rotate a bit vector to the left for one bit

public void rotateLeft(byte[] data) {
    byte t = (byte)((data[0] >>> 7) & 0x001);
    for (int i = 0; i < (data.length - 1); ++i) {
        data[i] = (byte)(((data[i] << 1) & 0x0FE) | ((data[i + 1] >>> 7) & 0x001));
    }
    data[data.length - 1] = (byte)(((data[data.length - 1] << 1) & 0x0FE) | t);
}

OTHER TIPS

It's not c/c++ section but enter my position, I'm looking at a possible bug from the C developer. I can't proof Java implementation here. But i think @Michael Roland way is not true! Maby! Maby i'm wrong!

Sry for my English,- its terrible :) but it's not my native language. I'm Russian.

Check first MSB (7 - bit) of array[0] and then shiffting this to the left. And then XOR if MSB 7 bit was == 1; Or save first MSB bit of array[0] and after shiffting put this bit at the end of array[15] at the end (LSB bit).

Just proof it's here: https://www.nxp.com/docs/en/application-note/AN10922.pdf

Try this way:

Zeros <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5

Data <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00

First u have to encrypt 16 bytes (zeros) with SesionKey;

enc_aes_128_ecb(Zeros);

And u get EncryptedData.

EncryptedData <- 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC

Check bit 7 [MSB - LSB] of EncryptedData[0] == 1? switch i to true;

 bool i = false;
  if (EncryptedData[0] & 0x80){
    i = true;
  }

Then do Shiffting of all EncryptedData to 1 bit <<.

ShiftLeft(EncryptedData,16);

And now, when i == true - XOR the last byte [15] with 0x87

if (i){
    ShiftedEncryptedData[15] ^= 0x87;
  }

7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58

Save it as KEY_1.

Try bit 7 [MSB - LSB] of ShiftedEncryptedData[0] == 1?

 i = false;
  if (ShiftedEncryptedData[0] & 0x80){
    i = true;
  }

Then do Shiffting of all ShiftedEncryptedData to 1 bit <<.

ShiftLeft(ShiftedEncryptedData,16);

And now, when i == true - XOR the last byte [15] with 0x87

if (i){
   ShiftedEncryptedData[15] ^= 0x87;
}

F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0

Save it as KEY_2.

Now we take our Data (6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00)

As Michael say's - pad command with 0x80 0x00...

XOR Data with KEY_2 - if command was padded, or KEY_1 if don't. If we have more like 16 bytes (32 for example) u have to XOR just last 16 bytes.

Then encrypt it:

enc_aes_128_ecb(Data);

Now u have a CMAC.

CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3


Zeros <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5

Key_1 <- 7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58

Key_2 <- F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0

Data <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00

CMAC <- CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3

C/C++ function:

void ShiftLeft(byte *data, byte dataLen){
  for (int n = 0; n < dataLen - 1; n++) {
   data[n] = ((data[n] << 1) | ((data[n+1] >> 7)&0x01));
  }
  data[dataLen - 1] <<= 1;
}   

Have a nice day :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top