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.
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
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]
x[0..15]
one bit to the left. -> rx[0..15]
rx[15]
) is one: xor rx[15]
with 0x86
.rx[0..15]
as crc_k1[0..15]
.rx[0..15]
one bit to the left. -> rrx[0..15]
rrx[15]
) is one: xor rrx[15]
with 0x86
.rrx[0..15]
as crc_k2[0..15]
.How to calculate CMAC
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.0x51
) this would look like: 0x51 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
crc_k2[0..15]
.crc_k1[0..15]
.enc(IV xor datablock)
, cipher text of previous block is new IV) the result with the session key.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);
}