From 73af477a8774e1ede5dd8de6491eb353dc0b12bd Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 22 Mar 2024 23:58:55 +0100 Subject: Basic Gradle setup. --- .../java/cz/crcs/ectester/applet/AppletBase.java | 962 +++++++++++++ .../java/cz/crcs/ectester/applet/AppletUtil.java | 35 + .../java/cz/crcs/ectester/applet/CardConsts.java | 65 + .../cz/crcs/ectester/applet/ECKeyGenerator.java | 526 +++++++ .../java/cz/crcs/ectester/applet/ECKeyTester.java | 225 +++ .../cz/crcs/ectester/applet/ECTesterApplet.java | 63 + .../ectester/applet/ECTesterAppletExtended.java | 64 + .../java/cz/crcs/ectester/applet/EC_Consts.java | 1512 ++++++++++++++++++++ 8 files changed, 3452 insertions(+) create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/AppletBase.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/AppletUtil.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/CardConsts.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/ECKeyGenerator.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/ECKeyTester.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/ECTesterApplet.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/ECTesterAppletExtended.java create mode 100644 applet/src/main/java/cz/crcs/ectester/applet/EC_Consts.java (limited to 'applet/src/main/java') diff --git a/applet/src/main/java/cz/crcs/ectester/applet/AppletBase.java b/applet/src/main/java/cz/crcs/ectester/applet/AppletBase.java new file mode 100644 index 0000000..411e5a1 --- /dev/null +++ b/applet/src/main/java/cz/crcs/ectester/applet/AppletBase.java @@ -0,0 +1,962 @@ +package cz.crcs.ectester.applet; + +import javacard.framework.*; +import javacard.security.*; + +/** + * Applet base class, that handles instructions, given + * either basic or extended length APDUs. + * + * @author Petr Svenda petr@svenda.com + * @author Jan Jancar johny@neuromancer.sk + */ +public abstract class AppletBase extends Applet { + public static final byte[] VERSION = {'v', '0', '.', '3', '.', '3'}; + + public static final short ARRAY_LENGTH = 0x100; + public static final short APDU_MAX_LENGTH = 1024;//512 + + // TEMPORARRY ARRAY IN RAM + byte[] ramArray = null; + byte[] ramArray2 = null; + byte[] apduArray = null; + short apduEnd = 0; + short cdata = 0; + + RandomData randomData = null; + + ECKeyTester keyTester = null; + ECKeyGenerator keyGenerator = null; + KeyPair localKeypair = null; + KeyPair remoteKeypair = null; + + protected AppletBase(byte[] buffer, short offset, byte length) { + if (length > 9) { + /* + short dataOffset = offset; + // shift to privilege offset + dataOffset += (short) (1 + buffer[offset]); + // finally shift to Application specific offset + dataOffset += (short) (1 + buffer[dataOffset]); + // go to proprietary data + dataOffset++; + */ + short resetMemory = JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_TRANSIENT_RESET); + short deselectMemory = JCSystem.getAvailableMemory(JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT); + short bigMem; + short smallMem; + byte bigMemType; + byte smallMemType; + if (resetMemory >= deselectMemory) { + bigMem = resetMemory; + smallMem = deselectMemory; + bigMemType = JCSystem.CLEAR_ON_RESET; + smallMemType = JCSystem.CLEAR_ON_DESELECT; + } else { + bigMem = deselectMemory; + smallMem = resetMemory; + bigMemType = JCSystem.CLEAR_ON_DESELECT; + smallMemType = JCSystem.CLEAR_ON_RESET; + } + short[] lensBig = new short[]{APDU_MAX_LENGTH + 2 * ARRAY_LENGTH, APDU_MAX_LENGTH + ARRAY_LENGTH, APDU_MAX_LENGTH,}; + short[] lensSmall = new short[]{0, ARRAY_LENGTH, 2 * ARRAY_LENGTH}; + byte[] allocsBig = new byte[]{0x07, 0x03, 0x01}; + boolean done = false; + for (short i = 0; i < 3; ++i) { + if (lensBig[i] <= bigMem && lensSmall[i] <= smallMem) { + byte allocI = 1; + while (allocI < 0x08) { + byte type = ((allocI & allocsBig[i]) != 0) ? bigMemType : smallMemType; + switch (allocI) { + case 0x01: + apduArray = JCSystem.makeTransientByteArray(APDU_MAX_LENGTH, type); + break; + case 0x02: + ramArray = JCSystem.makeTransientByteArray(ARRAY_LENGTH, type); + break; + case 0x04: + ramArray2 = JCSystem.makeTransientByteArray(ARRAY_LENGTH, type); + break; + } + allocI = (byte) (allocI << 1); + } + done = true; + break; + } + } + if (!done) { + ISOException.throwIt((short) 0x6a84); + } + + randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); + EC_Consts.randomData = randomData; + + keyGenerator = new ECKeyGenerator(); + keyTester = new ECKeyTester(); + } + } + + public void process(APDU apdu) throws ISOException { + // get the APDU buffer + byte[] apduBuffer = apdu.getBuffer(); + byte cla = apduBuffer[ISO7816.OFFSET_CLA]; + byte ins = apduBuffer[ISO7816.OFFSET_INS]; + + // ignore the applet select command dispatched to the process + if (selectingApplet()) { + return; + } + + if (cla == CardConsts.CLA_ECTESTERAPPLET) { + try { + if (ins == CardConsts.INS_BUFFER) { + short read = readAPDU(apdu, true); + if (read == -1) { + ISOException.throwIt(CardConsts.SW_CANNOT_FIT); + return; + } + apduEnd += read; + apdu.setOutgoingAndSend((short) 0, (short) 0); + return; + } else { + apduEnd = 0; + if (ins == CardConsts.INS_PERFORM) { + ins = apduArray[ISO7816.OFFSET_INS]; + apdu.setIncomingAndReceive(); + } else { + if (readAPDU(apdu, false) == -1) { + ISOException.throwIt(CardConsts.SW_CANNOT_FIT); + return; + } + } + } + + short length = 0; + switch (ins) { + case CardConsts.INS_ALLOCATE_KA: + length = insAllocateKA(apdu); + break; + case CardConsts.INS_ALLOCATE_SIG: + length = insAllocateSig(apdu); + break; + case CardConsts.INS_ALLOCATE: + length = insAllocate(apdu); + break; + case CardConsts.INS_CLEAR: + length = insClear(apdu); + break; + case CardConsts.INS_SET: + length = insSet(apdu); + break; + case CardConsts.INS_TRANSFORM: + length = insTransform(apdu); + break; + case CardConsts.INS_GENERATE: + length = insGenerate(apdu); + break; + case CardConsts.INS_EXPORT: + length = insExport(apdu); + break; + case CardConsts.INS_ECDH: + length = insECDH(apdu); + break; + case CardConsts.INS_ECDH_DIRECT: + length = insECDH_direct(apdu); + break; + case CardConsts.INS_ECDSA: + length = insECDSA(apdu); + break; + case CardConsts.INS_ECDSA_SIGN: + length = insECDSA_sign(apdu); + break; + case CardConsts.INS_ECDSA_VERIFY: + length = insECDSA_verify(apdu); + break; + case CardConsts.INS_CLEANUP: + length = insCleanup(apdu); + break; + case CardConsts.INS_GET_INFO: + length = insGetInfo(apdu); + break; + case CardConsts.INS_SET_DRY_RUN_MODE: + length = insSetDryRunMode(apdu); + break; + default: + // The INS code is not supported by the dispatcher + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + break; + } + apdu.setOutgoingAndSend((short) 0, length); + + } catch (ISOException e) { + throw e; // Our exception from code, just re-emit + } catch (ArrayIndexOutOfBoundsException e) { + ISOException.throwIt(CardConsts.SW_ArrayIndexOutOfBoundsException); + } catch (ArithmeticException e) { + ISOException.throwIt(CardConsts.SW_ArithmeticException); + } catch (ArrayStoreException e) { + ISOException.throwIt(CardConsts.SW_ArrayStoreException); + } catch (NullPointerException e) { + ISOException.throwIt(CardConsts.SW_NullPointerException); + } catch (NegativeArraySizeException e) { + ISOException.throwIt(CardConsts.SW_NegativeArraySizeException); + } catch (CryptoException e) { + ISOException.throwIt((short) (CardConsts.SW_CryptoException_prefix | e.getReason())); + } catch (SystemException e) { + ISOException.throwIt((short) (CardConsts.SW_SystemException_prefix | e.getReason())); + } catch (PINException e) { + ISOException.throwIt((short) (CardConsts.SW_PINException_prefix | e.getReason())); + } catch (TransactionException e) { + ISOException.throwIt((short) (CardConsts.SW_TransactionException_prefix | e.getReason())); + } catch (CardRuntimeException e) { + ISOException.throwIt((short) (CardConsts.SW_CardRuntimeException_prefix | e.getReason())); + } catch (Exception e) { + ISOException.throwIt(CardConsts.SW_Exception); + } + + } else ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } + + private short readAPDU(APDU apdu, boolean skipHeader) { + byte[] apduBuffer = apdu.getBuffer(); + + /* How much stuff is in apduBuffer */ + short read = apdu.setIncomingAndReceive(); + short cdataOffset = getOffsetCdata(apdu); + read += cdataOffset; + + /* Where to start reading from? */ + short offset = 0; + if (skipHeader) { + offset = cdataOffset; + cdata = CardConsts.CDATA_EXTENDED; + } else { + cdata = CardConsts.CDATA_BASIC; + } + + /* How much stuff was really sent in this APDU? */ + short total = (short) (getIncomingLength(apdu) + cdataOffset); + short todo = (short) (total - offset); + /* Can we fit? */ + if (todo > (short) (apduArray.length - apduEnd)) { + return -1; + } + + /* How much stuff was copied over. */ + short written = 0; + while (written < todo) { + Util.arrayCopyNonAtomic(apduBuffer, offset, apduArray, (short) (apduEnd + written), (short) (read - offset)); + written += (short) (read - offset); + offset = 0; + read = apdu.receiveBytes((short) 0); + } + return written; + } + + abstract short getOffsetCdata(APDU apdu); + + abstract short getIncomingLength(APDU apdu); + + abstract short getBase(); + + /** + * Allocates KeyAgreement object, returns allocate SW. + * + * @param apdu DATA = byte KeyAgreementType + * @return length of response + */ + private short insAllocateKA(APDU apdu) { + byte kaType = apduArray[cdata]; + short sw = keyTester.allocateKA(kaType); + Util.setShort(apdu.getBuffer(), (short) 0, sw); + return 2; + } + + /** + * Allocates a Signature object, returns allocate SW. + * + * @param apdu DATA = byte SignatureType + * @return length of response + */ + private short insAllocateSig(APDU apdu) { + byte sigType = apduArray[cdata]; + short sw = keyTester.allocateSig(sigType); + Util.setShort(apdu.getBuffer(), (short) 0, sw); + return 2; + } + + /** + * Allocates local and remote keyPairs. + * returns allocate SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = byte build + * DATA = short keyLength + * byte keyClass + * @return length of response + */ + private short insAllocate(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + byte build = apduArray[ISO7816.OFFSET_P2]; + short keyLength = Util.getShort(apduArray, cdata); + byte keyClass = apduArray[(short) (cdata + 2)]; + + return allocate(keyPair, build, keyLength, keyClass, apdu.getBuffer(), (short) 0); + } + + /** + * Clears local and remote keyPair's keys {@code .clearKey()}. + * returns clearKey SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = + * @return length of response + */ + private short insClear(APDU apdu) { + byte keyPair = apduArray[ISO7816.OFFSET_P1]; + + short len = 0; + if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) { + len += clear(localKeypair, apdu.getBuffer(), (short) 0); + } + if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) { + len += clear(remoteKeypair, apdu.getBuffer(), len); + } + + return len; + } + + /** + * Sets curve parameters on local and remote keyPairs. + * returns setCurve SWs + * + * @param apdu P1 = byte keyPair (KEYPAIR_* | ...) + * P2 = byte curve (EC_Consts.CURVE_*) + * DATA = short params (EC_Consts.PARAMETER_* | ...) + *
+ * if curveID = CURVE_EXTERNAL:
+ * [short paramLength, byte[] param],
+ * for all params in params,
+ * in order: field,a,b,g,r,k,w,s
+ * @return length of response
+ */
+ private short insSet(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte curve = apduArray[ISO7816.OFFSET_P2];
+ short params = Util.getShort(apduArray, cdata);
+
+ short len = 0;
+
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += set(localKeypair, curve, params, apduArray, (short) (cdata + 2), apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += set(remoteKeypair, curve, params, apduArray, (short) (cdata + 2), apdu.getBuffer(), len);
+ }
+
+ return len;
+ }
+
+ /**
+ * Transforms curve paramaters of local and remote keyPairs.
+ * returns transformCurve SWs
+ *
+ * @param apdu P1 = byte keyPair (KEYPAIR_* | ...)
+ * P2 = byte key (EC_Consts.KEY_* | ...)
+ * DATA = short params (EC_Consts.PARAMETER_* | ...)
+ * short transformation (EC_Consts.TRANSFORMATION_* || ...)
+ * @return length of response
+ */
+ private short insTransform(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte key = apduArray[ISO7816.OFFSET_P2];
+ short params = Util.getShort(apduArray, cdata);
+ short transformation = Util.getShort(apduArray, (short) (cdata + 2));
+
+ short len = 0;
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += transform(localKeypair, key, params, transformation, apdu.getBuffer(), (short) 0);
+ }
+
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += transform(remoteKeypair, key, params, transformation, apdu.getBuffer(), len);
+ }
+
+ return len;
+ }
+
+ /**
+ * Generates the local and remote keyPairs.
+ * returns generate SWs
+ *
+ * @param apdu P1 = byte keyPair (KEYPAIR_* | ...)
+ * P2 =
+ * @return length of response
+ */
+ private short insGenerate(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+
+ short len = 0;
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += generate(localKeypair, apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += generate(remoteKeypair, apdu.getBuffer(), len);
+ }
+
+ return len;
+ }
+
+ /**
+ * Exports selected key and domain parameters from the selected keyPair and key.
+ *
+ * @param apdu P1 = byte keyPair (KEYPAIR_* | ...)
+ * P2 = byte key (EC_Consts.KEY_* | ...)
+ * DATA = short params
+ * @return length of response
+ */
+ private short insExport(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte key = apduArray[ISO7816.OFFSET_P2];
+ short params = Util.getShort(apduArray, cdata);
+
+ short swOffset = 0;
+ short len = (short) (keyPair == CardConsts.KEYPAIR_BOTH ? 4 : 2);
+
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += export(localKeypair, key, params, apdu.getBuffer(), swOffset, len);
+ swOffset += 2;
+ }
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += export(remoteKeypair, key, params, apdu.getBuffer(), swOffset, len);
+ }
+
+ return len;
+ }
+
+ /**
+ * Performs ECDH, between the pubkey specified in P1(local/remote) and the privkey specified in P2(local/remote).
+ * returns deriveSecret SW, {@code if(export == EXPORT_TRUE)} => short secretlen, byte[] secret
+ *
+ * @param apdu P1 = byte pubkey (KEYPAIR_*)
+ * P2 = byte privkey (KEYPAIR_*)
+ * DATA = byte export (EXPORT_TRUE || EXPORT_FALSE)
+ * short transformation (EC_Consts.TRANSFORMATION_* | ...)
+ * byte type (EC_Consts.KA_* | ...)
+ * @return length of response
+ */
+ private short insECDH(APDU apdu) {
+ byte pubkey = apduArray[ISO7816.OFFSET_P1];
+ byte privkey = apduArray[ISO7816.OFFSET_P2];
+ byte export = apduArray[cdata];
+ short transformation = Util.getShort(apduArray, (short) (cdata + 1));
+ byte type = apduArray[(short) (cdata + 3)];
+
+ return ecdh(pubkey, privkey, export, transformation, type, apdu.getBuffer(), (short) 0);
+ }
+
+ /**
+ * Performs ECDH, directly between the privkey specified in P1(local/remote) and the raw data
+ *
+ * @param apdu P1 = byte privkey (KEYPAIR_*)
+ * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE)
+ * DATA = short transformation (EC_Consts.TRANSFORMATION_* | ...)
+ * byte type (EC_Consts.KA_* | ...)
+ * short length
+ * byte[] pubkey
+ * @return length of response
+ */
+ private short insECDH_direct(APDU apdu) {
+ byte privkey = apduArray[ISO7816.OFFSET_P1];
+ byte export = apduArray[ISO7816.OFFSET_P2];
+ short transformation = Util.getShort(apduArray, cdata);
+ byte type = apduArray[(short) (cdata + 2)];
+ short length = Util.getShort(apduArray, (short) (cdata + 3));
+
+ return ecdh_direct(privkey, export, transformation, type, (short) (cdata + 5), length, apdu.getBuffer(), (short) 0);
+ }
+
+ /**
+ * Performs ECDSA signature and verification on data provided or random, using the keyPair in P1(local/remote).
+ * returns ecdsa SW, {@code if(export == EXPORT_TRUE)} => short signature_length, byte[] signature
+ *
+ * @param apdu P1 = byte keyPair (KEYPAIR_*)
+ * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE)
+ * DATA = byte sigType
+ * short dataLength (00 = random data generated, !00 = data length)
+ * byte[] data
+ * @return length of response
+ */
+ private short insECDSA(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte export = apduArray[ISO7816.OFFSET_P2];
+ byte sigType = apduArray[cdata];
+
+ short len = 0;
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += ecdsa(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += ecdsa(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len);
+ }
+
+ return len;
+ }
+
+ /**
+ * @param apdu P1 = byte keyPair (KEYPAIR_*)
+ * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE)
+ * DATA = byte sigType
+ * short dataLength (00 = random data generated, !00 = data length)
+ * byte[] data
+ * @return length of response
+ */
+ private short insECDSA_sign(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte export = apduArray[ISO7816.OFFSET_P2];
+ byte sigType = apduArray[cdata];
+
+ short len = 0;
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += ecdsa_sign(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += ecdsa_sign(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len);
+ }
+ return len;
+ }
+
+ /**
+ * @param apdu P1 = byte keyPair (KEYPAIR_*)
+ * P2 = byte sigType
+ * DATA = short dataLength (00 = random data generated, !00 = data length)
+ * byte[] data
+ * short sigLength
+ * byte[] signature
+ * @return length of response
+ */
+ private short insECDSA_verify(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte sigType = apduArray[ISO7816.OFFSET_P2];
+
+ short len = 0;
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ len += ecdsa_verify(localKeypair, sigType, apduArray, cdata, apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ len += ecdsa_verify(remoteKeypair, sigType, apduArray, cdata, apdu.getBuffer(), len);
+ }
+ return len;
+ }
+
+
+ /**
+ * Performs card memory cleanup via JCSystem.requestObjectDeletion()
+ *
+ * @param apdu no data
+ * @return length of response
+ */
+ private short insCleanup(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+
+ return cleanup(apdubuf, (short) 0);
+ }
+
+ /**
+ * Gathers info about the applet and the card environment.
+ *
+ * @param apdu no data
+ * @return length of response
+ */
+ private short insGetInfo(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+
+ return getInfo(apdubuf, (short) 0);
+ }
+
+ /**
+ * Set the dry run mode of the applet.
+ *
+ * @param apdu P1 = byte mode (MODE_* || ...)
+ * @return length of response
+ */
+ private short insSetDryRunMode(APDU apdu) {
+ byte[] apdubuf = apdu.getBuffer();
+ byte mode = apduArray[ISO7816.OFFSET_P1];
+
+ short len = 0;
+ if (mode == CardConsts.MODE_NORMAL) {
+ len = setDryRunMode(apdubuf, false, (short) 0);
+ }
+ if (mode == CardConsts.MODE_DRY_RUN) {
+ len = setDryRunMode(apdubuf, true, (short) 0);
+ }
+ return len;
+ }
+
+ /**
+ * @param keyPair which keyPair to use, local/remote (KEYPAIR_* | ...)
+ * @param build whether to use KeyBuilder or Keypair alloc
+ * @param keyLength key length to set
+ * @param keyClass key class to allocate
+ * @param outBuffer buffer to write sw to
+ * @param outOffset offset into buffer
+ * @return length of data written to the buffer
+ */
+ private short allocate(byte keyPair, byte build, short keyLength, byte keyClass, byte[] outBuffer, short outOffset) {
+ short length = 0;
+ if ((keyPair & CardConsts.KEYPAIR_LOCAL) != 0) {
+ if ((build & CardConsts.BUILD_KEYPAIR) != 0) {
+ localKeypair = keyGenerator.allocatePair(keyClass, keyLength);
+ if (keyGenerator.getSW() != ISO7816.SW_NO_ERROR && (build & CardConsts.BUILD_KEYBUILDER) != 0) {
+ localKeypair = keyGenerator.constructPair(keyClass, keyLength);
+ }
+ } else if ((build & CardConsts.BUILD_KEYBUILDER) != 0) {
+ localKeypair = keyGenerator.constructPair(keyClass, keyLength);
+ }
+ Util.setShort(outBuffer, outOffset, keyGenerator.getSW());
+ length += 2;
+ }
+
+ if ((keyPair & CardConsts.KEYPAIR_REMOTE) != 0) {
+ if ((build & CardConsts.BUILD_KEYPAIR) != 0) {
+ remoteKeypair = keyGenerator.allocatePair(keyClass, keyLength);
+ if (keyGenerator.getSW() != ISO7816.SW_NO_ERROR && (build & CardConsts.BUILD_KEYBUILDER) != 0) {
+ remoteKeypair = keyGenerator.constructPair(keyClass, keyLength);
+ }
+ } else if ((build & CardConsts.BUILD_KEYBUILDER) != 0) {
+ remoteKeypair = keyGenerator.constructPair(keyClass, keyLength);
+ }
+ Util.setShort(outBuffer, (short) (outOffset + length), keyGenerator.getSW());
+ length += 2;
+ }
+
+ return length;
+ }
+
+ /**
+ * @param keyPair KeyPair to clear
+ * @param outBuffer buffer to write sw to
+ * @param outOffset offset into buffer
+ * @return length of data written to the buffer
+ */
+ private short clear(KeyPair keyPair, byte[] outBuffer, short outOffset) {
+ short sw = keyGenerator.clearPair(keyPair, EC_Consts.KEY_BOTH);
+ Util.setShort(outBuffer, outOffset, sw);
+
+ return 2;
+ }
+
+ /**
+ * @param keyPair KeyPair to set params on
+ * @param curve curve to set (EC_Consts.CURVE_*)
+ * @param params parameters to set (EC_Consts.PARAMETER_* | ...)
+ * @param inBuffer buffer to read params from
+ * @param inOffset input offset in buffer
+ * @param outBuffer buffer to write sw to
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short set(KeyPair keyPair, byte curve, short params, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) {
+ short sw = ISO7816.SW_NO_ERROR;
+
+ switch (curve) {
+ case EC_Consts.CURVE_default:
+ //default, dont set anything
+ break;
+ case EC_Consts.CURVE_external:
+ //external
+ sw = keyGenerator.setExternalCurve(keyPair, params, inBuffer, inOffset);
+ break;
+ default:
+ //custom
+ sw = keyGenerator.setCurve(keyPair, curve, params, ramArray, (short) 0);
+ break;
+ }
+
+ Util.setShort(outBuffer, outOffset, sw);
+ return 2;
+ }
+
+ /**
+ * @param keyPair KeyPair to transform
+ * @param key key to transform (EC_Consts.KEY_* | ...)
+ * @param params parameters to transform (EC_Consts.PARAMETER_* | ...)
+ * @param transformation transformation type (EC_Consts.TRANSFORMATION_*)
+ * @param outBuffer buffer to output sw to
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short transform(KeyPair keyPair, byte key, short params, short transformation, byte[] outBuffer, short outOffset) {
+ short sw = keyGenerator.transformCurve(keyPair, key, params, transformation, ramArray, (short) 0);
+ Util.setShort(outBuffer, outOffset, sw);
+ return 2;
+ }
+
+ /**
+ * @param keyPair KeyPair to generate
+ * @param outBuffer buffer to output sw to
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short generate(KeyPair keyPair, byte[] outBuffer, short outOffset) {
+ short sw = keyGenerator.generatePair(keyPair);
+ Util.setShort(outBuffer, outOffset, sw);
+
+ return 2;
+ }
+
+ /**
+ * @param keyPair KeyPair to export from
+ * @param key which key to export from (EC_Consts.KEY_PUBLIC | EC_Consts.KEY_PRIVATE)
+ * @param params which params to export (EC_Consts.PARAMETER_* | ...)
+ * @param outBuffer buffer to export params to
+ * @param swOffset offset to output sw to buffer
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short export(KeyPair keyPair, byte key, short params, byte[] outBuffer, short swOffset, short outOffset) {
+ short length = 0;
+
+ short sw = ISO7816.SW_NO_ERROR;
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) {
+ //export params from public
+ length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PUBLIC, params, outBuffer, outOffset);
+ sw = keyGenerator.getSW();
+ }
+ if ((key & EC_Consts.KEY_PRIVATE) != 0 && sw == ISO7816.SW_NO_ERROR) {
+ //export params from private
+ length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PRIVATE, params, outBuffer, (short) (outOffset + length));
+ sw = keyGenerator.getSW();
+ }
+ Util.setShort(outBuffer, swOffset, sw);
+
+ return length;
+ }
+
+ /**
+ * @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
+ * @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
+ * @param export whether to export ECDH secret
+ * @param transformation whether to transform the pubkey before ECDH
+ * @param type KeyAgreement type to test
+ * @param outBuffer buffer to write sw to, and export ECDH secret {@code if(export == EXPORT_TRUE)}
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short ecdh(byte pubkey, byte privkey, byte export, short transformation, byte type, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ KeyPair pub = ((pubkey & CardConsts.KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
+ KeyPair priv = ((privkey & CardConsts.KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
+
+ short secretLength = 0;
+ if (keyTester.getKaType() == type) {
+ secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation);
+ } else {
+ short allocateSW = keyTester.allocateKA(type);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation);
+ }
+ }
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ if ((export == CardConsts.EXPORT_TRUE)) {
+ Util.setShort(outBuffer, (short) (outOffset + length), secretLength);
+ length += 2;
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), secretLength);
+ length += secretLength;
+ }
+
+ return length;
+ }
+
+ private short ecdh_direct(byte privkey, byte export, short transformation, byte type, short keyOffset, short keyLength, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ KeyPair priv = ((privkey & CardConsts.KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
+
+ short secretLength = 0;
+ if (keyTester.getKaType() == type) {
+ secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation);
+ } else {
+ short allocateSW = keyTester.allocateKA(type);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation);
+ }
+ }
+
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ if ((export == CardConsts.EXPORT_TRUE)) {
+ Util.setShort(outBuffer, (short) (outOffset + length), secretLength);
+ length += 2;
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), secretLength);
+ length += secretLength;
+ }
+ return length;
+ }
+
+ /**
+ * @param sign keyPair to use for signing and verification
+ * @param sigType Signature type to use
+ * @param export whether to export ECDSA signature
+ * @param inBuffer buffer to read dataLength and data to sign from
+ * @param inOffset input offset in buffer
+ * @param outBuffer buffer to write sw to, and export ECDSA signature {@code if(export == EXPORT_TRUE)}
+ * @param outOffset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short ecdsa(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ short dataLength = Util.getShort(inBuffer, inOffset);
+ if (dataLength == 0) { //no data to sign
+ //generate random
+ dataLength = 64;
+ randomData.generateData(ramArray, (short) 0, dataLength);
+ } else {
+ Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength);
+ }
+
+ short signatureLength = 0;
+ if (keyTester.getSigType() == sigType) {
+ signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ } else {
+ short allocateSW = keyTester.allocateSig(sigType);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ signatureLength = keyTester.testECDSA((ECPrivateKey) sign.getPrivate(), (ECPublicKey) sign.getPublic(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ }
+ }
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ if (export == CardConsts.EXPORT_TRUE) {
+ Util.setShort(outBuffer, (short) (outOffset + length), signatureLength);
+ length += 2;
+
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength);
+ length += signatureLength;
+ }
+
+ return length;
+ }
+
+ private short ecdsa_sign(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ short dataLength = Util.getShort(inBuffer, inOffset);
+ if (dataLength == 0) { //no data to sign
+ //generate random
+ dataLength = 64;
+ randomData.generateData(ramArray, (short) 0, dataLength);
+ } else {
+ Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength);
+ }
+
+ short signatureLength = 0;
+ if (keyTester.getSigType() == sigType) {
+ signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ } else {
+ short allocateSW = keyTester.allocateSig(sigType);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ }
+ }
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ if (export == CardConsts.EXPORT_TRUE) {
+ Util.setShort(outBuffer, (short) (outOffset + length), signatureLength);
+ length += 2;
+
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength);
+ length += signatureLength;
+ }
+
+ return length;
+ }
+
+ private short ecdsa_verify(KeyPair verify, byte sigType, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ short dataLength = Util.getShort(inBuffer, inOffset);
+ short dataOffset = (short) (inOffset + 2);
+ short sigLength = Util.getShort(inBuffer, (short) (dataOffset + dataLength));
+ short sigOffset = (short) (dataOffset + dataLength + 2);
+
+ if (keyTester.getSigType() == sigType) {
+ keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength);
+ } else {
+ short allocateSW = keyTester.allocateSig(sigType);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength);
+ }
+ }
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ return length;
+ }
+
+ /**
+ * @param buffer buffer to write sw to
+ * @param offset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short cleanup(byte[] buffer, short offset) {
+ short sw = ISO7816.SW_NO_ERROR;
+ try {
+ if (JCSystem.isObjectDeletionSupported())
+ JCSystem.requestObjectDeletion();
+ } catch (CardRuntimeException crex) {
+ sw = crex.getReason();
+ }
+
+ Util.setShort(buffer, offset, sw);
+ return 2;
+ }
+
+ /**
+ * @param buffer buffer to write sw to
+ * @param offset output offset in buffer
+ * @return length of data written to the buffer
+ */
+ private short getInfo(byte[] buffer, short offset) {
+ short length = 0;
+ Util.setShort(buffer, (short) (offset + length), ISO7816.SW_NO_ERROR);
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), (short) VERSION.length);
+ length += 2;
+ Util.arrayCopyNonAtomic(VERSION, (short) 0, buffer, (short) (offset + length), (short) (VERSION.length));
+ length += VERSION.length;
+ Util.setShort(buffer, (short) (offset + length), getBase());
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), JCSystem.getVersion());
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), (short) (JCSystem.isObjectDeletionSupported() ? 1 : 0));
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), (short) buffer.length);
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), (short) ramArray.length);
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), (short) ramArray2.length);
+ length += 2;
+ Util.setShort(buffer, (short) (offset + length), (short) apduArray.length);
+ length += 2;
+ return length;
+ }
+
+ private short setDryRunMode(byte[] buffer, boolean mode, short offset) {
+ if (keyTester != null) {
+ keyTester.setDryRun(mode);
+ }
+ if (keyGenerator != null) {
+ keyGenerator.setDryRun(mode);
+ }
+ Util.setShort(buffer, offset, ISO7816.SW_NO_ERROR);
+ return 2;
+ }
+}
diff --git a/applet/src/main/java/cz/crcs/ectester/applet/AppletUtil.java b/applet/src/main/java/cz/crcs/ectester/applet/AppletUtil.java
new file mode 100644
index 0000000..c4a6744
--- /dev/null
+++ b/applet/src/main/java/cz/crcs/ectester/applet/AppletUtil.java
@@ -0,0 +1,35 @@
+package cz.crcs.ectester.applet;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyPair;
+import javacard.security.Signature;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class AppletUtil {
+
+ private static short nullCheck(Object obj, short sw) {
+ if (obj == null)
+ ISOException.throwIt(sw);
+ return ISO7816.SW_NO_ERROR;
+ }
+
+ public static short objCheck(Object obj) {
+ return nullCheck(obj, CardConsts.SW_OBJECT_NULL);
+ }
+
+ public static short keypairCheck(KeyPair keyPair) {
+ return nullCheck(keyPair, CardConsts.SW_KEYPAIR_NULL);
+ }
+
+ public static short kaCheck(KeyAgreement keyAgreement) {
+ return nullCheck(keyAgreement, CardConsts.SW_KA_NULL);
+ }
+
+ public static short signCheck(Signature signature) {
+ return nullCheck(signature, CardConsts.SW_SIGNATURE_NULL);
+ }
+}
diff --git a/applet/src/main/java/cz/crcs/ectester/applet/CardConsts.java b/applet/src/main/java/cz/crcs/ectester/applet/CardConsts.java
new file mode 100644
index 0000000..abac8e7
--- /dev/null
+++ b/applet/src/main/java/cz/crcs/ectester/applet/CardConsts.java
@@ -0,0 +1,65 @@
+package cz.crcs.ectester.applet;
+
+public class CardConsts {
+ // MAIN INSTRUCTION CLASS
+ public static final byte CLA_ECTESTERAPPLET = (byte) 0xB0;
+
+ // INSTRUCTIONS
+ public static final byte INS_ALLOCATE = (byte) 0x5a;
+ public static final byte INS_CLEAR = (byte) 0x5b;
+ public static final byte INS_SET = (byte) 0x5c;
+ public static final byte INS_TRANSFORM = (byte) 0x5d;
+ public static final byte INS_GENERATE = (byte) 0x5e;
+ public static final byte INS_EXPORT = (byte) 0x5f;
+ public static final byte INS_ECDH = (byte) 0x70;
+ public static final byte INS_ECDH_DIRECT = (byte) 0x71;
+ public static final byte INS_ECDSA = (byte) 0x72;
+ public static final byte INS_ECDSA_SIGN = (byte) 0x73;
+ public static final byte INS_ECDSA_VERIFY = (byte) 0x74;
+ public static final byte INS_CLEANUP = (byte) 0x75;
+ public static final byte INS_ALLOCATE_KA = (byte) 0x76;
+ public static final byte INS_ALLOCATE_SIG = (byte) 0x77;
+ public static final byte INS_GET_INFO = (byte) 0x78;
+ public static final byte INS_SET_DRY_RUN_MODE = (byte) 0x79;
+ public static final byte INS_BUFFER = (byte) 0x7a;
+ public static final byte INS_PERFORM = (byte) 0x7b;
+
+ // PARAMETERS for P1 and P2
+ public static final byte KEYPAIR_LOCAL = (byte) 0x01;
+ public static final byte KEYPAIR_REMOTE = (byte) 0x02;
+ public static final byte KEYPAIR_BOTH = KEYPAIR_LOCAL | KEYPAIR_REMOTE;
+ public static final byte BUILD_KEYPAIR = (byte) 0x01;
+ public static final byte BUILD_KEYBUILDER = (byte) 0x02;
+ public static final byte EXPORT_TRUE = (byte) 0xff;
+ public static final byte EXPORT_FALSE = (byte) 0x00;
+ public static final byte MODE_NORMAL = (byte) 0xaa;
+ public static final byte MODE_DRY_RUN = (byte) 0xbb;
+
+ // STATUS WORDS
+ public static final short SW_SIG_VERIFY_FAIL = (short) 0x0ee1;
+ public static final short SW_DH_DHC_MISMATCH = (short) 0x0ee2;
+ public static final short SW_KEYPAIR_NULL = (short) 0x0ee3;
+ public static final short SW_KA_NULL = (short) 0x0ee4;
+ public static final short SW_SIGNATURE_NULL = (short) 0x0ee5;
+ public static final short SW_OBJECT_NULL = (short) 0x0ee6;
+ public static final short SW_CANNOT_FIT = (short) 0x0ee7;
+ public static final short SW_Exception = (short) 0xff01;
+ public static final short SW_ArrayIndexOutOfBoundsException = (short) 0xff02;
+ public static final short SW_ArithmeticException = (short) 0xff03;
+ public static final short SW_ArrayStoreException = (short) 0xff04;
+ public static final short SW_NullPointerException = (short) 0xff05;
+ public static final short SW_NegativeArraySizeException = (short) 0xff06;
+ public static final short SW_CryptoException_prefix = (short) 0xf100;
+ public static final short SW_SystemException_prefix = (short) 0xf200;
+ public static final short SW_PINException_prefix = (short) 0xf300;
+ public static final short SW_TransactionException_prefix = (short) 0xf400;
+ public static final short SW_CardRuntimeException_prefix = (short) 0xf500;
+
+ //
+ public static final short BASE_221 = (short) 0x0221;
+ public static final short BASE_222 = (short) 0x0222;
+
+ //
+ public static final short CDATA_BASIC = (short) 5;
+ public static final short CDATA_EXTENDED = (short) 7;
+}
diff --git a/applet/src/main/java/cz/crcs/ectester/applet/ECKeyGenerator.java b/applet/src/main/java/cz/crcs/ectester/applet/ECKeyGenerator.java
new file mode 100644
index 0000000..601654a
--- /dev/null
+++ b/applet/src/main/java/cz/crcs/ectester/applet/ECKeyGenerator.java
@@ -0,0 +1,526 @@
+package cz.crcs.ectester.applet;
+
+import javacard.framework.CardRuntimeException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+import javacard.security.*;
+
+/**
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class ECKeyGenerator {
+
+ private short sw = ISO7816.SW_NO_ERROR;
+ private boolean dryRun = false;
+
+ /**
+ * @param keyClass
+ * @param keyLength
+ * @return
+ */
+ public KeyPair allocatePair(byte keyClass, short keyLength) {
+ sw = ISO7816.SW_NO_ERROR;
+ KeyPair ecKeyPair = null;
+ try {
+ if (!dryRun) {
+ ecKeyPair = new KeyPair(keyClass, keyLength);
+
+ if (ecKeyPair.getPublic() == null || ecKeyPair.getPrivate() == null) {
+ try {
+ ecKeyPair.genKeyPair();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return ecKeyPair;
+ }
+
+ /**
+ * @param keyClass
+ * @param keyLength
+ * @return
+ */
+ public KeyPair constructPair(byte keyClass, short keyLength) {
+ sw = ISO7816.SW_NO_ERROR;
+ KeyPair ecKeyPair = null;
+ byte privKeyType;
+ byte pubKeyType;
+ if (keyClass == KeyPair.ALG_EC_FP) {
+ privKeyType = KeyBuilder.TYPE_EC_FP_PRIVATE;
+ pubKeyType = KeyBuilder.TYPE_EC_FP_PUBLIC;
+ } else {
+ privKeyType = KeyBuilder.TYPE_EC_F2M_PRIVATE;
+ pubKeyType = KeyBuilder.TYPE_EC_F2M_PUBLIC;
+ }
+ try {
+ if (!dryRun) {
+ ECPrivateKey privateKey = (ECPrivateKey) KeyBuilder.buildKey(privKeyType, keyLength, false);
+ ECPublicKey publicKey = (ECPublicKey) KeyBuilder.buildKey(pubKeyType, keyLength, false);
+
+ ecKeyPair = new KeyPair(publicKey, privateKey);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return ecKeyPair;
+ }
+
+ /**
+ * @param keypair
+ * @param key
+ * @return
+ */
+ public short clearPair(KeyPair keypair, byte key) {
+ try {
+ sw = AppletUtil.keypairCheck(keypair);
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) keypair.getPublic().clearKey();
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) keypair.getPrivate().clearKey();
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return sw;
+ }
+
+ /**
+ * @param keypair
+ * @return
+ */
+ public short generatePair(KeyPair keypair) {
+ try {
+ sw = AppletUtil.keypairCheck(keypair);
+ if (!dryRun) {
+ keypair.genKeyPair();
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return sw;
+ }
+
+ /**
+ * @param keypair
+ * @param curve
+ * @param buffer
+ * @param offset
+ * @return
+ */
+ public short setCurve(KeyPair keypair, byte curve, byte[] buffer, short offset) {
+ return setCurve(keypair, curve, EC_Consts.PARAMETERS_ALL, buffer, offset);
+ }
+
+ /**
+ * @param keypair
+ * @param curve
+ * @param params
+ * @param buffer
+ * @param offset
+ * @return
+ */
+ public short setCurve(KeyPair keypair, byte curve, short params, byte[] buffer, short offset) {
+ return setCurve(keypair, EC_Consts.KEY_BOTH, curve, params, buffer, offset);
+ }
+
+ /**
+ * @param keypair
+ * @param key
+ * @param curve
+ * @param params
+ * @param buffer
+ * @param offset
+ * @return
+ */
+ public short setCurve(KeyPair keypair, byte key, byte curve, short params, byte[] buffer, short offset) {
+ byte alg = EC_Consts.getCurveType(curve);
+ sw = ISO7816.SW_NO_ERROR;
+
+ if (params == EC_Consts.PARAMETERS_NONE) {
+ return sw;
+ }
+
+ short length;
+ //handle fp and f2m differently, as a FP KeyPair doesnt contain a F2M field and vice versa.
+ if (alg == KeyPair.ALG_EC_FP && (params & EC_Consts.PARAMETER_FP) != 0) {
+ length = EC_Consts.getCurveParameter(curve, EC_Consts.PARAMETER_FP, buffer, offset);
+ sw = setParameter(keypair, key, EC_Consts.PARAMETER_FP, buffer, offset, length);
+ } else if (alg == KeyPair.ALG_EC_F2M && (params & EC_Consts.PARAMETER_F2M) != 0) {
+ length = EC_Consts.getCurveParameter(curve, EC_Consts.PARAMETER_F2M, buffer, offset);
+ sw = setParameter(keypair, key, EC_Consts.PARAMETER_F2M, buffer, offset, length);
+ }
+ if (sw != ISO7816.SW_NO_ERROR) return sw;
+
+ //go through all params
+ short paramMask = EC_Consts.PARAMETER_A;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (paramMask & params);
+ if (masked != 0) {
+ length = EC_Consts.getCurveParameter(curve, masked, buffer, offset);
+ sw = setParameter(keypair, key, masked, buffer, offset, length);
+ if (sw != ISO7816.SW_NO_ERROR) break;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return sw;
+ }
+
+ /**
+ * @param keypair
+ * @param params
+ * @param transformation
+ * @param buffer
+ * @param offset
+ * @return
+ */
+ public short transformCurve(KeyPair keypair, short params, short transformation, byte[] buffer, short offset) {
+ return transformCurve(keypair, EC_Consts.KEY_BOTH, params, transformation, buffer, offset);
+ }
+
+ /**
+ * @param keypair
+ * @param key
+ * @param params
+ * @param transformation
+ * @param buffer
+ * @param offset
+ * @return
+ */
+ public short transformCurve(KeyPair keypair, byte key, short params, short transformation, byte[] buffer, short offset) {
+ sw = ISO7816.SW_NO_ERROR;
+ if (params == EC_Consts.PARAMETERS_NONE) {
+ return sw;
+ }
+
+ //go through param bit by bit, and invalidate all selected params
+ short paramMask = EC_Consts.PARAMETER_FP;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (paramMask & params);
+ if (masked != 0) {
+ short length = exportParameter(keypair, key, masked, buffer, offset);
+ length = EC_Consts.transformParameter(transformation, buffer, offset, length);
+ sw = setParameter(keypair, key, masked, buffer, offset, length);
+ if (sw != ISO7816.SW_NO_ERROR) break;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return sw;
+ }
+
+ /**
+ * @param key
+ * @param param
+ * @param data
+ * @param offset
+ * @param length
+ * @return
+ */
+ public short setParameter(KeyPair keypair, byte key, short param, byte[] data, short offset, short length) {
+ try {
+ sw = AppletUtil.keypairCheck(keypair);
+
+ ECPublicKey ecPublicKey = null;
+ ECPrivateKey ecPrivateKey = null;
+ if (!dryRun) {
+ ecPublicKey = (ECPublicKey) keypair.getPublic();
+ ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+ }
+
+ switch (param) {
+ case EC_Consts.PARAMETER_FP:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldFP(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldFP(data, offset, length);
+ }
+ break;
+ case EC_Consts.PARAMETER_F2M:
+ if (length == 4) {
+ short i = Util.getShort(data, (short) (offset + 2));
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i);
+ }
+ } else if (length == 8) {
+ short i1 = Util.getShort(data, (short) (offset + 2));
+ short i2 = Util.getShort(data, (short) (offset + 4));
+ short i3 = Util.getShort(data, (short) (offset + 6));
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i1, i2, i3);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i1, i2, i3);
+ }
+ // if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setFieldF2M(i3, i2, i1);
+ // if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setFieldF2M(i3, i2, i1);
+ } else {
+ sw = ISO7816.SW_UNKNOWN;
+ }
+ break;
+ case EC_Consts.PARAMETER_A:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setA(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setA(data, offset, length);
+ }
+ break;
+ case EC_Consts.PARAMETER_B:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setB(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setB(data, offset, length);
+ }
+ break;
+ case EC_Consts.PARAMETER_G:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setG(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setG(data, offset, length);
+ }
+ break;
+ case EC_Consts.PARAMETER_R:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setR(data, offset, length);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setR(data, offset, length);
+ }
+ break;
+ case EC_Consts.PARAMETER_K:
+ short k = 0;
+ if (length > 2 || length <= 0) {
+ sw = ISO7816.SW_UNKNOWN;
+ break;
+ } else if (length == 2) {
+ k = Util.getShort(data, offset);
+ } else if (length == 1) {
+ k = data[offset];
+ }
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setK(k);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setK(k);
+ }
+ break;
+ case EC_Consts.PARAMETER_S:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) ecPrivateKey.setS(data, offset, length);
+ }
+ break;
+ case EC_Consts.PARAMETER_W:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) ecPublicKey.setW(data, offset, length);
+ }
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return sw;
+ }
+
+ /**
+ * @param keypair
+ * @param params
+ * @param inBuffer
+ * @param inOffset
+ * @return
+ */
+ public short setExternalCurve(KeyPair keypair, short params, byte[] inBuffer, short inOffset) {
+ return setExternalCurve(keypair, EC_Consts.KEY_BOTH, params, inBuffer, inOffset);
+ }
+
+ /**
+ * @param keypair
+ * @param key
+ * @param params
+ * @param inBuffer
+ * @param inOffset
+ * @return
+ */
+ public short setExternalCurve(KeyPair keypair, byte key, short params, byte[] inBuffer, short inOffset) {
+ sw = ISO7816.SW_NO_ERROR;
+ if (params == EC_Consts.PARAMETERS_NONE) {
+ return sw;
+ }
+
+ short paramMask = EC_Consts.PARAMETER_FP;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (paramMask & params);
+ if (masked != 0) {
+ short paramLength = Util.getShort(inBuffer, inOffset);
+ inOffset += 2;
+ sw = setParameter(keypair, key, masked, inBuffer, inOffset, paramLength);
+ inOffset += paramLength;
+ if (sw != ISO7816.SW_NO_ERROR) break;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return sw;
+ }
+
+ /**
+ * Exports a selected parameter from a given keyPairs key.
+ *
+ * @param keypair keypair to export from
+ * @param key key to export from (KEY_PUBLIC || KEY_PRIVATE)
+ * @param param parameter to export (EC_Consts.PARAMETER_* || ...)
+ * @param outputBuffer buffer to write to
+ * @param outputOffset offset to start writing in buffer
+ * @return length of data written
+ */
+ public short exportParameter(KeyPair keypair, byte key, short param, byte[] outputBuffer, short outputOffset) {
+ short length = 0;
+ try {
+ sw = AppletUtil.keypairCheck(keypair);
+
+ ECPublicKey ecPublicKey = null;
+ ECPrivateKey ecPrivateKey = null;
+ if (!dryRun) {
+ ecPublicKey = (ECPublicKey) keypair.getPublic();
+ ecPrivateKey = (ECPrivateKey) keypair.getPrivate();
+ }
+
+ switch (param) {
+ case EC_Consts.PARAMETER_FP:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0)
+ length = ecPublicKey.getField(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0)
+ length = ecPrivateKey.getField(outputBuffer, outputOffset);
+ }
+ break;
+ case EC_Consts.PARAMETER_F2M:
+ if ((key & EC_Consts.KEY_PUBLIC) != 0 && !dryRun) {
+ Util.setShort(outputBuffer, outputOffset, ecPublicKey.getSize());
+ length = 2;
+ length += ecPublicKey.getField(outputBuffer, (short) (outputOffset + 2));
+ }
+ if ((key & EC_Consts.KEY_PRIVATE) != 0 && !dryRun) {
+ Util.setShort(outputBuffer, outputOffset, ecPrivateKey.getSize());
+ length = 2;
+ length += ecPrivateKey.getField(outputBuffer, (short) (outputOffset + 2));
+ }
+ break;
+ case EC_Consts.PARAMETER_A:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getA(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getA(outputBuffer, outputOffset);
+ }
+ break;
+ case EC_Consts.PARAMETER_B:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getB(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getB(outputBuffer, outputOffset);
+ }
+ break;
+ case EC_Consts.PARAMETER_G:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getG(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getG(outputBuffer, outputOffset);
+ }
+ break;
+ case EC_Consts.PARAMETER_R:
+ if (!dryRun) {
+ if ((key & EC_Consts.KEY_PUBLIC) != 0) length = ecPublicKey.getR(outputBuffer, outputOffset);
+ if ((key & EC_Consts.KEY_PRIVATE) != 0) length = ecPrivateKey.getR(outputBuffer, outputOffset);
+ }
+ break;
+ case EC_Consts.PARAMETER_K:
+ if (!dryRun) {
+ length = 2;
+ if ((key & EC_Consts.KEY_PUBLIC) != 0)
+ Util.setShort(outputBuffer, outputOffset, ecPublicKey.getK());
+ if ((key & EC_Consts.KEY_PRIVATE) != 0)
+ Util.setShort(outputBuffer, outputOffset, ecPrivateKey.getK());
+ }
+ break;
+ case EC_Consts.PARAMETER_W:
+ if ((key & EC_Consts.KEY_PUBLIC) != 0 && !dryRun)
+ length = ecPublicKey.getW(outputBuffer, outputOffset);
+ break;
+ case EC_Consts.PARAMETER_S:
+ if ((key & EC_Consts.KEY_PRIVATE) != 0 && !dryRun)
+ length = ecPrivateKey.getS(outputBuffer, outputOffset);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return length;
+ }
+
+ /**
+ * Exports selected parameters from a given keyPairs key.
+ * Raw parameter data is always prepended by its length as a
+ * short value. The order of parameters is the usual one from
+ * EC_Consts: field,a,b,g,r,k,w,s.
+ *
+ * @param keypair keyPair to export from
+ * @param key key to export from (KEY_PUBLIC || KEY_PRIVATE)
+ * @param params params to export (EC_Consts.PARAMETER_* | ...)
+ * @param buffer buffer to export to
+ * @param offset offset to start writing in buffer
+ * @return length of data written
+ */
+ public short exportParameters(KeyPair keypair, byte key, short params, byte[] buffer, short offset) {
+ sw = ISO7816.SW_NO_ERROR;
+ if (params == EC_Consts.PARAMETERS_NONE) {
+ return sw;
+ }
+
+ short length = 0;
+ short paramMask = EC_Consts.PARAMETER_FP;
+ while (paramMask <= EC_Consts.PARAMETER_S) {
+ short masked = (short) (paramMask & params);
+ if (masked != 0) {
+ short len = exportParameter(keypair, key, masked, buffer, (short) (offset + 2));
+ if (len == 0) {
+ paramMask = (short) (paramMask << 1);
+ continue;
+ }
+ Util.setShort(buffer, offset, len);
+ offset += len + 2;
+ length += len + 2;
+ }
+ paramMask = (short) (paramMask << 1);
+ }
+ return length;
+ }
+
+ /**
+ * Copies this KeyPairs curve parameters to another ECKeyGenerator.
+ *
+ * @param from keyPair to copy from
+ * @param to keyPair to copy to
+ * @param params parameters to copy
+ * @param buffer buffer to use for copying
+ * @param offset offset to use in buffer
+ * @return sw
+ */
+ public short copyCurve(KeyPair from, KeyPair to, short params, byte[] buffer, short offset) {
+ try {
+ sw = AppletUtil.keypairCheck(from);
+ sw = AppletUtil.keypairCheck(to);
+
+ short param = EC_Consts.PARAMETER_FP;
+ while (param <= EC_Consts.PARAMETER_K) {
+ short masked = (short) (param & params);
+ if (masked != 0) {
+ short paramLength = exportParameter(from, EC_Consts.KEY_PUBLIC, masked, buffer, offset);
+ setParameter(to, EC_Consts.KEY_BOTH, masked, buffer, offset, paramLength);
+ }
+ param = (short) (param << 1);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return sw;
+ }
+
+ public short getSW() {
+ return sw;
+ }
+
+ public void setDryRun(boolean dryRun) {
+ this.dryRun = dryRun;
+ }
+}
diff --git a/applet/src/main/java/cz/crcs/ectester/applet/ECKeyTester.java b/applet/src/main/java/cz/crcs/ectester/applet/ECKeyTester.java
new file mode 100644
index 0000000..3af1cca
--- /dev/null
+++ b/applet/src/main/java/cz/crcs/ectester/applet/ECKeyTester.java
@@ -0,0 +1,225 @@
+package cz.crcs.ectester.applet;
+
+
+import javacard.framework.CardRuntimeException;
+import javacard.framework.ISO7816;
+import javacard.security.*;
+
+/**
+ * Class capable of testing ECDH/C and ECDSA.
+ * Note that ECDH and ECDHC output should equal, only the algorithm is different.
+ *
+ * @author Jan Jancar johny@neuromancer.sk
+ */
+public class ECKeyTester {
+ private KeyAgreement ecKeyAgreement = null;
+ private short kaType = 0;
+ private Signature ecdsaSignature = null;
+ private short sigType = 0;
+
+ private short sw = ISO7816.SW_NO_ERROR;
+ private boolean dryRun = false;
+
+ public short allocateKA(byte algorithm) {
+ sw = ISO7816.SW_NO_ERROR;
+ try {
+ if (!dryRun) {
+ ecKeyAgreement = KeyAgreement.getInstance(algorithm, false);
+ kaType = algorithm;
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return sw;
+ }
+
+ public short allocateSig(byte algorithm) {
+ sw = ISO7816.SW_NO_ERROR;
+ try {
+ if (!dryRun) {
+ ecdsaSignature = Signature.getInstance(algorithm, false);
+ sigType = algorithm;
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return sw;
+ }
+
+ /**
+ * Tests ECDH secret generation with keys from given {@code privatePair} and {@code publicPair}.
+ * Uses {@code pubkeyBuffer} at {@code pubkeyOffset} for computations.
+ * Output should equal with ECDHC output.
+ *
+ * @param privatePair KeyPair from which the private key is used
+ * @param publicPair KeyPair from which the public key is used
+ * @param pubkeyBuffer buffer to be used for the public key
+ * @param pubkeyOffset offset into pubkeyBuffer that can be used for the public key
+ * @param outputBuffer buffer to be used for the secret output
+ * @param outputOffset offset into the outputBuffer
+ * @param transformation (EC_Consts.TRANSFORMATION_* | ...)
+ * @return derived secret length
+ **/
+ public short testKA(KeyPair privatePair, KeyPair publicPair, byte[] pubkeyBuffer, short pubkeyOffset, byte[] outputBuffer, short outputOffset, short transformation) {
+ short length = 0;
+ try {
+ sw = AppletUtil.kaCheck(ecKeyAgreement);
+ sw = AppletUtil.keypairCheck(privatePair);
+ sw = AppletUtil.keypairCheck(publicPair);
+ if (!dryRun) {
+ short pubkeyLength = ((ECPublicKey) publicPair.getPublic()).getW(pubkeyBuffer, pubkeyOffset);
+ ecKeyAgreement.init(privatePair.getPrivate());
+
+ pubkeyLength = EC_Consts.transformParameter(transformation, pubkeyBuffer, pubkeyOffset, pubkeyLength);
+ length = ecKeyAgreement.generateSecret(pubkeyBuffer, pubkeyOffset, pubkeyLength, outputBuffer, outputOffset);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return length;
+ }
+
+ /**
+ * @param privatePair
+ * @param pubkey
+ * @param pubkeyOffset
+ * @param pubkeyLength
+ * @param outpuBuffer
+ * @param outputOffset
+ * @param transformation
+ * @return
+ */
+ public short testKA_direct(KeyPair privatePair, byte[] pubkey, short pubkeyOffset, short pubkeyLength, byte[] outpuBuffer, short outputOffset, short transformation) {
+ short length = 0;
+ try {
+ sw = AppletUtil.kaCheck(ecKeyAgreement);
+ sw = AppletUtil.keypairCheck(privatePair);
+
+ if (!dryRun) {
+ ecKeyAgreement.init(privatePair.getPrivate());
+ pubkeyLength = EC_Consts.transformParameter(transformation, pubkey, pubkeyOffset, pubkeyLength);
+ length = ecKeyAgreement.generateSecret(pubkey, pubkeyOffset, pubkeyLength, outpuBuffer, outputOffset);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return length;
+ }
+
+ /**
+ * Uses {@code signKey} to sign data from {@code inputBuffer} at {@code inputOffset} with {@code inputOffset}.
+ * Then checks for correct signature length.
+ * Then tries verifying the data with {@code verifyKey}.
+ *
+ * @param signKey key to use for signing
+ * @param verifyKey key to use for verifying the signature
+ * @param inputBuffer buffer to sign data from
+ * @param inputOffset offset into inputBuffer to sign data from
+ * @param inputLength length of data to sign
+ * @param sigBuffer buffer to output signature to
+ * @param sigOffset offset into sigBuffer to output to
+ * @return signature length
+ */
+ public short testECDSA(ECPrivateKey signKey, ECPublicKey verifyKey, byte[] inputBuffer, short inputOffset, short inputLength, byte[] sigBuffer, short sigOffset) {
+ short length = 0;
+ try {
+ sw = AppletUtil.signCheck(ecdsaSignature);
+
+ if (!dryRun) {
+ ecdsaSignature.init(signKey, Signature.MODE_SIGN);
+ length = ecdsaSignature.sign(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset);
+
+ ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY);
+ if (!ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, length)) {
+ sw = CardConsts.SW_SIG_VERIFY_FAIL;
+ }
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return length;
+ }
+
+ /**
+ * @param signKey
+ * @param inputBuffer
+ * @param inputOffset
+ * @param inputLength
+ * @param sigBuffer
+ * @param sigOffset
+ * @return
+ */
+ public short testECDSA_sign(ECPrivateKey signKey, byte[] inputBuffer, short inputOffset, short inputLength, byte[] sigBuffer, short sigOffset) {
+ short length = 0;
+ try {
+ sw = AppletUtil.signCheck(ecdsaSignature);
+
+ if (!dryRun) {
+ ecdsaSignature.init(signKey, Signature.MODE_SIGN);
+ length = ecdsaSignature.sign(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset);
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return length;
+ }
+
+ /**
+ * @param verifyKey
+ * @param inputBuffer
+ * @param inputOffset
+ * @param inputLength
+ * @param sigBuffer
+ * @param sigOffset
+ * @param sigLength
+ * @return
+ */
+ public short testECDSA_verify(ECPublicKey verifyKey, byte[] inputBuffer, short inputOffset, short inputLength, byte[] sigBuffer, short sigOffset, short sigLength) {
+ short length = 0;
+ try {
+ sw = AppletUtil.signCheck(ecdsaSignature);
+
+ if (!dryRun) {
+ ecdsaSignature.init(verifyKey, Signature.MODE_VERIFY);
+ if (!ecdsaSignature.verify(inputBuffer, inputOffset, inputLength, sigBuffer, sigOffset, sigLength)) {
+ sw = CardConsts.SW_SIG_VERIFY_FAIL;
+ }
+ }
+ } catch (CardRuntimeException ce) {
+ sw = ce.getReason();
+ }
+ return length;
+ }
+
+ public KeyAgreement getKA() {
+ return ecKeyAgreement;
+ }
+
+ public Signature getSig() {
+ return ecdsaSignature;
+ }
+
+ public boolean hasKA() {
+ return ecKeyAgreement != null;
+ }
+
+ public boolean hasSig() {
+ return ecdsaSignature != null;
+ }
+
+ public short getKaType() {
+ return kaType;
+ }
+
+ public short getSigType() {
+ return sigType;
+ }
+
+ public short getSW() {
+ return sw;
+ }
+
+ public void setDryRun(boolean dryRun) {
+ this.dryRun = dryRun;
+ }
+}
diff --git a/applet/src/main/java/cz/crcs/ectester/applet/ECTesterApplet.java b/applet/src/main/java/cz/crcs/ectester/applet/ECTesterApplet.java
new file mode 100644
index 0000000..8eabd54
--- /dev/null
+++ b/applet/src/main/java/cz/crcs/ectester/applet/ECTesterApplet.java
@@ -0,0 +1,63 @@
+/*
+ * ECTester, tool for testing Elliptic curve cryptography implementations.
+ * Copyright (c) 2016-2019 Petr Svenda