diff --git a/.gitignore b/.gitignore index 56cd5e2c..04079938 100644 --- a/.gitignore +++ b/.gitignore @@ -115,5 +115,5 @@ bin/* vendor_libs/* /.apt_generated/ tools/format.bat -/tools/sdk/jdk-11.0.2 -/output +/tools/sdk/jdk-* +/output \ No newline at end of file diff --git a/build/build.cmd b/build/build.cmd index 90133da5..9238074c 100644 --- a/build/build.cmd +++ b/build/build.cmd @@ -5,7 +5,7 @@ REM These values must be defined correctly REM ********************************************************************* set ANT_HOME=..\tools\ant -set JAVA_HOME=..\tools\sdk\jdk-11.0.2 +set JAVA_HOME=..\tools\sdk\jdk-11.0.30 IF NOT EXIST %JAVA_HOME% ( ECHO The JDK path %JAVA_HOME% does not exist, aborting. diff --git a/build/build.xml b/build/build.xml index 17c7f2e9..a74dbfd7 100644 --- a/build/build.xml +++ b/build/build.xml @@ -33,7 +33,30 @@ - + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/asn1/OpenFIPS201-PUT-DATA.asn b/doc/asn1/OpenFIPS201-PUT-DATA.asn index d49a143b..ba906750 100644 --- a/doc/asn1/OpenFIPS201-PUT-DATA.asn +++ b/doc/asn1/OpenFIPS201-PUT-DATA.asn @@ -14,7 +14,8 @@ PutDataRequest ::= CHOICE { secureRequest [APPLICATION 31] NULL } -PutDataBulkRequest ::= [APPLICATION 10] SEQUENCE OF PutDataRequest +-- Moved from [APPLICATION 10] to avoid coliding with PutDataRequest.deletePinRequest +PutDataBulkRequest ::= [APPLICATION 30] SEQUENCE OF PutDataRequest PutDataCreateObjectRequest ::= SEQUENCE { id [11] OCTET STRING (SIZE(1..3)), diff --git a/src-platform/jc304/org/openfips201/applet/Platform.java b/src-platform/jc304/org/openfips201/applet/Platform.java index 80432907..c90781c2 100644 --- a/src-platform/jc304/org/openfips201/applet/Platform.java +++ b/src-platform/jc304/org/openfips201/applet/Platform.java @@ -40,6 +40,7 @@ import javacard.security.RandomData; import javacard.security.SecretKey; import javacard.security.Signature; +import javacard.security.CryptoException; import javacardx.crypto.Cipher; /* @@ -187,95 +188,19 @@ static class Cryptography { private static MessageDigest cspSHA384; private static RandomData cspRandom; - private Cryptography() { - } + // Basic support bitmap per mechanism ID + private static short mechanismSupport = (short) 0xffff; - private static void init() { - - try { - if (cspAES == null) { - cspAES = Cipher.getInstance(Cipher.CIPHER_AES_ECB, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspAES = null; - } + // Keygen support bitmap per mechanism ID + private static short generateSupport = (short) 0xffff; - try { - if (cspTDEA == null) { - cspTDEA = Cipher.getInstance(Cipher.CIPHER_DES_ECB, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspTDEA = null; - } - - try { - if (cspRSA == null) { - cspRSA = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspRSA = null; - } - - try { - if (cspECDSA == null) { - cspECDSA = Signature.getInstance(Signature.SIG_CIPHER_ECDSA, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspECDSA = null; - } - - try { - if (cspCMAC == null) { - cspCMAC = null; - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspCMAC = null; - } - - try { - if (cspECDH == null) { - cspECDH = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspECDH = null; - } - - try { - if (cspSHA256 == null) { - cspSHA256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspSHA256 = null; - } - - try { - if (cspSHA384 == null) { - cspSHA384 = MessageDigest.getInstance(MessageDigest.ALG_SHA_384, false); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspSHA384 = null; - } - - try { - if (cspRandom == null) { - cspRandom = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); - } - } catch (Exception ex) { - // Just fall-through if it isn't supported - cspRandom = null; - } + private Cryptography() { + } + static void init() { } - private static void terminate() { + static void terminate() { cspAES = null; cspTDEA = null; cspRSA = null; @@ -286,6 +211,66 @@ private static void terminate() { requestObjectDeletion(); } + private static short mechanismBit(byte mechanism) { + switch (mechanism) { + case Constants.ID_ALG_DEFAULT: + case Constants.ID_ALG_TDEA_3KEY: + return (short) 0x0001; + case Constants.ID_ALG_AES_128: + return (short) 0x0002; + case Constants.ID_ALG_AES_192: + return (short) 0x0004; + case Constants.ID_ALG_AES_256: + return (short) 0x0008; + case Constants.ID_ALG_RSA_1024: + return (short) 0x0010; + case Constants.ID_ALG_RSA_2048: + return (short) 0x0020; + case Constants.ID_ALG_RSA_3072: + return (short) 0x0040; + case Constants.ID_ALG_RSA_4096: + return (short) 0x0080; + case Constants.ID_ALG_ECC_P256: + case Constants.ID_ALG_ECC_CS2: + return (short) 0x0100; + case Constants.ID_ALG_ECC_P384: + case Constants.ID_ALG_ECC_CS7: + return (short) 0x0200; + default: + return (short) 0x0000; + } + } + + private static void setUnsupported(byte mechanism) { + mechanismSupport &= (short) ~mechanismBit(mechanism); + } + + private static void setGenerateUnsupported(byte mechanism) { + generateSupport &= (short) ~mechanismBit(mechanism); + } + + static boolean supportsGenerate(byte mechanism) { + return supportsMechanism(mechanism) && (generateSupport & mechanismBit(mechanism)) != (short) 0; + } + + // Ensures mechanism is flagged unsupported when encountering a NO_SUCH_ALGORITHM CryptoException. + static void onCryptoException(byte mechanism, CryptoException ex) { + if (ex.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + setUnsupported(mechanism); + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } + throw ex; + } + + // As per onCryptoException, but flagging keygen support. + static void onGenerateException(byte mechanism, CryptoException ex) { + if (ex.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + setGenerateUnsupported(mechanism); + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } + throw ex; + } + static boolean supportsMechanism(byte mechanism) { switch (mechanism) { @@ -293,28 +278,20 @@ static boolean supportsMechanism(byte mechanism) { // Supported Algorithms case Constants.ID_ALG_DEFAULT: case Constants.ID_ALG_TDEA_3KEY: - // FIPS: Disabled in Approved mode - return (!Config.FIPS_APPROVED_MODE && cspTDEA != null); + case Constants.ID_ALG_RSA_1024: + return !Config.FIPS_APPROVED_MODE && (mechanismSupport & mechanismBit(mechanism)) != (short) 0; case Constants.ID_ALG_AES_128: case Constants.ID_ALG_AES_192: case Constants.ID_ALG_AES_256: - return (cspAES != null); - - case Constants.ID_ALG_RSA_1024: - // FIPS: Disabled in Approved mode - return (!Config.FIPS_APPROVED_MODE && cspRSA != null); - case Constants.ID_ALG_RSA_2048: case Constants.ID_ALG_RSA_3072: case Constants.ID_ALG_RSA_4096: - return (cspRSA != null); - case Constants.ID_ALG_ECC_P256: case Constants.ID_ALG_ECC_P384: case Constants.ID_ALG_ECC_CS2: case Constants.ID_ALG_ECC_CS7: - return (cspECDSA != null && cspECDH != null); + return (mechanismSupport & mechanismBit(mechanism)) != (short) 0; default: return false; @@ -324,10 +301,16 @@ static boolean supportsMechanism(byte mechanism) { static MessageDigest getMessageDigest(byte algorithm) { switch (algorithm) { case MessageDigest.ALG_SHA_256: + if (cspSHA256 == null) { + cspSHA256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); + } cspSHA256.reset(); return cspSHA256; case MessageDigest.ALG_SHA_384: - cspSHA256.reset(); + if (cspSHA384 == null) { + cspSHA384 = MessageDigest.getInstance(MessageDigest.ALG_SHA_384, false); + } + cspSHA384.reset(); return cspSHA384; default: ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); @@ -385,7 +368,7 @@ static Key buildKey(byte algorithm, short length) { ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } - return null; + return (Key) key; default: return null; @@ -431,12 +414,18 @@ static short encipher(SecretKey secretKey, byte[] inBuffer, short inOffset, shor case KeyBuilder.TYPE_DES: case KeyBuilder.TYPE_DES_TRANSIENT_DESELECT: case KeyBuilder.TYPE_DES_TRANSIENT_RESET: + if (cspTDEA == null) { + cspTDEA = Cipher.getInstance(Cipher.CIPHER_DES_ECB, false); + } cipher = cspTDEA; break; case KeyBuilder.TYPE_AES: case KeyBuilder.TYPE_AES_TRANSIENT_DESELECT: case KeyBuilder.TYPE_AES_TRANSIENT_RESET: + if (cspAES == null) { + cspAES = Cipher.getInstance(Cipher.CIPHER_AES_ECB, false); + } cipher = cspAES; break; @@ -513,6 +502,9 @@ static short sign(ECPrivateKey privateKey, byte[] inBuffer, short inOffset, shor return (short) 0; // Keep compiler happy } + if (cspECDSA == null) { + cspECDSA = Signature.getInstance(Signature.SIG_CIPHER_ECDSA, false); + } cspECDSA.init(privateKey, Signature.MODE_SIGN); return cspECDSA.signPreComputedHash(inBuffer, inOffset, inLength, outBuffer, outOffset); } @@ -550,6 +542,9 @@ static short computeRSADP1(PrivateKey privateKey, byte[] inBuffer, short inOffse // with the only remaining option, which is to perform a private key decryption // operation, which makes us feel awkward and wrong. // + if (cspRSA == null) { + cspRSA = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + } cspRSA.init(privateKey, Cipher.MODE_DECRYPT); return cspRSA.doFinal(inBuffer, inOffset, inLength, outBuffer, outOffset); } @@ -570,7 +565,9 @@ static short computeECDH(ECPrivateKey privateKey, byte[] inBuffer, short inOffse // NOTE: The Java Card implementation of generateSecret() performs sufficient buffer state and // length checking that we don't double-up here. - + if (cspECDH == null) { + cspECDH = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); + } cspECDH.init(privateKey); return cspECDH.generateSecret(inBuffer, inOffset, inLength, outBuffer, outOffset); } @@ -589,6 +586,9 @@ static short generateRandom(byte[] buffer, short offset, short length) { buffer[(short) (offset + i)] = (byte) (i % 256); } } else { + if (cspRandom == null) { + cspRandom = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); + } cspRandom.generateData(buffer, offset, length); } diff --git a/src-platform/jc305/org/openfips201/applet/Platform.java b/src-platform/jc305/org/openfips201/applet/Platform.java index b2446efd..508da344 100644 --- a/src-platform/jc305/org/openfips201/applet/Platform.java +++ b/src-platform/jc305/org/openfips201/applet/Platform.java @@ -40,6 +40,7 @@ import javacard.security.RandomData; import javacard.security.SecretKey; import javacard.security.Signature; +import javacard.security.CryptoException; import javacardx.crypto.Cipher; /* @@ -192,15 +193,28 @@ static class Cryptography { private static ECKey ecParamsP256 = null; private static ECKey ecParamsP384 = null; + // Basic support bitmap per mechanism ID + private static short mechanismSupport = (short) 0xffff; + + // Keygen support bitmap per mechanism ID + private static short generateSupport = (short) 0xffff; + private Cryptography() { } private static void init() { - if (cspECDH == null) { - // We know this primitive is supported by P71D600 - cspECDH = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); - } + } + + private static void terminate() { + cspECDH = null; + cspSHA256 = null; + cspSHA384 = null; + ecParamsP256 = null; + ecParamsP384 = null; + requestObjectDeletion(); + } + private static ECKey getEcParamsP256() { if (ecParamsP256 == null) { ecParamsP256 = (ECKey) KeyBuilder.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PARAMETERS, JCSystem.MEMORY_TYPE_PERSISTENT, KeyBuilder.LENGTH_EC_FP_256, false); @@ -211,6 +225,10 @@ private static void init() { ecParamsP256.setFieldFP(PIVKeyECC.ECParamsP256.P, (short) 0, (short) PIVKeyECC.ECParamsP256.P.length); ecParamsP256.setK(PIVKeyECC.ECParamsP256.H); } + return ecParamsP256; + } + + private static ECKey getEcParamsP384() { if (ecParamsP384 == null) { ecParamsP384 = (ECKey) KeyBuilder.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PARAMETERS, JCSystem.MEMORY_TYPE_PERSISTENT, KeyBuilder.LENGTH_EC_FP_384, false); @@ -221,22 +239,69 @@ private static void init() { ecParamsP384.setFieldFP(PIVKeyECC.ECParamsP384.P, (short) 0, (short) PIVKeyECC.ECParamsP384.P.length); ecParamsP384.setK(PIVKeyECC.ECParamsP384.H); } + return ecParamsP384; + } - if (cspSHA256 == null) { - cspSHA256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); + // Returns the indicator bit for a mechanism (zero if untracked). The PIV-SM cipher suites share + // the bit of the underlying curve. + private static short mechanismBit(byte mechanism) { + switch (mechanism) { + case Constants.ID_ALG_DEFAULT: + case Constants.ID_ALG_TDEA_3KEY: + return (short) 0x0001; + case Constants.ID_ALG_AES_128: + return (short) 0x0002; + case Constants.ID_ALG_AES_192: + return (short) 0x0004; + case Constants.ID_ALG_AES_256: + return (short) 0x0008; + case Constants.ID_ALG_RSA_1024: + return (short) 0x0010; + case Constants.ID_ALG_RSA_2048: + return (short) 0x0020; + case Constants.ID_ALG_RSA_3072: + return (short) 0x0040; + case Constants.ID_ALG_RSA_4096: + return (short) 0x0080; + case Constants.ID_ALG_ECC_P256: + case Constants.ID_ALG_ECC_CS2: + return (short) 0x0100; + case Constants.ID_ALG_ECC_P384: + case Constants.ID_ALG_ECC_CS7: + return (short) 0x0200; + default: + return (short) 0x0000; } - if (cspSHA384 == null) { - cspSHA384 = MessageDigest.getInstance(MessageDigest.ALG_SHA_384, false); + } + + private static void setUnsupported(byte mechanism) { + mechanismSupport &= (short) ~mechanismBit(mechanism); + } + + private static void setGenerateUnsupported(byte mechanism) { + generateSupport &= (short) ~mechanismBit(mechanism); + } + + static boolean supportsGenerate(byte mechanism) { + return supportsMechanism(mechanism) && (generateSupport & mechanismBit(mechanism)) != (short) 0; + } + + // Ensures mechanism is flagged unsupported when encountering a NO_SUCH_ALGORITHM CryptoException. + static void onCryptoException(byte mechanism, CryptoException ex) { + if (ex.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + setUnsupported(mechanism); + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } + throw ex; } - private static void terminate() { - cspECDH = null; - cspSHA256 = null; - cspSHA384 = null; - ecParamsP256 = null; - ecParamsP384 = null; - requestObjectDeletion(); + // As per onCryptoException, but flagging keygen support. + static void onGenerateException(byte mechanism, CryptoException ex) { + if (ex.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + setGenerateUnsupported(mechanism); + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } + throw ex; } static boolean supportsMechanism(byte mechanism) { @@ -247,7 +312,7 @@ static boolean supportsMechanism(byte mechanism) { case Constants.ID_ALG_DEFAULT: case Constants.ID_ALG_TDEA_3KEY: case Constants.ID_ALG_RSA_1024: - return !Config.FIPS_APPROVED_MODE; + return !Config.FIPS_APPROVED_MODE && (mechanismSupport & mechanismBit(mechanism)) != (short) 0; // Supported Algorithms case Constants.ID_ALG_AES_128: @@ -260,7 +325,7 @@ static boolean supportsMechanism(byte mechanism) { case Constants.ID_ALG_ECC_P384: case Constants.ID_ALG_ECC_CS2: case Constants.ID_ALG_ECC_CS7: - return true; + return (mechanismSupport & mechanismBit(mechanism)) != (short) 0; default: return false; @@ -270,10 +335,16 @@ static boolean supportsMechanism(byte mechanism) { static MessageDigest getMessageDigest(byte algorithm) { switch (algorithm) { case MessageDigest.ALG_SHA_256: + if (cspSHA256 == null) { + cspSHA256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false); + } cspSHA256.reset(); return cspSHA256; case MessageDigest.ALG_SHA_384: - cspSHA256.reset(); + if (cspSHA384 == null) { + cspSHA384 = MessageDigest.getInstance(MessageDigest.ALG_SHA_384, false); + } + cspSHA384.reset(); return cspSHA384; default: ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); @@ -329,10 +400,10 @@ static Key buildKey(byte algorithm, short length) { case KeyBuilder.TYPE_EC_FP_PRIVATE: case KeyBuilder.TYPE_EC_FP_PUBLIC: if (length == KeyBuilder.LENGTH_EC_FP_256) { - return KeyBuilder.buildKeyWithSharedDomain(algorithm, JCSystem.MEMORY_TYPE_PERSISTENT, (Key) ecParamsP256, + return KeyBuilder.buildKeyWithSharedDomain(algorithm, JCSystem.MEMORY_TYPE_PERSISTENT, (Key) getEcParamsP256(), false); } else if (length == KeyBuilder.LENGTH_EC_FP_384) { - return KeyBuilder.buildKeyWithSharedDomain(algorithm, JCSystem.MEMORY_TYPE_PERSISTENT, (Key) ecParamsP384, + return KeyBuilder.buildKeyWithSharedDomain(algorithm, JCSystem.MEMORY_TYPE_PERSISTENT, (Key) getEcParamsP384(), false); } else { ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); @@ -544,7 +615,9 @@ static short computeECDH(ECPrivateKey privateKey, byte[] inBuffer, short inOffse // NOTE: The Java Card implementation of generateSecret() performs sufficient buffer state and // length checking that we don't double-up here. - + if (cspECDH == null) { + cspECDH = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); + } cspECDH.init(privateKey); return cspECDH.generateSecret(inBuffer, inOffset, inLength, outBuffer, outOffset); } diff --git a/src/org/openfips201/applet/Constants.java b/src/org/openfips201/applet/Constants.java index e40ebdb1..06724572 100644 --- a/src/org/openfips201/applet/Constants.java +++ b/src/org/openfips201/applet/Constants.java @@ -266,11 +266,20 @@ private Constants() { // Tag for updating a configuration operation. static final byte TAG_OP_UPDATE_CONFIG = (byte) 0x68; + // Tag for deleting a data object (container) operation. + static final byte TAG_OP_DELETE_OBJECT = (byte) 0x69; + + // Tag for deleting a verifier (PIN/PUK) operation. + static final byte TAG_OP_DELETE_PIN = (byte) 0x6A; + + // Tag for deleting a key operation. + static final byte TAG_OP_DELETE_KEY = (byte) 0x6B; + // Tag for securing the applet (non-constructed tag). static final byte TAG_OP_SECURE_APPLET = (byte) 0x5F; // Tag for bulk requests. - static final byte TAG_OP_BULK_REQUEST = (byte) 0x6A; + static final byte TAG_OP_BULK_REQUEST = (byte) 0x7E; //////////////////////////////////////////////////////////////////////////////// // Custom Error Constants diff --git a/src/org/openfips201/applet/PIV.java b/src/org/openfips201/applet/PIV.java index 0885d0b1..cd250ac6 100644 --- a/src/org/openfips201/applet/PIV.java +++ b/src/org/openfips201/applet/PIV.java @@ -264,7 +264,7 @@ void getData(PIVAPDU pApdu) throws ISOException { // Special - Handle the 2-byte BITG case case Constants.ID_DATA_BITG: buffer[0] = Constants.ID_DATA_BITG_MSB; - buffer[1] = Constants.ID_DATA_BITG_MSB; + buffer[1] = Constants.ID_DATA_BITG_LSB; buffer[2] = 0; length = 3; break; @@ -2197,6 +2197,105 @@ private void processCreateObjectRequest(byte operation, TLVReader reader) { } } + /** + * Deletes a data object (container) identified by its 1-3 byte identifier. + * + * @throws ISOException SW_REFERENCE_NOT_FOUND if no container matches the identifier + */ + private void processDeleteObjectRequest(TLVReader reader) { + // PRE-CONDITION 1 - The 'ID' tag MUST be present + if (!reader.match(Constants.TAG_OBJECT_ID)) { + ISOException.throwIt(Constants.SW_PUT_DATA_ID_MISSING); + return; + } + + // PRE-CONDITION 2 - The 'ID' tag MUST have length between 1 and 3 + short tagLength = reader.getLength(); + if (tagLength < Constants.OBJECT_ID_MIN_LENGTH || tagLength > Constants.OBJECT_ID_MAX_LENGTH) { + ISOException.throwIt(Constants.SW_PUT_DATA_ID_INVALID_LENGTH); + return; + } + + int id = PIVContainer.parseId(reader.getData(), reader.getDataOffset(), tagLength); + reader.moveNext(); + + // PRE-CONDITION 3 - The referenced object MUST exist + if (!dataStore.removeContainer(id)) { + ISOException.throwIt(Constants.SW_REFERENCE_NOT_FOUND); + } + } + + /** + * Deletes a verifier (PIN/PUK) identified by its single-byte reference. + * + * @throws ISOException SW_REFERENCE_NOT_FOUND if no verifier matches the identifier + */ + private void processDeletePinRequest(TLVReader reader) { + // PRE-CONDITION 1 - The 'ID' tag MUST be present + if (!reader.match(Constants.TAG_OBJECT_ID)) { + ISOException.throwIt(Constants.SW_PUT_DATA_ID_MISSING); + return; + } + + // PRE-CONDITION 2 - The 'ID' tag MUST be length 1 + if (reader.getLength() != (short) 1) { + ISOException.throwIt(Constants.SW_PUT_DATA_ID_INVALID_LENGTH); + return; + } + + byte id = reader.toByte(); + reader.moveNext(); + + // PRE-CONDITION 3 - The referenced verifier MUST exist + if (!dataStore.removeVerifier(id)) { + ISOException.throwIt(Constants.SW_REFERENCE_NOT_FOUND); + } + } + + /** + * Deletes one or more keys identified by a single-byte reference. The 'KEY MECHANISM' tag is + * optional: when supplied, only the key matching both the id and mechanism is removed; when + * omitted, every key sharing the id is removed (a reference may hold multiple mechanisms). + * + * @throws ISOException SW_REFERENCE_NOT_FOUND if no matching key exists + */ + private void processDeleteKeyRequest(TLVReader reader) { + // PRE-CONDITION 1 - The 'ID' tag MUST be present + if (!reader.match(Constants.TAG_OBJECT_ID)) { + ISOException.throwIt(Constants.SW_PUT_DATA_ID_MISSING); + return; + } + + // PRE-CONDITION 2 - The 'ID' tag MUST be length 1 + if (reader.getLength() != (short) 1) { + ISOException.throwIt(Constants.SW_PUT_DATA_ID_INVALID_LENGTH); + return; + } + + byte id = reader.toByte(); + reader.moveNext(); + + boolean found; + if (reader.match(Constants.TAG_KEY_MECHANISM)) { + // PRE-CONDITION 3 - If present, the 'KEY MECHANISM' tag MUST be length 1 + if (reader.getLength() != (short) 1) { + ISOException.throwIt(Constants.SW_PUT_DATA_KEY_MECHANISM_INVALID); + return; + } + + byte mechanism = reader.toByte(); + reader.moveNext(); + found = dataStore.removeKey(id, mechanism); + } else { + found = dataStore.removeKeysById(id); + } + + // PRE-CONDITION 4 - At least one matching key MUST have existed + if (!found) { + ISOException.throwIt(Constants.SW_REFERENCE_NOT_FOUND); + } + } + /** * This is the administrative equivalent for the PUT DATA card and is intended for use by Card * Management Systems to generate the on-card file-system. @@ -2269,6 +2368,21 @@ void putDataAdmin(PIVAPDU pApdu) throws ISOException { processCreateObjectRequest(operation, reader); break; + // Delete a data object (container) + case Constants.TAG_OP_DELETE_OBJECT: + processDeleteObjectRequest(reader); + break; + + // Delete a verifier + case Constants.TAG_OP_DELETE_PIN: + processDeletePinRequest(reader); + break; + + // Delete a key + case Constants.TAG_OP_DELETE_KEY: + processDeleteKeyRequest(reader); + break; + // Update one or more configuration parameters case Constants.TAG_OP_UPDATE_CONFIG: try { diff --git a/src/org/openfips201/applet/PIVAPDU.java b/src/org/openfips201/applet/PIVAPDU.java index d48aeed8..66dbca81 100644 --- a/src/org/openfips201/applet/PIVAPDU.java +++ b/src/org/openfips201/applet/PIVAPDU.java @@ -773,7 +773,12 @@ void processOutgoing(APDU apdu) throws ISOException { apdu.setOutgoingLength(outLength); apdu.sendBytes(Constants.ZERO_SHORT, outLength); } else { - // Same as plaintext + short remaining = (short)(context[CONTEXT_REMAINING] - inLength); + if (remaining > 0) { + status = ISO7816.SW_BYTES_REMAINING_00; + status |= (remaining > (short)0xff) ? (short)0xff : remaining; + } + apdu.setOutgoingLength(outLength); if (outLength > 0) { apdu.sendBytesLong(data, context[CONTEXT_OFFSET], outLength); diff --git a/src/org/openfips201/applet/PIVDataStore.java b/src/org/openfips201/applet/PIVDataStore.java index 759e0712..84db67c7 100644 --- a/src/org/openfips201/applet/PIVDataStore.java +++ b/src/org/openfips201/applet/PIVDataStore.java @@ -65,6 +65,33 @@ void addContainer(PIVContainer container) { } } + /** + * Unlinks and erases the container matching {@code id}. Unlinking is performed before erasure so + * that a tear can never leave a cleared object reachable from the store. + * + * @return true if a container was found and removed + */ + boolean removeContainer(int id) { + PIVObject prev = null; + PIVObject current = firstContainer; + while (current != null) { + if (current.id == id) { + if (prev == null) { + firstContainer = (PIVContainer) current.nextObject; + } else { + prev.nextObject = current.nextObject; + } + current.clear(); + current.nextObject = null; + Platform.requestObjectDeletion(); + return true; + } + prev = current; + current = current.nextObject; + } + return false; + } + PIVKey getKey(byte id) { if (firstKey == null) { return null; @@ -102,7 +129,68 @@ void addKey(PIVKey key) { } else { firstKey.last().nextObject = key; } - } + } + + /** + * Unlinks and erases the single key matching both {@code id} and {@code mechanism}. + * + * @return true if a key was found and removed + */ + boolean removeKey(byte id, byte mechanism) { + int target = id & 0xFF; + PIVObject prev = null; + PIVObject current = firstKey; + while (current != null) { + if (current.id == target && ((PIVKey) current).getMechanism() == mechanism) { + if (prev == null) { + firstKey = (PIVKey) current.nextObject; + } else { + prev.nextObject = current.nextObject; + } + current.clear(); + current.nextObject = null; + Platform.requestObjectDeletion(); + return true; + } + prev = current; + current = current.nextObject; + } + return false; + } + + /** + * Unlinks and erases every key matching {@code id}, regardless of mechanism. This caters for the + * case where a single reference holds multiple keys (e.g. a PKI and a symmetric key at 9E). + * + * @return true if at least one key was found and removed + */ + boolean removeKeysById(byte id) { + int target = id & 0xFF; + boolean removed = false; + PIVObject prev = null; + PIVObject current = firstKey; + while (current != null) { + if (current.id == target) { + PIVObject next = current.nextObject; + if (prev == null) { + firstKey = (PIVKey) next; + } else { + prev.nextObject = next; + } + current.clear(); + current.nextObject = null; + removed = true; + current = next; // prev is unchanged, the removed node is gone + } else { + prev = current; + current = current.nextObject; + } + } + if (removed) { + Platform.requestObjectDeletion(); + } + return removed; + } PIVVerifier getVerifier(byte id) { if (firstVerifier == null) { @@ -174,7 +262,35 @@ void addVerifier(PIVVerifier verifier) { firstVerifier = verifier; } else { firstVerifier.last().nextObject = verifier; - } + } + } + + /** + * Unlinks and erases the verifier matching {@code id}. Note that clear() only resets the OwnerPIN + * state (the underlying object cannot be zeroised), so the node itself is released for reclamation. + * + * @return true if a verifier was found and removed + */ + boolean removeVerifier(byte id) { + int target = id & 0xFF; + PIVObject prev = null; + PIVObject current = firstVerifier; + while (current != null) { + if (current.id == target) { + if (prev == null) { + firstVerifier = (PIVVerifier) current.nextObject; + } else { + prev.nextObject = current.nextObject; + } + current.clear(); + current.nextObject = null; + Platform.requestObjectDeletion(); + return true; + } + prev = current; + current = current.nextObject; + } + return false; } } diff --git a/src/org/openfips201/applet/PIVKeyECC.java b/src/org/openfips201/applet/PIVKeyECC.java index f47d2e6c..aa9b36f2 100644 --- a/src/org/openfips201/applet/PIVKeyECC.java +++ b/src/org/openfips201/applet/PIVKeyECC.java @@ -29,6 +29,7 @@ import javacard.security.ECPublicKey; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.CryptoException; /** Provides functionality for ECC PIV key objects */ class PIVKeyECC extends PIVKeyPKI { @@ -42,7 +43,8 @@ class PIVKeyECC extends PIVKeyPKI { private static final byte ELEMENT_ECC_SECRET = (byte) 0x87; // PERSISTENT - The key store - private KeyPair keyPair; + private ECPublicKey publicKey; + private ECPrivateKey privateKey; PIVKeyECC(int id, byte modeContact, byte modeContactless, byte adminKey, byte mechanism, byte role, byte attributes) throws ISOException { @@ -93,7 +95,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE return; // Keep static analyser happy } - ((ECPublicKey) keyPair.getPublic()).setW(buffer, offset, length); + publicKey.setW(buffer, offset, length); break; case ELEMENT_ECC_SECRET: @@ -102,7 +104,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE return; // Keep static analyser happy } - ((ECPrivateKey) keyPair.getPrivate()).setS(buffer, offset, length); + privateKey.setS(buffer, offset, length); break; default: @@ -116,38 +118,49 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE */ private void allocate() { - if (keyPair != null) { + if (publicKey != null && privateKey != null) { return; } - ECPrivateKey privateKey; - ECPublicKey publicKey; - - switch (getMechanism()) { - case Constants.ID_ALG_ECC_P256: - case Constants.ID_ALG_ECC_CS2: - privateKey = (ECPrivateKey)Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256); - publicKey = (ECPublicKey)Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256); - break; - - case Constants.ID_ALG_ECC_P384: - case Constants.ID_ALG_ECC_CS7: - privateKey = (ECPrivateKey)Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_384); - publicKey = (ECPublicKey)Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_384); - break; - - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return; // Keep compiler happy + if (!Platform.Cryptography.supportsMechanism(getMechanism())) { + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } - keyPair = new KeyPair(publicKey, privateKey); + try { + switch (getMechanism()) { + case Constants.ID_ALG_ECC_P256: + case Constants.ID_ALG_ECC_CS2: + privateKey = (ECPrivateKey) Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PRIVATE, + KeyBuilder.LENGTH_EC_FP_256); + publicKey = (ECPublicKey) Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PUBLIC, + KeyBuilder.LENGTH_EC_FP_256); + break; + + case Constants.ID_ALG_ECC_P384: + case Constants.ID_ALG_ECC_CS7: + privateKey = (ECPrivateKey) Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PRIVATE, + KeyBuilder.LENGTH_EC_FP_384); + publicKey = (ECPublicKey) Platform.Cryptography.buildKey(KeyBuilder.ALG_TYPE_EC_FP_PUBLIC, + KeyBuilder.LENGTH_EC_FP_384); + break; + + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return; // Keep compiler happy + } + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); + } } @Override short sign(byte[] inBuffer, short inOffset, short inLength, byte[] outBuffer, short outOffset) { - return Platform.Cryptography.sign((ECPrivateKey) keyPair.getPrivate(), inBuffer, inOffset, inLength, outBuffer, - outOffset); + try { + return Platform.Cryptography.sign(privateKey, inBuffer, inOffset, inLength, outBuffer, outOffset); + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); + return (short) 0; // Keep compiler happy + } } @Override @@ -155,17 +168,32 @@ short keyEstablish(byte[] inBuffer, short inOffset, short inLength, byte[] outBu // PRE-CONDITION 1 - The input buffer must equal the expected public key point value // NOTE: This is checked by the underlying crypto implementation now - - return Platform.Cryptography.computeECDH((ECPrivateKey) keyPair.getPrivate(), inBuffer, inOffset, inLength, - outBuffer, outOffset); + try { + return Platform.Cryptography.computeECDH(privateKey, inBuffer, inOffset, inLength, outBuffer, outOffset); + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); + return (short) 0; // Keep compiler happy + } } @Override short generate(byte[] outBuffer, short outOffset) throws CardRuntimeException { + if (!Platform.Cryptography.supportsGenerate(getMechanism())) { + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } + short length = 0; clear(); allocate(); - keyPair.genKeyPair(); + + try { + KeyPair keyPair = new KeyPair(publicKey, privateKey); + keyPair.genKeyPair(); + } catch (CryptoException ex) { + clear(); + Platform.Cryptography.onGenerateException(getMechanism(), ex); + return (short) 0; // Keep compiler happy + } TLVWriter writer = TLVWriter.getInstance(); @@ -174,7 +202,7 @@ short generate(byte[] outBuffer, short outOffset) throws CardRuntimeException { writer.writeTagByte(ELEMENT_ECC_POINT); writer.writeLength(getPublicPointLength()); outOffset = writer.getOffset(); - outOffset += ((ECPublicKey) keyPair.getPublic()).getW(outBuffer, outOffset); + outOffset += publicKey.getW(outBuffer, outOffset); writer.setOffset(outOffset); length = writer.finish(); @@ -236,17 +264,24 @@ final short getPublicPointLength() throws ISOException { */ @Override boolean isInitialised() { - return (keyPair != null && keyPair.getPrivate().isInitialized() && keyPair.getPublic().isInitialized()); + return (privateKey != null && privateKey.isInitialized() && publicKey != null && publicKey.isInitialized()); } @Override void clear() { - if (keyPair == null) + if (privateKey == null && publicKey == null) { return; + } - keyPair.getPrivate().clearKey(); - keyPair.getPublic().clearKey(); - keyPair = null; + if (privateKey != null) { + privateKey.clearKey(); + privateKey = null; + } + + if (publicKey != null) { + publicKey.clearKey(); + publicKey = null; + } Platform.requestObjectDeletion(); } diff --git a/src/org/openfips201/applet/PIVKeyRSA.java b/src/org/openfips201/applet/PIVKeyRSA.java index c721f7c5..4592fc5a 100644 --- a/src/org/openfips201/applet/PIVKeyRSA.java +++ b/src/org/openfips201/applet/PIVKeyRSA.java @@ -27,9 +27,11 @@ import javacard.framework.ISOException; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.PrivateKey; import javacard.security.RSAPrivateCrtKey; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; +import javacard.security.CryptoException; final class PIVKeyRSA extends PIVKeyPKI { @@ -64,7 +66,8 @@ final class PIVKeyRSA extends PIVKeyPKI { private static final short CONST_LENGTH_EXPONENT = (short) 3; // RSA - The public exponent length // PERSISTENT - The key store - private KeyPair keyPair; + private RSAPublicKey publicKey; + private PrivateKey privateKey; PIVKeyRSA(int id, byte modeContact, byte modeContactless, byte adminKey, byte mechanism, byte role, byte attributes) { @@ -108,11 +111,9 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE allocate(); } - RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - - if (keyPair.getPrivate() instanceof RSAPrivateCrtKey) { + if (privateKey instanceof RSAPrivateCrtKey) { // RSA-CRT - RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate(); + RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey) privateKey; switch (element) { @@ -137,7 +138,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE if (length != (short) (getKeyLengthBytes() / 2)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - privateKey.setP(buffer, offset, length); + crtKey.setP(buffer, offset, length); break; // RSA Prime Exponent Q @@ -145,7 +146,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE if (length != (short) (getKeyLengthBytes() / 2)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - privateKey.setQ(buffer, offset, length); + crtKey.setQ(buffer, offset, length); break; // RSA D mod P - 1 @@ -153,7 +154,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE if (length != (short) (getKeyLengthBytes() / 2)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - privateKey.setDP1(buffer, offset, length); + crtKey.setDP1(buffer, offset, length); break; // RSA D mod Q - 1 @@ -161,7 +162,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE if (length != (short) (getKeyLengthBytes() / 2)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - privateKey.setDQ1(buffer, offset, length); + crtKey.setDQ1(buffer, offset, length); break; // RSA Inverse Q @@ -169,7 +170,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE if (length != (short) (getKeyLengthBytes() / 2)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - privateKey.setPQ(buffer, offset, length); + crtKey.setPQ(buffer, offset, length); break; default: @@ -179,7 +180,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE } } else { // RSA - RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + RSAPrivateKey rsaKey = (RSAPrivateKey) privateKey; switch (element) { @@ -190,7 +191,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE } // NOTE: We don't worry about transactions here since if this is torn between // writes, the caller can send it again - privateKey.setModulus(buffer, offset, length); + rsaKey.setModulus(buffer, offset, length); publicKey.setModulus(buffer, offset, length); break; @@ -207,7 +208,7 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE if (length != getKeyLengthBytes()) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - privateKey.setExponent(buffer, offset, length); + rsaKey.setExponent(buffer, offset, length); break; default: @@ -223,16 +224,23 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE * Allocates memory for the private and public key parts */ private void allocate() { - if (keyPair == null) { - // Generate the key based on our configuration - RSAPublicKey publicKey = (RSAPublicKey) Platform.Cryptography.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, getKeyLengthBits()); + if (publicKey != null && privateKey != null) { + return; + } + + if (!Platform.Cryptography.supportsMechanism(getMechanism())) { + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } + + try { + publicKey = (RSAPublicKey) Platform.Cryptography.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, getKeyLengthBits()); if (hasAttribute(ATTR_RSA_CRT)) { - RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) Platform.Cryptography.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, getKeyLengthBits()); - keyPair = new KeyPair(publicKey, privateKey); + privateKey = (PrivateKey) Platform.Cryptography.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, getKeyLengthBits()); } else { - RSAPrivateKey privateKey = (RSAPrivateKey) Platform.Cryptography.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, getKeyLengthBits()); - keyPair = new KeyPair(publicKey, privateKey); + privateKey = (PrivateKey) Platform.Cryptography.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, getKeyLengthBits()); } + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); } } @@ -241,19 +249,24 @@ private void allocate() { */ @Override boolean isInitialised() { - return (keyPair != null && keyPair.getPrivate().isInitialized() - && keyPair.getPublic().isInitialized()); + return (privateKey != null && privateKey.isInitialized() && publicKey != null && publicKey.isInitialized()); } @Override void clear() { - if (keyPair == null) { + if (privateKey == null && publicKey == null) { return; } - keyPair.getPrivate().clearKey(); - keyPair.getPublic().clearKey(); - keyPair = null; + if (privateKey != null) { + privateKey.clearKey(); + privateKey = null; + } + + if (publicKey != null) { + publicKey.clearKey(); + publicKey = null; + } Platform.requestObjectDeletion(); } @@ -266,8 +279,12 @@ short sign(byte[] inBuffer, short inOffset, short inLength, byte[] outBuffer, sh } // EXECUTION - return Platform.Cryptography.computeRSADP1(keyPair.getPrivate(), inBuffer, inOffset, inLength, - outBuffer, outOffset); + try { + return Platform.Cryptography.computeRSADP1(privateKey, inBuffer, inOffset, inLength, outBuffer, outOffset); + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); + return (short) 0; // Keep compiler happy + } } @Override @@ -281,11 +298,22 @@ short keyEstablish(byte[] inBuffer, short inOffset, short inLength, byte[] outBu @Override short generate(byte[] outBuffer, short outOffset) throws CardRuntimeException { + if (!Platform.Cryptography.supportsGenerate(getMechanism())) { + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } + try { // Clear and allocate the key objects clear(); allocate(); - keyPair.genKeyPair(); + + try { + KeyPair keyPair = new KeyPair(publicKey, privateKey); + keyPair.genKeyPair(); + } catch (CryptoException ex) { + Platform.Cryptography.onGenerateException(getMechanism(), ex); + return (short) 0; // Keep compiler happy + } TLVWriter writer = TLVWriter.getInstance(); @@ -301,8 +329,6 @@ short generate(byte[] outBuffer, short outOffset) throws CardRuntimeException { writer.init(outBuffer, outOffset, TLV.LENGTH_3BYTE_MAX, CONST_TAG_RESPONSE); } - RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); - // Modulus writer.writeTagByte(CONST_TAG_MODULUS); writer.writeLength(getKeyLengthBytes()); @@ -325,6 +351,10 @@ short generate(byte[] outBuffer, short outOffset) throws CardRuntimeException { // Done, return the response length return writer.finish(); + } catch (ISOException ex) { + // Preserve an already-mapped status (e.g. unsupported); clear and rethrow before the re-wrap below. + clear(); + throw ex; } catch (CardRuntimeException ex) { // At this point we are in a nondeterministic state so we will // clear both the public and private keys if they exist diff --git a/src/org/openfips201/applet/PIVKeySYM.java b/src/org/openfips201/applet/PIVKeySYM.java index 05ed62e9..1a661bb7 100644 --- a/src/org/openfips201/applet/PIVKeySYM.java +++ b/src/org/openfips201/applet/PIVKeySYM.java @@ -28,6 +28,7 @@ import javacard.security.DESKey; import javacard.security.KeyBuilder; import javacard.security.SecretKey; +import javacard.security.CryptoException; /** Provides functionality for symmetric PIV key objects */ final class PIVKeySYM extends PIVKey { @@ -118,6 +119,9 @@ void update(byte element, byte[] buffer, short offset, short length) throws ISOE } private void allocate() throws ISOException { + if (!Platform.Cryptography.supportsMechanism(getMechanism())) { + ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); + } clear(); byte keyType; @@ -149,7 +153,11 @@ private void allocate() throws ISOException { return; // Keep compiler happy } - key = (SecretKey) Platform.Cryptography.buildKey(keyType, keyLen); + try { + key = (SecretKey) Platform.Cryptography.buildKey(keyType, keyLen); + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); + } } @Override @@ -214,6 +222,11 @@ short encipher(byte[] inBuffer, short inOffset, short inLength, byte[] outBuffer ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - return Platform.Cryptography.encipher(key, inBuffer, inOffset, inLength, outBuffer, outOffset); + try { + return Platform.Cryptography.encipher(key, inBuffer, inOffset, inLength, outBuffer, outOffset); + } catch (CryptoException ex) { + Platform.Cryptography.onCryptoException(getMechanism(), ex); + return (short) 0; // Keep compiler happy + } } }