diff --git a/app/src/main/java/im/status/applet_installer_test/appletinstaller/Crypto.java b/app/src/main/java/im/status/applet_installer_test/appletinstaller/Crypto.java index da9d84f..8be1abb 100644 --- a/app/src/main/java/im/status/applet_installer_test/appletinstaller/Crypto.java +++ b/app/src/main/java/im/status/applet_installer_test/appletinstaller/Crypto.java @@ -1,8 +1,11 @@ package im.status.applet_installer_test.appletinstaller; +import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -12,10 +15,11 @@ import javax.crypto.spec.SecretKeySpec; public class Crypto { + + public static final byte[] NullBytes8 = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + public static byte[] deriveKey(byte[] cardKey, byte[] seq, byte[] purposeData) { - byte[] key24 = new byte[24]; - System.arraycopy(cardKey, 0, key24, 0, 16); - System.arraycopy(cardKey, 0, key24, 16, 8); + byte[] key24 = resizeKey24(cardKey); try { byte[] derivationData = new byte[16]; @@ -24,10 +28,10 @@ public class Crypto { // 2 bytes sequence counter + 12 bytes 0x00 System.arraycopy(seq, 0, derivationData, 2, 2); - Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); - IvParameterSpec iv = new IvParameterSpec(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); SecretKeySpec tmpKey = new SecretKeySpec(key24, "DESede"); - cipher.init(Cipher.ENCRYPT_MODE, tmpKey, iv); + + Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, tmpKey, new IvParameterSpec(NullBytes8)); return cipher.doFinal(derivationData); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { @@ -39,11 +43,51 @@ public class Crypto { public static byte[] appendDESPadding(byte[] data) { int length = data.length + 1; - for (; length % 8 != 0; length++){} + for (; length % 8 != 0; length++){ + } byte[] newData = new byte[length]; System.arraycopy(data, 0, newData, 0, data.length); newData[data.length] = (byte)0x80; return newData; } + + public static boolean verifyCryptogram(byte[] key, byte[] hostChallenge, byte[] cardChallenge, byte[] cardCryptogram) { + byte[] data = new byte[hostChallenge.length + cardChallenge.length]; + System.arraycopy(hostChallenge, 0, data, 0, hostChallenge.length); + System.arraycopy(cardChallenge, 0, data, hostChallenge.length, cardChallenge.length); + byte[] paddedData = appendDESPadding(data); + byte[] calculated = mac3des(key, paddedData, NullBytes8); + + return Arrays.equals(calculated , cardCryptogram); + } + + public static byte[] mac3des(byte[] keyData, byte[] data, byte[] iv) { + try { + SecretKeySpec key = new SecretKeySpec(resizeKey24(keyData), "DESede"); + Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); + byte[] result = cipher.doFinal(data, 0, 24); + byte[] tail = new byte[8]; + System.arraycopy(result, 16, tail, 0, 8); + return tail; + } catch (GeneralSecurityException e) { + throw new RuntimeException("error calculating mac.", e); + } + } + + public static byte[] resizeKey24(byte[] keyData) { + byte[] key = new byte[24]; + System.arraycopy(keyData, 0, key, 0, 16); + System.arraycopy(keyData, 0, key, 16, 8); + + return key; + } + + public static byte[] resizeKey8(byte[] keyData) { + byte[] key = new byte[8]; + System.arraycopy(keyData, 0, key, 0, 8); + + return key; + } } diff --git a/app/src/test/java/im/status/applet_installer_test/appletinstaller/CryptoTest.java b/app/src/test/java/im/status/applet_installer_test/appletinstaller/CryptoTest.java index 33f1feb..8ed6bde 100644 --- a/app/src/test/java/im/status/applet_installer_test/appletinstaller/CryptoTest.java +++ b/app/src/test/java/im/status/applet_installer_test/appletinstaller/CryptoTest.java @@ -31,4 +31,13 @@ public class CryptoTest { String expected = "AABB800000000000"; assertEquals(expected, HexUtils.byteArrayToHexString(result)); } + + @Test + public void verifyCryptogram() { + byte[] encKey = HexUtils.hexStringToByteArray("16B5867FF50BE7239C2BF1245B83A362"); + byte[] hostChallenge = HexUtils.hexStringToByteArray("32da078d7aac1cff"); + byte[] cardChallenge = HexUtils.hexStringToByteArray("007284f64a7d6465"); + byte[] cardCryptogram = HexUtils.hexStringToByteArray("05c4bb8a86014e22"); + assertTrue(Crypto.verifyCryptogram(encKey, hostChallenge, cardChallenge, cardCryptogram)); + } } \ No newline at end of file diff --git a/app/src/test/java/im/status/applet_installer_test/appletinstaller/apducommands/InitializeUpdateTest.java b/app/src/test/java/im/status/applet_installer_test/appletinstaller/apducommands/InitializeUpdateTest.java index 011553a..19248c2 100644 --- a/app/src/test/java/im/status/applet_installer_test/appletinstaller/apducommands/InitializeUpdateTest.java +++ b/app/src/test/java/im/status/applet_installer_test/appletinstaller/apducommands/InitializeUpdateTest.java @@ -23,14 +23,6 @@ public class InitializeUpdateTest { assertEquals(challenge, cmd.getData()); } - @Test - public void verifyCryptogram() { - byte[] encKey = HexUtils.hexStringToByteArray("A08DF4027BD8ACC9BD89DA2760909D09"); - byte[] cardCryptogram = HexUtils.hexStringToByteArray("1cd15bd25265c990"); - byte[] hostChallenge = HexUtils.hexStringToByteArray("13e7a37b30ef2c22"); - byte[] cardChallenge = HexUtils.hexStringToByteArray("0066ef6b33b04b11"); - } - @Test public void validateResponse_BadResponse() throws APDUException { byte[] apdu = HexUtils.hexStringToByteArray("000002650183039536622002003b5e508f751c0af3016e3fbc23d3a66982");