From 8a06c7d07485973541ef0b189c99708ac4270056 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Wed, 27 Sep 2017 15:22:34 +0300 Subject: [PATCH] implement CHANGE PIN --- .../java/im/status/wallet/SecureChannel.java | 6 ++- .../java/im/status/wallet/WalletApplet.java | 28 ++++++++++++- .../status/wallet/WalletAppletCommandSet.java | 5 +++ .../im/status/wallet/WalletAppletTest.java | 40 +++++++++++++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/main/java/im/status/wallet/SecureChannel.java b/src/main/java/im/status/wallet/SecureChannel.java index bcd10d9..e7b98dc 100644 --- a/src/main/java/im/status/wallet/SecureChannel.java +++ b/src/main/java/im/status/wallet/SecureChannel.java @@ -53,7 +53,7 @@ public class SecureChannel { apdu.setOutgoingAndSend((short) 0, SC_SECRET_LENGTH); } - public short decryptAPDU(byte[] apduBuffer) { + public byte decryptAPDU(byte[] apduBuffer) { scCipher.init(scKey, Cipher.MODE_DECRYPT, apduBuffer, ISO7816.OFFSET_CDATA, SC_BLOCK_SIZE); short len = scCipher.doFinal(apduBuffer, (short)(ISO7816.OFFSET_CDATA + SC_BLOCK_SIZE), (short) (apduBuffer[ISO7816.OFFSET_LC] - SC_BLOCK_SIZE), apduBuffer, ISO7816.OFFSET_CDATA); @@ -61,7 +61,9 @@ public class SecureChannel { len--; } - return (short) (len - 1); + apduBuffer[ISO7816.OFFSET_LC] = (byte) (len - 1); + + return apduBuffer[ISO7816.OFFSET_LC]; } public short encryptAPDU(byte[] apduBuffer, short len) { diff --git a/src/main/java/im/status/wallet/WalletApplet.java b/src/main/java/im/status/wallet/WalletApplet.java index 703e6a1..d020aff 100644 --- a/src/main/java/im/status/wallet/WalletApplet.java +++ b/src/main/java/im/status/wallet/WalletApplet.java @@ -88,9 +88,9 @@ public class WalletApplet extends Applet { } byte[] apduBuffer = apdu.getBuffer(); - short len = secureChannel.decryptAPDU(apduBuffer); + byte len = secureChannel.decryptAPDU(apduBuffer); - if (!ownerPIN.check(apduBuffer, ISO7816.OFFSET_CDATA, (byte) len)) { + if (!ownerPIN.check(apduBuffer, ISO7816.OFFSET_CDATA, len)) { ISOException.throwIt((short)((short) 0x63c0 | (short) ownerPIN.getTriesRemaining())); } } @@ -101,6 +101,16 @@ public class WalletApplet extends Applet { if (!(secureChannel.isOpen() && ownerPIN.isValidated())) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + + byte[] apduBuffer = apdu.getBuffer(); + byte len = secureChannel.decryptAPDU(apduBuffer); + + if (!(len == 6 && allDigits(apduBuffer, ISO7816.OFFSET_CDATA, len))) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + } + + ownerPIN.update(apduBuffer, ISO7816.OFFSET_CDATA, len); + ownerPIN.check(apduBuffer, ISO7816.OFFSET_CDATA, len); } private void unblockPIN(APDU apdu) { @@ -126,4 +136,18 @@ public class WalletApplet extends Applet { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } } + + private boolean allDigits(byte[] buffer, short off, short len) { + while(len > 0) { + len--; + + byte c = buffer[(short)(off+len)]; + + if (c < 0x30 || c > 0x39) { + return false; + } + } + + return true; + } } diff --git a/src/test/java/im/status/wallet/WalletAppletCommandSet.java b/src/test/java/im/status/wallet/WalletAppletCommandSet.java index ac064fe..877692c 100644 --- a/src/test/java/im/status/wallet/WalletAppletCommandSet.java +++ b/src/test/java/im/status/wallet/WalletAppletCommandSet.java @@ -35,4 +35,9 @@ public class WalletAppletCommandSet { CommandAPDU verifyPIN = new CommandAPDU(0x80, WalletApplet.INS_VERIFY_PIN, 0, 0, secureChannel.encryptAPDU(pin.getBytes())); return apduChannel.transmit(verifyPIN); } + + public ResponseAPDU changePIN(String pin) throws CardException { + CommandAPDU changePIN = new CommandAPDU(0x80, WalletApplet.INS_CHANGE_PIN, 0, 0, secureChannel.encryptAPDU(pin.getBytes())); + return apduChannel.transmit(changePIN); + } } diff --git a/src/test/java/im/status/wallet/WalletAppletTest.java b/src/test/java/im/status/wallet/WalletAppletTest.java index 236e66c..76b2f4d 100644 --- a/src/test/java/im/status/wallet/WalletAppletTest.java +++ b/src/test/java/im/status/wallet/WalletAppletTest.java @@ -95,4 +95,44 @@ public class WalletAppletTest { //TODO: Unblock PIN to make the test non-destructive } + + @Test + @DisplayName("CHANGE PIN command") + void changePinTest() throws CardException { + ResponseAPDU response = cmdSet.changePIN("123456"); + assertEquals(0x6985, response.getSW()); + + cmdSet.openSecureChannel(); + + response = cmdSet.changePIN("123456"); + assertEquals(0x6985, response.getSW()); + + response = cmdSet.verifyPIN("000000"); + assertEquals(0x9000, response.getSW()); + + response = cmdSet.changePIN("123456"); + assertEquals(0x9000, response.getSW()); + + response = cmdSet.changePIN("654321"); + assertEquals(0x9000, response.getSW()); + + apduChannel.getCard().getATR(); + cmdSet.select(); + cmdSet.openSecureChannel(); + + response = cmdSet.verifyPIN("654321"); + assertEquals(0x9000, response.getSW()); + + response = cmdSet.changePIN("654a21"); + assertEquals(0x6A80, response.getSW()); + + response = cmdSet.changePIN("54321"); + assertEquals(0x6A80, response.getSW()); + + response = cmdSet.changePIN("7654321"); + assertEquals(0x6A80, response.getSW()); + + response = cmdSet.changePIN("000000"); + assertEquals(0x9000, response.getSW()); + } }