diff --git a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java index a4d184b..a6782d4 100644 --- a/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java +++ b/MobileAuthApp/app/src/main/java/com/tarkvaraprojekt/mobileauthapp/NFC/Comms.java @@ -21,6 +21,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; +import java.util.Locale; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -30,7 +31,7 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class Comms { - private static final byte[] master = { // select Main AID + private static final byte[] selectMain = { // select IAS-ECC 0, -92, 4, 12, 16, -96, 0, 0, 0, 119, 1, 8, 0, 7, 0, 0, -2, 0, 0, 1, 0 }; @@ -58,19 +59,31 @@ public class Comms { 127, 73, 79, 6, 10, 4, 0, 127, 0, 7, 2, 2, 4, 2, 4, -122, 65 }; - private static final byte[] masterSec = { + private static final byte[] selectAppSecure = { 12, -92, 4, 12, 45, -121, 33, 1 }; - private static final byte[] personal = { // select personal data DF + private static final byte[] IASECCAID = { // Identification Authentication Signature - European Citizen Card Application Identifier + -96, 0, 0, 0, 119, 1, 8, 0, 7, 0, 0, -2, 0, 0, 1, 0 + }; + + private static final byte[] QSCDAID = { // Qualified Signature Creation Device Application Identifier + 81, 83, 67, 68, 32, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110 + }; + + private static final byte[] selectFile = { // 12, -92, 1, 12, 29, -121, 17, 1 }; - private static final byte[] read = { // read binary + private static final byte[] read = { 12, -80, 0, 0, 13, -105, 1, 0 }; - private IsoDep idCard; + private static final byte[] verifyPIN1 = { + 12, 32, 0, 1, 29, -121, 17, 1 + }; + + private final IsoDep idCard; private final byte[] keyEnc; private final byte[] keyMAC; private byte ssc; // Send sequence counter. @@ -95,10 +108,6 @@ public class Comms { keyMAC = keys[1]; } - public byte[] getAuthenticationCertificate() { - return new byte[0]; - } - /** * Calculates the message authentication code * @@ -165,8 +174,8 @@ public class Comms { */ private byte[][] PACE(String CAN) throws IOException, NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { - // select the ECC applet on the chip - byte[] response = idCard.transceive(master); + // select the IAS-ECC application on the chip + byte[] response = idCard.transceive(selectMain); Log.i("Select applet", Hex.toHexString(response)); // initiate PACE @@ -221,6 +230,22 @@ public class Comms { } + /** + * Selects a file and reads its contents + * + * @param FID file identifier of the required file + * @return decrypted file contents + */ + private byte[] readFileByFID(byte[] FID) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException { + byte [] APDU = createSecureAPDU(FID, selectFile); + byte [] response = idCard.transceive(APDU); + Log.i(String.format("Select FID %s", Hex.toHexString(FID)), Hex.toHexString(response)); + APDU = createSecureAPDU(new byte[0], read); + response = idCard.transceive(APDU); + Log.i("Read binary", Hex.toHexString(response)); + return encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE); + } + /** * Encrypts or decrypts the APDU data * @@ -267,8 +292,7 @@ public class Comms { byte[] MAC = getMAC(macData, keyMAC); // construct the APDU using the encrypted data and the MAC - byte[] APDU = new byte[incomplete.length + encryptedData.length + MAC.length + 3]; - System.arraycopy(incomplete, 0, APDU, 0, incomplete.length); + byte[] APDU = Arrays.copyOf(incomplete, incomplete.length + encryptedData.length + MAC.length + 3); if (encryptedData.length > 0) { System.arraycopy(encryptedData, 0, APDU, incomplete.length, encryptedData.length); } @@ -280,49 +304,79 @@ public class Comms { } + /** + * Selects the IAS ECC application, which provides the PKI functionalities, after a successful PACE. + */ + private void selectIASECCApplication() throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException { + byte[] APDU = createSecureAPDU(IASECCAID, selectAppSecure); + byte[] response = idCard.transceive(APDU); + Log.i("Select the main application", Hex.toHexString(response)); + if (!Hex.toHexString(response, response.length - 2, 2).equals("9000")) { + throw new RuntimeException("Could not select IAS-ECC."); // *Should* never happen. + } + } + /** * Gets the contents of the personal data dedicated file * - * @param FID the last bytes of file identifiers being requested - * @return array containing the data strings + * @param lastBytes the last bytes of the personal data file identifiers (0 < x < 16) + * @return array containing the corresponding data strings * */ - public String[] readPersonalData(byte[] FID) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException { + public String[] readPersonalData(byte[] lastBytes) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException { - String[] personalData = new String[FID.length]; - byte[] data; - byte[] APDU; - byte[] response; + String[] personalData = new String[lastBytes.length]; + int stringIndex = 0; + + // select the application + selectIASECCApplication(); // select the personal data dedicated file - data = new byte[]{80, 0}; // personal data DF FID - APDU = createSecureAPDU(data, personal); - response = idCard.transceive(APDU); + byte[] FID = new byte[]{80, 0}; // personal data dedicated file FID + byte[] APDU = createSecureAPDU(FID, selectFile); + byte[] response = idCard.transceive(APDU); Log.i("Select personal data DF", Hex.toHexString(response)); - // select and read the first 8 elementary files in the DF - for (int i = 0; i < FID.length; i++) { + // select and read the personal data elementary files + for (byte index : lastBytes) { - byte index = FID[i]; if (index > 15 || index < 1) throw new RuntimeException("Invalid personal data FID."); - - data[1] = index; - APDU = createSecureAPDU(data, personal); - response = idCard.transceive(APDU); - Log.i(String.format("Select EF 500%d", index), Hex.toHexString(response)); - - APDU = createSecureAPDU(new byte[0], read); - response = idCard.transceive(APDU); - Log.i(String.format("Read binary EF 500%d", index), Hex.toHexString(response)); + FID[1] = index; // store the decrypted datum - byte[] raw = encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE); - int indexOfTerminator = Hex.toHexString(raw).lastIndexOf("80") / 2; - personalData[i] = new String(Arrays.copyOfRange(raw, 0, indexOfTerminator)); + response = readFileByFID(FID); + int indexOfTerminator = Hex.toHexString(response).lastIndexOf("80") / 2; + personalData[stringIndex++] = new String(Arrays.copyOfRange(response, 0, indexOfTerminator)); } return personalData; } + + /** + * Attempts to verify the user-provided PIN1 + * + * @param PIN1 byte array containing the PIN1 + */ + private void verifyPIN1(byte[] PIN1) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException { + selectIASECCApplication(); + byte[] paddedPIN1 = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + System.arraycopy(PIN1, 0, paddedPIN1, 0, PIN1.length); + byte[] APDU = createSecureAPDU(paddedPIN1, verifyPIN1); + byte[] response = idCard.transceive(APDU); + Log.i("PIN1 verification", Hex.toHexString(response)); + String sw1sw2 = Hex.toHexString(response, response.length - 2, 2); + if (!sw1sw2.equals("9000")) { + if (sw1sw2.equals("6983")) throw new RuntimeException("Invalid PIN1. Authentication method blocked."); + else throw new RuntimeException(String.format("Invalid PIN1. %c attempt%s left.", sw1sw2.charAt(sw1sw2.length() - 1), sw1sw2.endsWith("1") ? "" : "s")); + } + } + + public byte[] getAuthenticationCertificate(String PIN1) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException { + + return new byte[0]; + + } + }