diff options
| -rw-r--r-- | !uploader/gppro_upload.bat | 1 | ||||
| -rw-r--r-- | !uploader/gppro_upload_emv.bat | 3 | ||||
| -rw-r--r-- | !uploader/simpleECC.cap | bin | 8898 -> 9444 bytes | |||
| -rw-r--r-- | dist/SimpleAPDU.jar | bin | 42485 -> 43458 bytes | |||
| -rw-r--r-- | src/applets/SimpleECCApplet.java | 165 | ||||
| -rw-r--r-- | src/simpleapdu/SimpleAPDU.java | 102 |
6 files changed, 225 insertions, 46 deletions
diff --git a/!uploader/gppro_upload.bat b/!uploader/gppro_upload.bat index 9fd68d5..0dd7864 100644 --- a/!uploader/gppro_upload.bat +++ b/!uploader/gppro_upload.bat @@ -1,4 +1,5 @@ gp.exe -delete 4543546573746572 -deletedeps -verbose +gp.exe -deletedeps -verbose -delete 4A43416C6754657374 gp.exe -install simpleECC.cap -verbose diff --git a/!uploader/gppro_upload_emv.bat b/!uploader/gppro_upload_emv.bat index dac8169..8d757a0 100644 --- a/!uploader/gppro_upload_emv.bat +++ b/!uploader/gppro_upload_emv.bat @@ -1,5 +1,6 @@ gp.exe -delete 4C6162616B417070 -deletedeps -verbose -emv +gp.exe -deletedeps -verbose -emv -delete 4A43416C6754657374 -gp.exe -install simpleECC.cap -verbose -emv +gp.exe -install simpleECC.cap -verbose -emv -d diff --git a/!uploader/simpleECC.cap b/!uploader/simpleECC.cap Binary files differindex f9eeeb4..39e0bc4 100644 --- a/!uploader/simpleECC.cap +++ b/!uploader/simpleECC.cap diff --git a/dist/SimpleAPDU.jar b/dist/SimpleAPDU.jar Binary files differindex 771e051..8f8ff3f 100644 --- a/dist/SimpleAPDU.jar +++ b/dist/SimpleAPDU.jar diff --git a/src/applets/SimpleECCApplet.java b/src/applets/SimpleECCApplet.java index c3aa700..5519104 100644 --- a/src/applets/SimpleECCApplet.java +++ b/src/applets/SimpleECCApplet.java @@ -23,6 +23,7 @@ public class SimpleECCApplet extends javacard.framework.Applet final static byte INS_TESTECSUPPORTALL_FP = (byte) 0x5e; final static byte INS_TESTECSUPPORTALL_F2M = (byte) 0x5f; final static byte INS_TESTEC_GENERATEINVALID_FP = (byte) 0x70; + final static byte INS_TESTEC_LASTUSEDPARAMS = (byte) 0x40; @@ -43,6 +44,8 @@ public class SimpleECCApplet extends javacard.framework.Applet public final static byte ECTEST_GENERATE_KEYPAIR_INVALIDCUSTOMCURVE = (byte) 0xc6; public final static byte ECTEST_ECDH_AGREEMENT_VALID_POINT = (byte) 0xc7; public final static byte ECTEST_ECDH_AGREEMENT_INVALID_POINT = (byte) 0xc8; + public final static byte ECTEST_EXECUTED_REPEATS = (byte) 0xc9; + public final static byte ECTEST_DH_GENERATESECRET = (byte) 0xca; public final static short FLAG_ECTEST_ALLOCATE_KEYPAIR = (short) 0x0001; public final static short FLAG_ECTEST_GENERATE_KEYPAIR_DEFCURVE = (short) 0x0002; @@ -55,8 +58,15 @@ public class SimpleECCApplet extends javacard.framework.Applet public final static short FLAG_ECTEST_ALL = (short) 0x00ff; + public final static short CORRUPT_B_FULLRANDOM = (short) 0x0001; + public final static short CORRUPT_B_ONEBYTERANDOM = (short) 0x0002; + public final static short CORRUPT_B_LASTBYTEINCREMENT = (short) 0x0003; + + + public final static short SW_SKIPPED = (short) 0x0ee1; public final static short SW_KEYPAIR_GENERATED_INVALID = (short) 0x0ee2; + public final static short SW_INVALID_CORRUPTION_TYPE = (short) 0x0ee3; /* public static final byte[] EC192_FP_PUBLICW = new byte[]{ (byte) 0x04, (byte) 0xC9, (byte) 0xC0, (byte) 0xED, (byte) 0xFB, (byte) 0x27, @@ -105,6 +115,8 @@ public class SimpleECCApplet extends javacard.framework.Applet private byte m_ramArray2[] = null; // PERSISTENT ARRAY IN EEPROM private byte m_dataArray[] = null; + + short m_lenB = 0; protected SimpleECCApplet(byte[] buffer, short offset, byte length) { short dataOffset = offset; @@ -168,7 +180,9 @@ public class SimpleECCApplet extends javacard.framework.Applet case INS_TESTEC_GENERATEINVALID_FP: TestEC_FP_GenerateInvalidCurve(apdu); break; - + case INS_TESTEC_LASTUSEDPARAMS: + TestECSupportInvalidCurve_lastUsedParams(apdu); + break; /* case INS_ALLOCATEKEYPAIRS: AllocateKeyPairs(apdu); @@ -453,15 +467,23 @@ public class SimpleECCApplet extends javacard.framework.Applet byte[] apdubuf = apdu.getBuffer(); short len = apdu.setIncomingAndReceive(); + short offset = ISO7816.OFFSET_CDATA; + short repeats = Util.getShort(apdubuf, offset); + offset += 2; + short corruptionType = Util.getShort(apdubuf, offset); + offset += 2; + byte bRewindOnSuccess = apdubuf[offset]; + offset++; + short dataOffset = 0; // FP - dataOffset += TestECSupportInvalidCurve(KeyPair.ALG_EC_FP, (short) 160, apdubuf, dataOffset, apdubuf[ISO7816.OFFSET_P1]); + dataOffset += TestECSupportInvalidCurve(KeyPair.ALG_EC_FP, (short) 160, apdubuf, dataOffset, repeats, corruptionType, bRewindOnSuccess); apdu.setOutgoingAndSend((short) 0, dataOffset); } - short TestECSupportInvalidCurve(byte keyClass, short keyLen, byte[] buffer, short bufferOffset, short repeats) { + short TestECSupportInvalidCurve(byte keyClass, short keyLen, byte[] buffer, short bufferOffset, short repeats, short corruptionType, byte bRewindOnSuccess) { short baseOffset = bufferOffset; short testFlags = FLAG_ECTEST_ALL; @@ -476,6 +498,9 @@ public class SimpleECCApplet extends javacard.framework.Applet bufferOffset++; Util.setShort(buffer, bufferOffset, keyLen); bufferOffset += 2; + + short numExecutionsOffset = bufferOffset; // num executions to be stored later + bufferOffset += 2; // // 1. Allocate KeyPair object @@ -516,35 +541,112 @@ public class SimpleECCApplet extends javacard.framework.Applet // EC_Consts.m_random = randomData; EC_Consts.setValidECKeyParams(ecPubKey, ecPrivKey, keyClass, keyLen, m_ramArray); - short lenB = ecPubKey.getB(m_ramArray, (short) 0); // store valid B - + + m_lenB = ecPubKey.getB(m_ramArray, (short) 0); // store valid B + Util.arrayCopyNonAtomic(m_ramArray, (short) 0, m_ramArray2, (short) 0, m_lenB); // also in m_ramArray2 + short startOffset = bufferOffset; -// for (short i = 0; i < repeats; i++) { - for (short i = 0; i < (short) 1000; i++) { + short i; + for (i = 0; i < repeats; i++) { if ((testFlags & FLAG_ECTEST_SET_INVALIDCURVE) != (short) 0) { - bufferOffset = startOffset; - + if (bRewindOnSuccess == 1) { + // if nothing unexpected happened, rewind bufferOffset back again + bufferOffset = startOffset; + } + + // Store valid curve B param + ecPubKey.getB(m_ramArray, (short) 0); // store valid B + Util.arrayCopyNonAtomic(m_ramArray, (short) 0, m_ramArray2, (short) 0, m_lenB); // also in m_ramArray2 + // set invalid curve buffer[bufferOffset] = ECTEST_SET_INVALIDCURVE; bufferOffset++; - randomData.generateData(m_ramArray2, (short) 0, lenB); - ecPubKey.setB(m_ramArray2, (short) 0, lenB); - ecPrivKey.setB(m_ramArray2, (short) 0, lenB); - Util.setShort(buffer, bufferOffset, ISO7816.SW_NO_ERROR); - bufferOffset += 2; + // Supported types of invalid curve: + // 1. Completely random B + // 2. Valid B but with one random byte randomly changed + // 3. Valid B but with last byte incremented + switch (corruptionType) { + case CORRUPT_B_FULLRANDOM: + randomData.generateData(m_ramArray2, (short) 0, m_lenB); + break; + case CORRUPT_B_ONEBYTERANDOM: + // Copy valid B into m_ramArray2 + Util.arrayCopyNonAtomic(m_ramArray, (short) 0, m_ramArray2, (short) 0, m_lenB); + // Generate random position and one random byte for subsequent change + // Note - we are using same array m_ramArray2, but in area unsued by stored B + randomData.generateData(m_ramArray2, m_lenB, (short) 2); + + short rngPos = m_ramArray2[m_lenB]; // random position (within B) + if (rngPos < 0) { rngPos = (short) -rngPos; } // make it positive + rngPos %= m_lenB; + m_ramArray2[rngPos] = m_ramArray2[(short) (m_lenB + 1)]; // set random byte on random position + // Make sure its not the valid byte again + if (m_ramArray[rngPos] == m_ramArray2[rngPos]) { + m_ramArray2[rngPos] += 1; // if yes, just increment + } + + break; + case CORRUPT_B_LASTBYTEINCREMENT: + m_ramArray2[(short) (m_lenB - 1)] += 1; + // Make sure its not the valid byte again + if (m_ramArray[(short) (m_lenB - 1)] == m_ramArray2[(short) (m_lenB - 1)]) { + m_ramArray2[(short) (m_lenB - 1)] += 1; // if yes, increment once more + } + break; + default: + ISOException.throwIt(SW_INVALID_CORRUPTION_TYPE); + break; + } + + + // Set corrupted B parameter + try { + ecPubKey.setB(m_ramArray2, (short) 0, m_lenB); + ecPrivKey.setB(m_ramArray2, (short) 0, m_lenB); + Util.setShort(buffer, bufferOffset, ISO7816.SW_NO_ERROR); // ok if setB itself will not emit exception + bufferOffset += 2; + }catch (CryptoException e) { + Util.setShort(buffer, bufferOffset, e.getReason()); + bufferOffset += 2; + // if we reach this line, we are interested in value of B that caused incorrect response + break; // stop execution, return B + }catch (Exception e) { + Util.setShort(buffer, bufferOffset, ISO7816.SW_UNKNOWN); + bufferOffset += 2; + // if we reach this line, we are interested in value of B that caused incorrect response + break; // stop execution, return B + } + // Gen key pair with invalid curve try { buffer[bufferOffset] = ECTEST_GENERATE_KEYPAIR_INVALIDCUSTOMCURVE; bufferOffset++; // Should fail ecKeyPair.genKeyPair(); - // If this line is reached, we generated valid key pair - what should not happen - Util.setShort(buffer, bufferOffset, SW_KEYPAIR_GENERATED_INVALID); + // If this line is reached, we generated key pair - what should not happen + Util.setShort(buffer, bufferOffset, ISO7816.SW_NO_ERROR); bufferOffset += 2; + // if we reach this line, we are interested in value of B - Util.arrayCopyNonAtomic(m_ramArray2, (short) 0, buffer, bufferOffset, lenB); - bufferOffset += lenB; + try { + buffer[bufferOffset] = ECTEST_DH_GENERATESECRET; + bufferOffset++; + ecPrivKey = (ECPrivateKey) ecKeyPair.getPrivate(); + if (dhKeyAgreement == null) { + dhKeyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH, false); + } + dhKeyAgreement.init(ecPrivKey); + short lenW = ecPubKey.getW(m_ramArray2, (short) 0); // store valid B + dhKeyAgreement.generateSecret(m_ramArray2, (short) 0, lenW, m_ramArray, (short) 0); + } catch (CryptoException e) { + Util.setShort(buffer, bufferOffset, e.getReason()); + bufferOffset += 2; + } catch (Exception e) { + Util.setShort(buffer, bufferOffset, ISO7816.SW_UNKNOWN); + bufferOffset += 2; + } + break; // stop execution, return B } catch (CryptoException e) { Util.setShort(buffer, bufferOffset, e.getReason()); @@ -555,13 +657,14 @@ public class SimpleECCApplet extends javacard.framework.Applet } // - // Generate keypair with valid curve + // Generate keypair with valid curve - to check that whole engine is not somehow blocked + // after previous attempt with invalid curve // // set valid curve buffer[bufferOffset] = ECTEST_SET_VALIDCURVE; bufferOffset++; - ecPubKey.setB(m_ramArray, (short) 0, lenB); // valid B - ecPrivKey.setB(m_ramArray, (short) 0, lenB); + EC_Consts.setValidECKeyParams(ecPubKey, ecPrivKey, keyClass, keyLen, m_ramArray); + Util.setShort(buffer, bufferOffset, ISO7816.SW_NO_ERROR); bufferOffset += 2; @@ -571,15 +674,19 @@ public class SimpleECCApplet extends javacard.framework.Applet bufferOffset++; // Should succeed ecKeyPair.genKeyPair(); - // If this line is reached, we generated valid key pair + // If this line is reached, we generated valid key pair (expected) Util.setShort(buffer, bufferOffset, ISO7816.SW_NO_ERROR); bufferOffset += 2; } catch (CryptoException e) { Util.setShort(buffer, bufferOffset, e.getReason()); bufferOffset += 2; + // if we reach this line, we are interested in value of B that caused incorrect response + break; // stop execution, return B } catch (Exception e) { Util.setShort(buffer, bufferOffset, ISO7816.SW_UNKNOWN); bufferOffset += 2; + // if we reach this line, we are interested in value of B that caused incorrect response + break; // stop execution, return B } // If we reach this line => everything was as expected @@ -591,9 +698,23 @@ public class SimpleECCApplet extends javacard.framework.Applet } } + // Set number of executed repeats + Util.setShort(buffer, numExecutionsOffset, i); + return (short) (bufferOffset - baseOffset); } + void TestECSupportInvalidCurve_lastUsedParams(APDU apdu) { + byte[] apdubuf = apdu.getBuffer(); + apdu.setIncomingAndReceive(); + + short offset = 0; + Util.arrayCopyNonAtomic(m_ramArray2, (short) 0, apdubuf, offset, m_lenB); + offset += m_lenB; + + apdu.setOutgoingAndSend((short) 0, offset); + } + void AllocateKeyPairReturnDefCourve(APDU apdu) { byte[] apdubuf = apdu.getBuffer(); apdu.setIncomingAndReceive(); diff --git a/src/simpleapdu/SimpleAPDU.java b/src/simpleapdu/SimpleAPDU.java index 5898916..eb6a1b4 100644 --- a/src/simpleapdu/SimpleAPDU.java +++ b/src/simpleapdu/SimpleAPDU.java @@ -7,6 +7,7 @@ import javacard.framework.ISO7816; import javacard.security.CryptoException; import javacard.security.KeyPair; import javax.smartcardio.ResponseAPDU; +import org.bouncycastle.util.Arrays; /** * @@ -20,12 +21,31 @@ public class SimpleAPDU { private static byte TESTECSUPPORTALL_FP[] = {(byte) 0xB0, (byte) 0x5E, (byte) 0x00, (byte) 0x00, (byte) 0x00}; private static byte TESTECSUPPORTALL_F2M[] = {(byte) 0xB0, (byte) 0x5F, (byte) 0x00, (byte) 0x00, (byte) 0x00}; - private static byte TESTECSUPPORTALL_FP_KEYGEN_INVALIDCURVEB[] = {(byte) 0xB0, (byte) 0x70, (byte) 0x10, (byte) 0x00, (byte) 0x00}; + private static byte TESTECSUPPORTALL_LASTUSEDPARAMS[] = {(byte) 0xB0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + + private static byte TESTECSUPPORTALL_FP_KEYGEN_INVALIDCURVEB[] = {(byte) 0xB0, (byte) 0x70, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; static short getShort(byte[] array, int offset) { return (short) (((array[offset] & 0xFF) << 8) | (array[offset + 1] & 0xFF)); } + static void setShort(byte[] array, int offset, short value) { + array[offset + 1] = (byte) (value & 0xFF); + array[offset] = (byte) ((value >> 8) & 0xFF); + } + static void testFPkeyGen_setNumRepeats(byte[] apduArray, short numRepeats) { + // num repeats starts at index 5 + setShort(apduArray, 5, numRepeats); + } + static void testFPkeyGen_rewindOnSuccess(byte[] apduArray, boolean bRewind) { + // rewind info at index 7 + apduArray[7] = bRewind ? (byte) 1 : (byte) 0; + } + static void testFPkeyGen_setCorruptionType(byte[] apduArray, short corruptionType) { + // corruptionType starts at index 7 + setShort(apduArray, 7, corruptionType); + } + public static void main(String[] args) { try { // @@ -34,9 +54,18 @@ public class SimpleAPDU { if (cardManager.ConnectToCard()) { // Select our application on card cardManager.sendAPDU(SELECT_ECTESTERAPPLET); - // Test setting invalid curves - ResponseAPDU resp_fp_keygen = cardManager.sendAPDU(TESTECSUPPORTALL_FP_KEYGEN_INVALIDCURVEB); + + // Test setting invalid parameter B of curev + byte[] testAPDU = Arrays.clone(TESTECSUPPORTALL_FP_KEYGEN_INVALIDCURVEB); + //testFPkeyGen_setCorruptionType(testAPDU, SimpleECCApplet.CORRUPT_B_LASTBYTEINCREMENT); + testFPkeyGen_setCorruptionType(testAPDU, SimpleECCApplet.CORRUPT_B_ONEBYTERANDOM); + //testFPkeyGen_setCorruptionType(testAPDU, SimpleECCApplet.CORRUPT_B_FULLRANDOM); + testFPkeyGen_setNumRepeats(testAPDU, (short) 1000); + testFPkeyGen_rewindOnSuccess(testAPDU, false); + ResponseAPDU resp_fp_keygen = cardManager.sendAPDU(testAPDU); + ResponseAPDU resp_keygen_params = cardManager.sendAPDU(TESTECSUPPORTALL_LASTUSEDPARAMS); PrintECKeyGenInvalidCurveB(resp_fp_keygen); + PrintECKeyGenInvalidCurveB_lastUserParams(resp_keygen_params); // Test support for different types of curves ResponseAPDU resp_fp = cardManager.sendAPDU(TESTECSUPPORTALL_FP); @@ -44,7 +73,7 @@ public class SimpleAPDU { PrintECSupport(resp_fp); PrintECSupport(resp_f2m); - + cardManager.DisconnectFromCard(); } else { @@ -79,6 +108,12 @@ public class SimpleAPDU { if (code == SimpleECCApplet.SW_SKIPPED) { codeStr = "skipped"; } + if (code == SimpleECCApplet.SW_KEYPAIR_GENERATED_INVALID) { + codeStr = "SW_KEYPAIR_GENERATED_INVALID"; + } + if (code == SimpleECCApplet.SW_INVALID_CORRUPTION_TYPE) { + codeStr = "SW_INVALID_CORRUPTION_TYPE"; + } return String.format("fail\t(%s,\t0x%4x)", codeStr, code); } } @@ -89,26 +124,31 @@ public class SimpleAPDU { MUST_FAIL } static int VerifyPrintResult(String message, byte expectedTag, byte[] buffer, int bufferOffset, ExpResult expRes) { - if (buffer[bufferOffset] != expectedTag) { - System.out.println("ERROR: mismatched tag"); - assert(buffer[bufferOffset] == expectedTag); - } - bufferOffset++; - short resCode = getShort(buffer, bufferOffset); - bufferOffset += 2; - - boolean bHiglight = false; - if ((expRes == ExpResult.MUST_FAIL) && (resCode == ISO7816.SW_NO_ERROR)) { - bHiglight = true; - } - if ((expRes == ExpResult.SHOULD_SUCCEDD) && (resCode != ISO7816.SW_NO_ERROR)) { - bHiglight = true; - } - if (bHiglight) { - System.out.println(String.format("!! %-50s%s", message, getPrintError(resCode))); + if (bufferOffset >= buffer.length) { + System.out.println("No more data returned"); } else { - System.out.println(String.format(" %-50s%s", message, getPrintError(resCode))); + if (buffer[bufferOffset] != expectedTag) { + System.out.println("ERROR: mismatched tag"); + assert(buffer[bufferOffset] == expectedTag); + } + bufferOffset++; + short resCode = getShort(buffer, bufferOffset); + bufferOffset += 2; + + boolean bHiglight = false; + if ((expRes == ExpResult.MUST_FAIL) && (resCode == ISO7816.SW_NO_ERROR)) { + bHiglight = true; + } + if ((expRes == ExpResult.SHOULD_SUCCEDD) && (resCode != ISO7816.SW_NO_ERROR)) { + bHiglight = true; + } + if (bHiglight) { + System.out.println(String.format("!! %-50s%s", message, getPrintError(resCode))); + } + else { + System.out.println(String.format(" %-50s%s", message, getPrintError(resCode))); + } } return bufferOffset; } @@ -139,7 +179,7 @@ public class SimpleAPDU { bufferOffset = VerifyPrintResult("Generate key with valid curve:", SimpleECCApplet.ECTEST_GENERATE_KEYPAIR_CUSTOMCURVE, buffer, bufferOffset, ExpResult.SHOULD_SUCCEDD); bufferOffset = VerifyPrintResult("ECDH agreement with valid point:", SimpleECCApplet.ECTEST_ECDH_AGREEMENT_VALID_POINT, buffer, bufferOffset, ExpResult.SHOULD_SUCCEDD); bufferOffset = VerifyPrintResult("ECDH agreement with invalid point (fail is good):", SimpleECCApplet.ECTEST_ECDH_AGREEMENT_INVALID_POINT, buffer, bufferOffset, ExpResult.MUST_FAIL); - bufferOffset = VerifyPrintResult("Set invalid custom curve (my fail):", SimpleECCApplet.ECTEST_SET_INVALIDCURVE, buffer, bufferOffset, ExpResult.MAY_FAIL); + bufferOffset = VerifyPrintResult("Set invalid custom curve (may fail):", SimpleECCApplet.ECTEST_SET_INVALIDCURVE, buffer, bufferOffset, ExpResult.MAY_FAIL); bufferOffset = VerifyPrintResult("Generate key with invalid curve (fail is good):", SimpleECCApplet.ECTEST_GENERATE_KEYPAIR_INVALIDCUSTOMCURVE, buffer, bufferOffset, ExpResult.MUST_FAIL); System.out.println(); @@ -167,6 +207,11 @@ public class SimpleAPDU { System.out.println(String.format("%-53s%d bits", "EC key length (bits):", keyLen)); bufferOffset += 2; + short numRepeats = getShort(buffer, bufferOffset); + bufferOffset += 2; + System.out.println(String.format("Executed repeats before unexpected error: %d times", numRepeats)); + + bufferOffset = VerifyPrintResult("KeyPair object allocation:", SimpleECCApplet.ECTEST_ALLOCATE_KEYPAIR, buffer, bufferOffset, ExpResult.SHOULD_SUCCEDD); while (bufferOffset < buffer.length) { bufferOffset = VerifyPrintResult("Set invalid custom curve:", SimpleECCApplet.ECTEST_SET_INVALIDCURVE, buffer, bufferOffset, ExpResult.SHOULD_SUCCEDD); @@ -178,4 +223,15 @@ public class SimpleAPDU { System.out.println(); } } + + static void PrintECKeyGenInvalidCurveB_lastUserParams(ResponseAPDU resp) { + byte[] buffer = resp.getData(); + short offset = 0; + System.out.print("Last used value of B: "); + while (offset < buffer.length) { + System.out.print(String.format("%x ", buffer[offset])); + offset++; + } + + } } |
