diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 49929346..f64858f5 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -416,20 +416,23 @@ private Object getInstanceFromPool(Object[] pool, byte alg) { return object; } + private void releaseInstance(Object[] pool, short index) { + if (((KMInstance) pool[index]).reserved != 0) { + JCSystem.beginTransaction(); + ((KMInstance) pool[index]).reserved = 0; + JCSystem.commitTransaction(); + } + } + private void releaseInstance(Object[] pool, Object object) { short index = 0; short len = (short) pool.length; while (index < len) { if (pool[index] != null) { if (object == ((KMInstance) pool[index]).object) { - JCSystem.beginTransaction(); - ((KMInstance) pool[index]).reserved = 0; - JCSystem.commitTransaction(); + releaseInstance(pool, index); break; } - } else { - // Reached end. - break; } index++; } @@ -1275,4 +1278,22 @@ public KMAttestationKey getAttestationKey() { public KMPreSharedKey getPresharedKey() { return (KMPreSharedKey) preSharedKey; } + + private void releasePool(Object[] pool) { + short index = 0; + short len = (short) pool.length; + while (index < len) { + if (pool[index] != null) { + releaseInstance(pool, index); + } + index++; + } + } + + @Override + public void releaseAllOperations() { + releasePool(cipherPool); + releasePool(sigPool); + releasePool(operationPool); + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 32741eed..aa133bda 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -23,8 +23,6 @@ public class KMOperationImpl implements KMOperation { - private Cipher cipher; - private Signature signature; private static final short CIPHER_ALG_OFFSET = 0x00; private static final short PADDING_OFFSET = 0x01; private static final short OPER_MODE_OFFSET = 0x02; @@ -34,9 +32,12 @@ public class KMOperationImpl implements KMOperation { //Java Card after the GCM update operation. private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; private short[] parameters; + // Either one of Cipher/Signature instance is stored. + private Object[] operationInst; public KMOperationImpl() { parameters = JCSystem.makeTransientShortArray((short) 6, JCSystem.CLEAR_ON_RESET); + operationInst = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); } public short getMode() { @@ -80,19 +81,15 @@ public void setCipherAlgorithm(short cipherAlg) { } public void setCipher(Cipher cipher) { - JCSystem.beginTransaction(); - this.cipher = cipher; - JCSystem.commitTransaction(); + operationInst[0] = cipher; } public void setSignature(Signature signer) { - JCSystem.beginTransaction(); - this.signature = signer; - JCSystem.commitTransaction(); + operationInst[0] = signer; } private void resetCipher() { - setCipher(null); + operationInst[0] = null; parameters[MAC_LENGTH_OFFSET] = 0; parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; parameters[BLOCK_MODE_OFFSET] = 0; @@ -104,7 +101,7 @@ private void resetCipher() { @Override public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - short len = cipher.update(inputDataBuf, inputDataStart, inputDataLength, + short len = ((Cipher) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); if (parameters[CIPHER_ALG_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { // Every time Block size data is stored as intermediate result. @@ -116,7 +113,7 @@ public short update(byte[] inputDataBuf, short inputDataStart, @Override public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) { - signature.update(inputDataBuf, inputDataStart, inputDataLength); + ((Signature) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength); return 0; } @@ -124,6 +121,7 @@ public short update(byte[] inputDataBuf, short inputDataStart, public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLen, byte[] outputDataBuf, short outputDataStart) { byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + Cipher cipher = (Cipher) operationInst[0]; short cipherAlg = parameters[CIPHER_ALG_OFFSET]; short blockMode = parameters[BLOCK_MODE_OFFSET]; short mode = parameters[OPER_MODE_OFFSET]; @@ -209,11 +207,11 @@ public short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signBuf, short signStart) { short len = 0; try { - len = signature.sign(inputDataBuf, inputDataStart, inputDataLength, + len = ((Signature) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart); } finally { - KMAndroidSEProvider.getInstance().releaseSignatureInstance(signature); - setSignature(null); + KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); + operationInst[0] = null; } return len; } @@ -223,31 +221,33 @@ public boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signBuf, short signStart, short signLength) { boolean ret = false; try { - ret = signature.verify(inputDataBuf, inputDataStart, inputDataLength, + ret = ((Signature) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength); } finally { - KMAndroidSEProvider.getInstance().releaseSignatureInstance(signature); - setSignature(null); + KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); + operationInst[0] = null; } return ret; } @Override public void abort() { - if (cipher != null) { - KMAndroidSEProvider.getInstance().releaseCipherInstance(cipher); - resetCipher(); - } - if (signature != null) { - KMAndroidSEProvider.getInstance().releaseSignatureInstance(signature); - setSignature(null); + if (operationInst[0] != null) { + if (parameters[OPER_MODE_OFFSET] == KMType.ENCRYPT || + parameters[OPER_MODE_OFFSET] == KMType.DECRYPT) { + KMAndroidSEProvider.getInstance().releaseCipherInstance((Cipher) operationInst[0]); + resetCipher(); + } else { + KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); + } + operationInst[0] = null; } KMAndroidSEProvider.getInstance().releaseOperationInstance(this); } @Override public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { - ((AEADCipher) cipher).updateAAD(dataBuf, dataStart, dataLength); + ((AEADCipher) operationInst[0]).updateAAD(dataBuf, dataStart, dataLength); } @Override @@ -258,4 +258,4 @@ public short getAESGCMOutputSize(short dataSize, short macLength) { return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); } } -} +} \ No newline at end of file diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index c6948e63..46bd03aa 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -1315,4 +1315,9 @@ public KMAttestationKey getAttestationKey() { public KMPreSharedKey getPresharedKey() { return (KMPreSharedKey) preSharedKey; } + + @Override + public void releaseAllOperations() { + //Do nothing. + } } diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index 517afb7f..1e80f4b2 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -451,6 +451,12 @@ public class KMFunctionalTest { private static final int OS_PATCH_LEVEL = 1; private static final int VENDOR_PATCH_LEVEL = 1; private static final int BOOT_PATCH_LEVEL = 1; + private static final short MAJOR_TYPE_MASK = 0xE0; + private static final byte CBOR_ARRAY_MAJOR_TYPE = (byte) 0x80; + private static final byte CBOR_UINT_MAJOR_TYPE = 0x00; + private static final short SE_POWER_RESET_FLAG = (short) 0x4000; + private static final boolean RESET = true; + private static final boolean NO_RESET = false; private CardSimulator simulator; private KMEncoder encoder; @@ -687,6 +693,13 @@ private void cleanUp() { simulator.deleteApplet(appletAID); } + private void resetAndSelect() { + simulator.reset(); + AID appletAID = AIDUtil.create("A000000062"); + // Select applet + simulator.selectApplet(appletAID); + } + private CommandAPDU encodeApdu(byte ins, short cmd) { byte[] buf = new byte[2500]; @@ -945,7 +958,7 @@ public void testDeviceLocked() { inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); short beginResp = begin(KMType.DECRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(inParams), (short) 0); + KMKeyParameters.instance(inParams), (short) 0, false); Assert.assertEquals(beginResp, KMError.DEVICE_LOCKED); short hwToken = KMHardwareAuthToken.instance(); KMHardwareAuthToken.cast(hwToken).setTimestamp(KMInteger.uint_16((byte) 2)); @@ -1958,14 +1971,22 @@ private short deleteKey(short keyBlob) { return respBuf[0]; } - private short abort(short opHandle) { + private short abort(short opHandle, boolean triggerReset) { short arrPtr = KMArray.instance((short) 1); KMArray.cast(arrPtr).add((short) 0, opHandle); CommandAPDU apdu = encodeApdu((byte) INS_ABORT_OPERATION_CMD, arrPtr); // print(commandAPDU.getBytes()); + if (triggerReset) { + resetAndSelect(); + } ResponseAPDU response = simulator.transmitCommand(apdu); byte[] respBuf = response.getBytes(); - return respBuf[0]; + short ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, (short) respBuf.length); + if (triggerReset) { + short error = KMInteger.cast(ret).getSignificantShort(); + Assert.assertEquals(error, SE_POWER_RESET_FLAG); + } + return ret; } public short getKeyCharacteristics(short keyBlob) { @@ -2398,7 +2419,7 @@ public boolean rsaVerifyMessage(byte[] input, short inputOff, short inputlen, by public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { short ret = begin(KMType.ENCRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(params), (short) 0); + KMKeyParameters.instance(params), (short) 0, false); // Get the operation handle. short opHandle = KMArray.cast(ret).get((short) 2); byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; @@ -2408,7 +2429,7 @@ public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { ret = finish(opHandle, KMByteBlob.instance(input, (short) 0, (short) input.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK); + (short) 0, (short) 0, (short) 0, KMError.OK, false); short dataPtr = KMArray.cast(ret).get((short) 2); byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; if (KMByteBlob.cast(dataPtr).length() > 0) { @@ -2422,7 +2443,7 @@ public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { public byte[] DecryptMessage(byte[] input, short params, byte[] keyBlob) { short ret = begin(KMType.DECRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(params), (short) 0); + KMKeyParameters.instance(params), (short) 0, false); // Get the operation handle. short opHandle = KMArray.cast(ret).get((short) 2); byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; @@ -2432,7 +2453,7 @@ public byte[] DecryptMessage(byte[] input, short params, byte[] keyBlob) { ret = finish(opHandle, KMByteBlob.instance(input, (short) 0, (short) input.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK); + (short) 0, (short) 0, (short) 0, KMError.OK, false); short dataPtr = KMArray.cast(ret).get((short) 2); byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; if (KMByteBlob.cast(dataPtr).length() > 0) { @@ -2462,7 +2483,7 @@ public void testUnsupportedBlockMode() { KMType.PKCS7, new byte[12]); short ret = begin(KMType.ENCRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(desPkcs7Params), (short) 0); + KMKeyParameters.instance(desPkcs7Params), (short) 0, false); Assert.assertTrue(ret == KMError.UNSUPPORTED_BLOCK_MODE); cleanUp(); } @@ -2494,7 +2515,7 @@ public void testDesEcbPkcs7PaddingCorrupted() { short ret = begin(KMType.DECRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(desPkcs7Params), (short) 0); + KMKeyParameters.instance(desPkcs7Params), (short) 0, false); // Get the operation handle. short opHandle = KMArray.cast(ret).get((short) 2); byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; @@ -2507,7 +2528,7 @@ public void testDesEcbPkcs7PaddingCorrupted() { (short) cipherText1.length); opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, - KMError.INVALID_ARGUMENT); + KMError.INVALID_ARGUMENT, false); cleanUp(); } @@ -2565,7 +2586,7 @@ public void testVtsRsaPkcs1Success() { // Do Begin operation. short ret = begin(KMType.DECRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(pkcs1Params), (short) 0); + KMKeyParameters.instance(pkcs1Params), (short) 0, false); // Get the operation handle. short opHandle = KMArray.cast(ret).get((short) 2); @@ -2578,7 +2599,7 @@ public void testVtsRsaPkcs1Success() { (short) cipherText1.length); // Finish should return UNKNOWN_ERROR. ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, - KMError.UNKNOWN_ERROR); + KMError.UNKNOWN_ERROR, false); } cleanUp(); } @@ -2737,9 +2758,9 @@ public void testUpgradeKey() { {0, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL+1, NO_UPGRADE, KMError.INVALID_ARGUMENT }, }; for (int i = 0; i < test_data.length; i++) { - setAndroidOSSystemProperties(simulator, (short) test_data[i][0], (short) test_data[i][1], - (short) test_data[i][2]); setBootParams(simulator, (short) test_data[i][3]); + setAndroidOSSystemProperties(simulator, (short) test_data[i][0], (short) test_data[i][1], + (short) test_data[i][2]); ret = upgradeKey( KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), null, null, test_data[i][5]); @@ -2780,6 +2801,125 @@ public void testUpgradeKey() { cleanUp(); } + public void testCardRest() { + byte[] input = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + // Test different combinations of reset events happening in the ordered flow of + // begin - begin1 - update - update1 - finish - finish1 - abort + boolean[][] resetEvents = { + //begin, begin1, update, update1, finish, finish1, abort + {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, + {RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, + {NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, + {NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, + {NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET}, + {NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET}, + {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET}, + {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET}, + {NO_RESET, NO_RESET, NO_RESET, RESET, RESET, NO_RESET, NO_RESET}, + {NO_RESET, RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, + {RESET, RESET, RESET, RESET, RESET, RESET, RESET}, + }; + for(int i = 0; i < resetEvents.length; i++) { + // Generate Key---------------- + short ret = generateHmacKey(null, null); + // Store the generated key in a new byte blob. + short keyBlobPtr = KMArray.cast(ret).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), keyBlob, + (short) 0, (short) keyBlob.length); + short inParams = getHmacParams(KMType.SHA2_256, true); + // Generate Key---------------- + + //Call begin operation---------------- + ret = begin(KMType.SIGN, keyBlobPtr, KMKeyParameters.instance(inParams), (short) 0, resetEvents[i][0]); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); + //Get the keyblobptr again. + keyBlobPtr = KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length); + //Call begin end---------------- + + //Call begin1 operation---------------- + inParams = getHmacParams(KMType.SHA2_256, true); + ret = begin(KMType.SIGN, keyBlobPtr, KMKeyParameters.instance(inParams), (short) 0, resetEvents[i][1]); + // Get the operation handle. + short opHandle1 = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf1 = new byte[KMRepository.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle1).getValue(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); + //Get the keyblobptr again. + keyBlobPtr = KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length); + //Call begin1 end---------------- + + //Call update operation---------------- + // Call update operation and check if the secure element power reset flag is set or not. + short dataPtr = KMByteBlob.instance(input, (short) 0, (short) input.length); + opHandle = KMInteger.instance(opHandleBuf, (short) 0, (short) opHandleBuf.length); + // update with trigger reset. + ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0, resetEvents[i][2]); + // If a reset event occurred then expect INVALID_OPERATION_HANDLE. + if (resetEvents[i][1] || resetEvents[i][2]) { + short err = KMInteger.cast(ret).getShort(); + Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); + } + //Call update end---------------- + + //Call update1 operation---------------- + // Call update1 operation and check if the secure element power reset flag is set or not. + dataPtr = KMByteBlob.instance(input, (short) 0, (short) input.length); + opHandle1 = KMInteger.instance(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); + // update with trigger reset. + ret = update(opHandle1, dataPtr, (short) 0, (short) 0, (short) 0, resetEvents[i][3]); + // If a reset event occurred then expect INVALID_OPERATION_HANDLE. + if (resetEvents[i][2] || resetEvents[i][3]) { + short err = KMInteger.cast(ret).getShort(); + Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); + } + //Call update end---------------- + + //Call finish operation---------------- + // Call finish operation and check if the secure element power reset flag is set or not. + dataPtr = KMByteBlob.instance((short) 0); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + short expectedErr = KMError.OK; + // If a reset event occurred then expect INVALID_OPERATION_HANDLE. + if (resetEvents[i][1] | resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4]) + expectedErr = KMError.INVALID_OPERATION_HANDLE; + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, expectedErr, resetEvents[i][4]); + //Call finish end---------------- + + //Call finish1 operation---------------- + // Call finish1 operation and check if the secure element power reset flag is set or not. + dataPtr = KMByteBlob.instance((short) 0); + opHandle1 = KMInteger.instance(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); + expectedErr = KMError.OK; + // If a reset event occurred then expect INVALID_OPERATION_HANDLE. + if (resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4] | resetEvents[i][5]) + expectedErr = KMError.INVALID_OPERATION_HANDLE; + ret = finish(opHandle1, dataPtr, null, (short) 0, (short) 0, (short) 0, expectedErr, resetEvents[i][5]); + //Call finish end---------------- + + //Call abort operation---------------- + // Call abort operation and check if the secure element power reset flag is set or not. + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + ret = abort(opHandle, resetEvents[i][6]); + if (resetEvents[i][1] || resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4] | resetEvents[i][5] | resetEvents[i][6]) { + short err = KMInteger.cast(ret).getShort(); + Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); + } + //Call finish end---------------- + KMRepository.instance().clean(); + } + } + + @Test + public void testCardResetFunctionality() { + init(); + testCardRest(); + cleanUp(); + } + @Test public void testDestroyAttIds() { init(); @@ -2859,15 +2999,17 @@ public void testAbortOperation() { byte[] plainData = "Hello World 123!".getBytes(); short ret = begin(KMType.ENCRYPT, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(inParams), (short) 0); + KMKeyParameters.instance(inParams), (short) 0, false); short opHandle = KMArray.cast(ret).get((short) 2); byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - abort(opHandle); + ret = abort(opHandle, false); + Assert.assertEquals(KMError.OK, KMInteger.cast(ret).getShort()); short dataPtr = KMByteBlob.instance(plainData, (short) 0, (short) plainData.length); opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0); + ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0, false); + ret = KMInteger.cast(ret).getShort(); Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, ret); cleanUp(); } @@ -3156,7 +3298,7 @@ public short processMessage( byte[] signature, boolean updateFlag, boolean aesGcmFlag) { - short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken); + short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken, false); short opHandle = KMArray.cast(beginResp).get((short) 2); byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); @@ -3184,7 +3326,7 @@ public short processMessage( inParams = KMKeyParameters.instance(inParams); } opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = update(opHandle, dataPtr, inParams, (short) 0, (short) 0); + ret = update(opHandle, dataPtr, inParams, (short) 0, (short) 0, false); dataPtr = KMArray.cast(ret).get((short) 3); if (KMByteBlob.cast(dataPtr).length() > 0) { Util.arrayCopyNonAtomic( @@ -3203,9 +3345,9 @@ public short processMessage( opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); if (keyPurpose == KMType.VERIFY) { - ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0, KMError.OK); + ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0, KMError.OK, false); } else { - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, KMError.OK); + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, KMError.OK, false); } if (len > 0) { dataPtr = KMArray.cast(ret).get((short) 2); @@ -3223,7 +3365,7 @@ public short processMessage( return ret; } - public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken) { + public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken, boolean triggerReset) { short arrPtr = KMArray.instance((short) 4); KMArray.cast(arrPtr).add((short) 0, KMEnum.instance(KMType.PURPOSE, keyPurpose)); KMArray.cast(arrPtr).add((short) 1, keyBlob); @@ -3233,6 +3375,9 @@ public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToke } KMArray.cast(arrPtr).add((short) 3, hwToken); CommandAPDU apdu = encodeApdu((byte) INS_BEGIN_OPERATION_CMD, arrPtr); + if (triggerReset) { + resetAndSelect(); + } //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); ResponseAPDU response = simulator.transmitCommand(apdu); short ret = KMArray.instance((short) 3); @@ -3242,19 +3387,31 @@ public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToke KMArray.cast(ret).add((short) 2, KMInteger.exp()); byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; - if (len > 5) { + byte majorType = readMajorType(respBuf); + //if (len > 5) { + if (majorType == CBOR_ARRAY_MAJOR_TYPE) { ret = decoder.decode(ret, respBuf, (short) 0, len); short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); Assert.assertEquals(error, KMError.OK); + if (triggerReset) { + error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); + Assert.assertEquals(error, SE_POWER_RESET_FLAG); + } return ret; - } else { - if (len == 3) { + } else {//Major type UINT. + ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, len); + if (triggerReset) { + short error = KMInteger.cast(ret).getSignificantShort(); + Assert.assertEquals(error, SE_POWER_RESET_FLAG); + } + return KMInteger.cast(ret).getShort(); + /*if (len == 3) { return respBuf[0]; } if (len == 4) { return respBuf[1]; } - return Util.getShort(respBuf, (short) 0); + return Util.getShort(respBuf, (short) 0);*/ } } @@ -3286,7 +3443,7 @@ public short translateExtendedErrorCodes(short err) { } public short finish(short operationHandle, short data, byte[] signature, short inParams, - short hwToken, short verToken, short expectedErr) { + short hwToken, short verToken, short expectedErr, boolean triggerReset) { if (hwToken == 0) { hwToken = KMHardwareAuthToken.instance(); } @@ -3312,6 +3469,9 @@ public short finish(short operationHandle, short data, byte[] signature, short i KMArray.cast(arrPtr).add((short) 5, verToken); CommandAPDU apdu = encodeApdu((byte) INS_FINISH_OPERATION_CMD, arrPtr); // print(commandAPDU.getBytes()); + if (triggerReset) { + resetAndSelect(); + } ResponseAPDU response = simulator.transmitCommand(apdu); byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; @@ -3329,16 +3489,24 @@ public short finish(short operationHandle, short data, byte[] signature, short i ret = decoder.decode(ret, respBuf, (short) 0, len); if (expectedErr == KMError.OK) { error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + if (triggerReset) { + short powerResetStatus = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); + Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); + } } else { error = KMInteger.cast(ret).getShort(); error = translateExtendedErrorCodes(error); + if (triggerReset) { + short powerResetStatus = KMInteger.cast(ret).getSignificantShort(); + Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); + } } Assert.assertEquals(error, expectedErr); return ret; } public short update(short operationHandle, short data, short inParams, short hwToken, - short verToken) { + short verToken, boolean triggerReset) { if (hwToken == 0) { hwToken = KMHardwareAuthToken.instance(); } @@ -3356,6 +3524,9 @@ public short update(short operationHandle, short data, short inParams, short hwT KMArray.cast(arrPtr).add((short) 3, hwToken); KMArray.cast(arrPtr).add((short) 4, verToken); CommandAPDU apdu = encodeApdu((byte) INS_UPDATE_OPERATION_CMD, arrPtr); + if (triggerReset) { + resetAndSelect(); + } // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); short ret = KMArray.instance((short) 4); @@ -3366,15 +3537,29 @@ public short update(short operationHandle, short data, short inParams, short hwT KMArray.cast(ret).add((short) 3, KMByteBlob.exp()); byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; - if (len > 5) { + byte majorType = readMajorType(respBuf); + if (majorType == CBOR_ARRAY_MAJOR_TYPE) { ret = decoder.decode(ret, respBuf, (short) 0, len); short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); Assert.assertEquals(error, KMError.OK); + if (triggerReset) { + error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); + Assert.assertEquals(error, SE_POWER_RESET_FLAG); + } } else { - ret = respBuf[1]; + ret = decoder.decode(KMInteger.exp(), respBuf, (short)0, len); + if (triggerReset) { + short powerResetStatus = KMInteger.cast(ret).getSignificantShort(); + Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); + } } return ret; } + + private byte readMajorType(byte[] resp) { + byte val = resp[0]; + return (byte) (val & MAJOR_TYPE_MASK); + } private void print(short blob) { print(KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index 47e6d305..14d8ef4c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -56,7 +56,7 @@ public KMEncoder() { bufferRef[0] = null; scratchBuf[START_OFFSET] = (short) 0; scratchBuf[LEN_OFFSET] = (short) 0; - scratchBuf[STACK_PTR_OFFSET] = (short) 0; + scratchBuf[STACK_PTR_OFFSET] = (short) 0; } private void push(short objPtr) { @@ -90,24 +90,27 @@ public short encode(short object, byte[] buffer, short startOff) { } // array{KMError.OK,Array{KMByteBlobs}} - public void encodeCertChain(byte[] buffer, short offset, short length) { + public void encodeCertChain(byte[] buffer, short offset, short length, short errInt32Ptr) { bufferRef[0] = buffer; scratchBuf[START_OFFSET] = offset; - scratchBuf[LEN_OFFSET] = (short) (offset + 3); + scratchBuf[LEN_OFFSET] = (short) (offset + 1); + //Total length is ArrayHeader + [UIntHeader + length(errInt32Ptr)] + scratchBuf[LEN_OFFSET] += (short) (1 + getEncodedIntegerLength(errInt32Ptr)); writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - writeByte(UINT_TYPE); // Error.OK + encodeInteger(errInt32Ptr); } //array{KMError.OK,Array{KMByteBlobs}} - public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) { + public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength, short errInt32Ptr) { bufferRef[0] = certBuffer; scratchBuf[START_OFFSET] = certStart; scratchBuf[LEN_OFFSET] = (short) (certStart + 1); //Array header - 2 elements i.e. 1 byte scratchBuf[START_OFFSET]--; - // Error.Ok - 1 byte - scratchBuf[START_OFFSET]--; + // errInt32Ptr - PowerResetStatus + ErrorCode - 4 bytes + // Integer header - 1 byte + scratchBuf[START_OFFSET] -= getEncodedIntegerLength(errInt32Ptr); //Array header - 2 elements i.e. 1 byte scratchBuf[START_OFFSET]--; // Cert Byte blob - typically 2 bytes length i.e. 3 bytes header @@ -115,31 +118,22 @@ public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, s if (certLength >= SHORT_PAYLOAD) { scratchBuf[START_OFFSET]--; } - bufferStart = scratchBuf[START_OFFSET]; if (scratchBuf[START_OFFSET] < bufferStart) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } + bufferStart = scratchBuf[START_OFFSET]; writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - writeByte(UINT_TYPE); // Error.OK + encodeInteger(errInt32Ptr); //PowerResetStatus + ErrorCode writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 element writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length return bufferStart; } - public short encodeError(short err, byte[] buffer, short startOff, short length) { + public short encodeError(short errInt32Ptr, byte[] buffer, short startOff, short length) { bufferRef[0] = buffer; scratchBuf[START_OFFSET] = startOff; - scratchBuf[LEN_OFFSET] = (short) (startOff + length); - // encode the err as UINT with value in err - should not be greater then 5 bytes. - if (err < UINT8_LENGTH) { - writeByte((byte) (UINT_TYPE | err)); - } else if (err < 0x100) { - writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); - writeByte((byte) err); - } else { - writeByte((byte) (UINT_TYPE | UINT16_LENGTH)); - writeShort(err); - } + scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); + encodeInteger(errInt32Ptr); return (short) (scratchBuf[START_OFFSET] - startOff); } @@ -289,6 +283,45 @@ private void encodeEnum(short obj) { writeByteValue(KMEnum.cast(obj).getVal()); } + /* The total length of UINT Major type along with actual length of + * integer is returned. + */ + public short getEncodedIntegerLength(short obj) { + byte[] val = KMInteger.cast(obj).getBuffer(); + short len = KMInteger.cast(obj).length(); + short startOff = KMInteger.cast(obj).getStartOff(); + byte index = 0; + // find out the most significant byte + while (index < len) { + if (val[(short) (startOff + index)] > 0) { + break; + } else if (val[(short) (startOff + index)] < 0) { + break; + } + index++; // index will be equal to len if value is 0. + } + // find the difference between most significant byte and len + short diff = (short) (len - index); + switch (diff) { + case 0: case 1: //Byte | Short + if ((val[(short) (startOff + index)] < UINT8_LENGTH) && + (val[(short) (startOff + index)] >= 0)) { + return (short) 1; + } else { + return (short) 2; + } + case 2: //Short + return (short) 3; + case 3: case 4: //Uint32 + return (short) 5; + case 5: case 6: case 7: case 8: //Uint64 + return (short) 9; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return 0; + } + private void encodeInteger(short obj) { byte[] val = KMInteger.cast(obj).getBuffer(); short len = KMInteger.cast(obj).length(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 2f3e187f..6566dd55 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -41,6 +41,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final short KM_HAL_VERSION = (short) 0x4000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; + private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -156,7 +157,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte OUTPUT_DATA = 25; public static final byte HW_TOKEN = 26; public static final byte VERIFICATION_TOKEN = 27; - protected static final byte SIGNATURE = 28; + public static final byte SIGNATURE = 28; // AddRngEntropy protected static final short MAX_SEED_SIZE = 2048; @@ -215,6 +216,7 @@ private void initializeTransientArrays() { bufferProp[BUF_START_OFFSET] = 0; bufferProp[BUF_LEN_OFFSET] = 0; } + /** * Selects this applet. * @@ -309,6 +311,11 @@ protected void validateApduHeader(APDU apdu) { @Override public void process(APDU apdu) { try { + // Handle the card reset status before processing apdu. + if (repository.isPowerResetEventOccurred()) { + // Release all the operation instances. + seProvider.releaseAllOperations(); + } repository.onProcess(); // Verify whether applet is in correct state. if ((keymasterState == KMKeymasterApplet.INIT_STATE) @@ -383,6 +390,7 @@ public void process(APDU apdu) { switch (apduIns) { case INS_SET_BOOT_PARAMS_CMD: if (seProvider.isBootSignalEventSupported() + && (keymasterState == KMKeymasterApplet.ACTIVE_STATE) && (!seProvider.isDeviceRebooted())) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } @@ -689,17 +697,20 @@ private void processSetVersionAndPatchLevels(APDU apdu) { private void processGetCertChainCmd(APDU apdu) { // Make the response tmpVariables[0] = seProvider.getCertificateChainLength(); - // Add arrayHeader and KMError.OK - tmpVariables[0] += 2; + short int32Ptr = buildErrorStatus(KMError.OK); + //Total Extra length + // Add arrayHeader and (PowerResetStatus + KMError.OK) + tmpVariables[2] = (short) (1 + encoder.getEncodedIntegerLength(int32Ptr)); + tmpVariables[0] += tmpVariables[2]; tmpVariables[1] = KMByteBlob.instance(tmpVariables[0]); bufferRef[0] = KMByteBlob.cast(tmpVariables[1]).getBuffer(); bufferProp[BUF_START_OFFSET] = KMByteBlob.cast(tmpVariables[1]).getStartOff(); bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[1]).length(); // read the cert chain from non-volatile memory. Cert chain is already in // CBOR format. - seProvider.readCertificateChain((byte[]) bufferRef[0], (short) (bufferProp[BUF_START_OFFSET] + 2)); + seProvider.readCertificateChain((byte[]) bufferRef[0], (short) (bufferProp[BUF_START_OFFSET] + tmpVariables[2])); // Encode cert chain. - encoder.encodeCertChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); + encoder.encodeCertChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET], int32Ptr); sendOutgoing(apdu); } @@ -882,7 +893,7 @@ private void processProvisionSharedSecretCmd(APDU apdu) { private void processGetProvisionStatusCmd(APDU apdu) { tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, KMInteger.uint_16(provisionStatus)); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); @@ -957,7 +968,7 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { checkVersionAndPatchLevel(scratchPad); // make response. tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); @@ -974,7 +985,7 @@ private void processGetHmacSharingParamCmd(APDU apdu) { KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(KMByteBlob.instance((short) 0)); // prepare the response tmpVariables[3] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[3]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); @@ -1140,7 +1151,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { tmpVariables[1] = KMByteBlob.instance(scratchPad, tmpVariables[6], tmpVariables[5]); // prepare the response tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); @@ -1158,8 +1169,8 @@ private boolean isKeyUpgradeRequired(short tag, short systemParam) { // OS version in key characteristics must be less the OS version stored in Javacard or the // stored version must be zero. Then only upgrade is allowed else it is invalid argument. if ((tag == KMType.OS_VERSION - && KMInteger.compare(tmpVariables[0], systemParam) == 1 - && KMInteger.compare(systemParam, tmpVariables[1]) == 0)) { + && KMInteger.compare(tmpVariables[0], systemParam) == 1 + && KMInteger.compare(systemParam, tmpVariables[1]) == 0)) { // Key needs upgrade. return true; } else if ((KMInteger.compare(tmpVariables[0], systemParam) == -1)) { @@ -1218,7 +1229,7 @@ private void processUpgradeKeyCmd(APDU apdu) { } // prepare the response tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); @@ -1495,7 +1506,8 @@ private void processAttestKeyCmd(APDU apdu) { cert.buffer((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); cert.build(); bufferProp[BUF_START_OFFSET] = - encoder.encodeCert((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], cert.getCertStart(), cert.getCertLength()); + encoder.encodeCert((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], cert.getCertStart(), cert.getCertLength(), + buildErrorStatus(KMError.OK)); bufferProp[BUF_LEN_OFFSET] = (short) (cert.getCertLength() + (cert.getCertStart() - bufferProp[BUF_START_OFFSET])); sendOutgoing(apdu); } @@ -1660,7 +1672,7 @@ private void processFinishOperationCmd(APDU apdu) { if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } - KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); @@ -1818,18 +1830,18 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratch if (op.getPurpose() == KMType.SIGN) { // len of signature will be 256 bytes short len = op.getOperation().sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, - (short) 0); + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, + (short) 0); // Maximum output size of signature is 256 bytes. data[OUTPUT_DATA] = KMByteBlob.instance((short) 256); Util.arrayCopyNonAtomic( - scratchPad, - (short) 0, - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len), - len); + scratchPad, + (short) 0, + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len), + len); } else { KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); } @@ -2142,7 +2154,7 @@ private void processUpdateOperationCmd(APDU apdu) { if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } - KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[3])); KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); @@ -2248,7 +2260,7 @@ private void processBeginOperationCmd(APDU apdu) { } tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); @@ -2858,7 +2870,7 @@ private void importKey(APDU apdu, byte[] scratchPad) { // prepare the response tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); @@ -3338,7 +3350,7 @@ private static void processGenerateKey(APDU apdu) { // prepare the response tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); @@ -3646,20 +3658,20 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); tmpVariables[1] = KMArray.instance((short) 5); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, - KMByteBlob.exp()); + KMByteBlob.exp()); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, - KMByteBlob.exp()); + KMByteBlob.exp()); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, - KMByteBlob.exp()); + KMByteBlob.exp()); tmpVariables[2] = KMKeyCharacteristics.exp(); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, - tmpVariables[2]); + tmpVariables[2]); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, - KMByteBlob.exp()); + KMByteBlob.exp()); data[KEY_BLOB] = decoder.decodeArray(tmpVariables[1], - KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[KEY_BLOB]).length()); + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); if (tmpVariables[0] < 4) { KMException.throwIt(KMError.INVALID_KEY_BLOB); @@ -3670,18 +3682,18 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); data[KEY_CHARACTERISTICS] = KMArray.cast(data[KEY_BLOB]).get( - KEY_BLOB_KEYCHAR); + KEY_BLOB_KEYCHAR); data[PUB_KEY] = KMType.INVALID_VALUE; if (tmpVariables[0] == 5) { data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); } data[HW_PARAMETERS] = KMKeyCharacteristics - .cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); + .cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); data[SW_PARAMETERS] = KMKeyCharacteristics - .cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); + .cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], - data[APP_DATA], data[ROT], scratchPad); + data[APP_DATA], data[ROT], scratchPad); // make auth data makeAuthData(scratchPad); // Decrypt Secret and verify auth tag @@ -3846,9 +3858,35 @@ private static short deriveKey(byte[] scratchPad) { return tmpVariables[3]; } + // This function masks the error code with POWER_RESET_MASK_FLAG + // in case if card reset event occurred. The clients of the Applet + // has to extract the power reset status from the error code and + // process accordingly. + private static short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (repository.isPowerResetEventOccurred()) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + KMInteger.cast(int32Ptr).getStartOff(), + powerResetStatus); + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + (short) (KMInteger.cast(int32Ptr).getStartOff() + 2), + err); + + // reset power reset status flag to its default value. + repository.restorePowerResetStatus(); + return int32Ptr; + } + private static void sendError(APDU apdu, short err) { - bufferProp[BUF_START_OFFSET] = repository.alloc((short) 2); - bufferProp[BUF_LEN_OFFSET] = encoder.encodeError(err, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], (short) 5); + bufferProp[BUF_START_OFFSET] = repository.alloc((short) 5); + short int32Ptr = buildErrorStatus(err); + bufferProp[BUF_LEN_OFFSET] = encoder.encodeError(int32Ptr, (byte[]) bufferRef[0], + bufferProp[BUF_START_OFFSET], (short) 5); sendOutgoing(apdu); } diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 3fbe47f2..d8705de4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -28,11 +28,7 @@ public class KMOperationState { public static final byte MAX_DATA = 20; - public static final byte MAX_REFS = 1; - private static final byte DATA = 0; - private static final byte REFS = 1; - private static final byte KMOPERATION = 0; - private static final byte SLOT = 1; + private static final byte OPERATION = 0; private static final byte TRUE = 1; private static final byte FALSE = 0; // byte type @@ -56,7 +52,6 @@ public class KMOperationState { private static final byte AES_GCM_UPDATE_ALLOWED = 8; // Object References - private static final byte OPERATION = 0; private byte[] data; private Object[] objRefs; private static KMOperationState prototype; @@ -64,7 +59,7 @@ public class KMOperationState { private KMOperationState() { data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); - objRefs = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); + objRefs = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); isDataUpdated = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); } @@ -75,22 +70,19 @@ private static KMOperationState proto() { return prototype; } - public static KMOperationState instance(short opHandle, Object[] slot) { + public static KMOperationState instance(short opHandle) { KMOperationState opState = proto(); opState.reset(); Util.setShort(prototype.data, OP_HANDLE, opHandle); - prototype.objRefs[SLOT] = slot; return opState; } - public static KMOperationState read(byte[] oprHandle, short off, Object[] slot) { + public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr) { KMOperationState opState = proto(); opState.reset(); - Util.arrayCopy((byte[]) slot[DATA], (short) 0, prototype.data, (short) 0, (short) prototype.data.length); - Object[] ops = ((Object[]) slot[REFS]); - prototype.objRefs[KMOPERATION] = ops[OPERATION]; + Util.arrayCopy(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); + prototype.objRefs[OPERATION] = opr; Util.setShort(prototype.data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); - prototype.objRefs[SLOT] = slot; return opState; } @@ -100,7 +92,7 @@ public void persist() { } KMRepository.instance().persistOperation(data, Util.getShort(data, OP_HANDLE), - (KMOperation) objRefs[KMOPERATION]); + (KMOperation) objRefs[OPERATION]); isDataUpdated[0] = FALSE; } @@ -114,8 +106,7 @@ public short getKeySize() { public void reset() { isDataUpdated[0] = FALSE; - objRefs[KMOPERATION] = null; - objRefs[SLOT] = null; + objRefs[OPERATION] = null; Util.arrayFillNonAtomic( data, (short) 0, (short) data.length, (byte) 0); } @@ -125,14 +116,8 @@ private void dataUpdated() { } public void release() { - Object[] slots = (Object[]) objRefs[SLOT]; - Object[] ops = ((Object[]) slots[REFS]); - ((KMOperation) ops[OPERATION]).abort(); - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic( - (byte[]) slots[0], (short) 0, (short) ((byte[]) slots[0]).length, (byte) 0); - ops[OPERATION] = null; - JCSystem.commitTransaction(); + if (objRefs[OPERATION] != null) + ((KMOperation) objRefs[OPERATION]).abort(); reset(); } @@ -150,13 +135,13 @@ public void setPurpose(byte purpose) { } public void setOperation(KMOperation opr) { - objRefs[KMOPERATION] = opr; + objRefs[OPERATION] = opr; dataUpdated(); persist(); } public KMOperation getOperation() { - return (KMOperation) objRefs[KMOPERATION]; + return (KMOperation) objRefs[OPERATION]; } public boolean isAuthPerOperationReqd() { diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index c9fe8398..6dfc2d0d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -42,6 +42,7 @@ public class KMRepository implements KMUpgradable { private static final short OPERATION_HANDLE_OFFSET = 1; private static final short OPERATION_HANDLE_ENTRY_SIZE = OPERATION_HANDLE_SIZE + OPERATION_HANDLE_STATUS_SIZE; + private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; // Data table offsets public static final byte COMPUTED_HMAC_KEY = 8; @@ -90,6 +91,17 @@ public class KMRepository implements KMUpgradable { private byte[] dataTable; private short dataIndex; private short[] reclaimIndex; + // This variable is used to monitor the power reset status as the Applet does not get + // any power reset event. Initially the value of this variable is set to POWER_RESET_STATUS_FLAG. + // If the power reset happens then this value becomes 0. + private byte[] powerResetStatus; + + // Operation table. + private static final short OPER_TABLE_DATA_OFFSET = 0; + private static final short OPER_TABLE_OPR_OFFSET = 1; + private static final short OPER_DATA_LEN = OPERATION_HANDLE_ENTRY_SIZE + KMOperationState.MAX_DATA; + private static final short DATA_ARRAY_LENGTH = MAX_OPS * OPER_DATA_LEN; + // Singleton instance private static KMRepository repository; @@ -102,28 +114,49 @@ public KMRepository(boolean isUpgrading) { heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); + powerResetStatus = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); heapIndex[0] = (short) 0; reclaimIndex[0] = HEAP_SIZE; + powerResetStatus[0] = POWER_RESET_STATUS_FLAG; newDataTable(isUpgrading); - operationStateTable = new Object[MAX_OPS]; - // create and initialize operation state table. - //First byte in the operation handle buffer denotes whether the operation is - //reserved or unreserved. - byte index = 0; - while (index < MAX_OPS) { - operationStateTable[index] = new Object[]{new byte[OPERATION_HANDLE_ENTRY_SIZE], - new Object[]{new byte[KMOperationState.MAX_DATA], - new Object[KMOperationState.MAX_REFS]}}; - index++; - } + + operationStateTable = new Object[2]; + operationStateTable[0] = JCSystem.makeTransientByteArray(DATA_ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); + operationStateTable[1] = JCSystem.makeTransientObjectArray(MAX_OPS, JCSystem.CLEAR_ON_RESET); + //Initialize the device locked status if (!isUpgrading) { setDeviceLock(false); setDeviceLockPasswordOnly(false); + } else { + // In case of upgrade, the applet is deleted and installed again so all + // volatile memory is erased. so it is necessary to force the power reset flag + // to 0 so that the HAL can clear its operation state. + powerResetStatus[0] = (byte) 0; } repository = this; } + // This function checks if card reset event occurred and this function + // should only be called before processing any of the APUs. + // Transient memory is cleared in two cases: + // 1. Card reset event + // 2. Applet upgrade. + public boolean isPowerResetEventOccurred() { + if (powerResetStatus[0] == POWER_RESET_STATUS_FLAG) { + return false; + } + return true; + } + + /** + * This function sets the power reset status flag to its + * default value. + */ + public void restorePowerResetStatus() { + powerResetStatus[0] = POWER_RESET_STATUS_FLAG; + } + public void getOperationHandle(short oprHandle, byte[] buf, short off, short len) { if (KMInteger.cast(oprHandle).length() != OPERATION_HANDLE_SIZE) { KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); @@ -133,17 +166,19 @@ public void getOperationHandle(short oprHandle, byte[] buf, short off, short len public KMOperationState findOperation(byte[] buf, short off, short len) { short index = 0; - byte[] opId; + byte[] oprTableData; + short offset = 0; + oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; + Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; while (index < MAX_OPS) { - opId = ((byte[]) ((Object[]) operationStateTable[index])[0]); - if (0 == Util.arrayCompare(buf, off, opId, OPERATION_HANDLE_OFFSET, len)) { - return KMOperationState - .read(opId, OPERATION_HANDLE_OFFSET, - (Object[]) ((Object[]) operationStateTable[index])[1]); + offset = (short) (index * OPER_DATA_LEN); + if (0 == Util.arrayCompare(buf, off, oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), len)) { + return KMOperationState.read(oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), oprTableData, + (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + operations[index]); } index++; } - return null; } @@ -164,13 +199,13 @@ public KMOperationState findOperation(short operationHandle) { /* opHandle is a KMInteger */ public KMOperationState reserveOperation(short opHandle) { short index = 0; - byte[] opId; + byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; + short offset = 0; while (index < MAX_OPS) { - opId = (byte[]) ((Object[]) operationStateTable[index])[0]; + offset = (short) (index * OPER_DATA_LEN); /* Check for unreserved operation state */ - if (opId[OPERATION_HANDLE_STATUS_OFFSET] == 0) { - return KMOperationState - .instance(opHandle, (Object[]) ((Object[]) operationStateTable[index])[1]); + if (oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 0) { + return KMOperationState.instance(opHandle); } index++; } @@ -179,7 +214,9 @@ public KMOperationState reserveOperation(short opHandle) { public void persistOperation(byte[] data, short opHandle, KMOperation op) { short index = 0; - byte[] opId; + byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; + Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + short offset = 0; short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); getOperationHandle( opHandle, @@ -188,21 +225,17 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op) { KMByteBlob.cast(buf).length()); //Update an existing operation state. while (index < MAX_OPS) { - opId = (byte[]) ((Object[]) operationStateTable[index])[0]; - if ((1 == opId[OPERATION_HANDLE_STATUS_OFFSET]) + offset = (short) (index * OPER_DATA_LEN); + if ((1 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) && (0 == Util.arrayCompare( - opId, - OPERATION_HANDLE_OFFSET, + oprTableData, + (short) (offset + OPERATION_HANDLE_OFFSET), KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), KMByteBlob.cast(buf).length()))) { - Object[] slot = (Object[]) ((Object[]) operationStateTable[index])[1]; - JCSystem.beginTransaction(); - Util.arrayCopy(data, (short) 0, (byte[]) slot[0], (short) 0, - (short) ((byte[]) slot[0]).length); - Object[] ops = ((Object[]) slot[1]); - ops[0] = op; - JCSystem.commitTransaction(); + Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + KMOperationState.MAX_DATA); + operations[index] = op; return; } index++; @@ -211,22 +244,18 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op) { index = 0; //Persist a new operation. while (index < MAX_OPS) { - opId = (byte[]) ((Object[]) operationStateTable[index])[0]; - if (0 == opId[OPERATION_HANDLE_STATUS_OFFSET]) { - Object[] slot = (Object[]) ((Object[]) operationStateTable[index])[1]; - JCSystem.beginTransaction(); - opId[OPERATION_HANDLE_STATUS_OFFSET] = 1;/*reserved */ + offset = (short) (index * OPER_DATA_LEN); + if (0 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) { + oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] = 1;/*reserved */ Util.arrayCopy( KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), - opId, - OPERATION_HANDLE_OFFSET, + oprTableData, + (short) (offset + OPERATION_HANDLE_OFFSET), OPERATION_HANDLE_SIZE); - Util.arrayCopy(data, (short) 0, (byte[]) slot[0], (short) 0, - (short) ((byte[]) slot[0]).length); - Object[] ops = ((Object[]) slot[1]); - ops[0] = op; - JCSystem.commitTransaction(); + Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + KMOperationState.MAX_DATA); + operations[index] = op; break; } index++; @@ -235,7 +264,9 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op) { public void releaseOperation(KMOperationState op) { short index = 0; - byte[] oprHandleBuf; + byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; + Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + short offset = 0; short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); getOperationHandle( op.getHandle(), @@ -243,17 +274,16 @@ public void releaseOperation(KMOperationState op) { KMByteBlob.cast(buf).getStartOff(), KMByteBlob.cast(buf).length()); while (index < MAX_OPS) { - oprHandleBuf = ((byte[]) ((Object[]) operationStateTable[index])[0]); - if ((oprHandleBuf[OPERATION_HANDLE_STATUS_OFFSET] == 1) && - (0 == Util.arrayCompare(oprHandleBuf, - OPERATION_HANDLE_OFFSET, + offset = (short) (index * OPER_DATA_LEN); + if ((oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 1) && + (0 == Util.arrayCompare(oprTableData, + (short) (offset + OPERATION_HANDLE_OFFSET), KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), KMByteBlob.cast(buf).length()))) { - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(oprHandleBuf, (short) 0, (short) oprHandleBuf.length, (byte) 0); - JCSystem.commitTransaction(); + Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); op.release(); + operations[index] = null; break; } index++; @@ -262,19 +292,17 @@ public void releaseOperation(KMOperationState op) { public void releaseAllOperations() { short index = 0; - byte[] oprHandleBuf; + byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; + Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + short offset = 0; while (index < MAX_OPS) { - oprHandleBuf = ((byte[]) ((Object[]) operationStateTable[index])[0]); - if (oprHandleBuf[OPERATION_HANDLE_STATUS_OFFSET] == 1) { - Object[] slot = (Object[]) ((Object[]) operationStateTable[index])[1]; - Object[] ops = ((Object[]) slot[1]); - ((KMOperation) ops[0]).abort(); - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic((byte[]) slot[0], (short) 0, - (short) ((byte[]) slot[0]).length, (byte) 0); - Util.arrayFillNonAtomic(oprHandleBuf, (short) 0, (short) oprHandleBuf.length, (byte) 0); - ops[0] = null; - JCSystem.commitTransaction(); + offset = (short) (index * OPER_DATA_LEN); + if (oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 1) { + Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); + if (operations[index] != null) { + ((KMOperation) operations[index]).abort(); + operations[index] = null; + } } index++; } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 65ae8fb3..167aa5b2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -559,4 +559,10 @@ KMOperation initAsymmetricOperation( */ KMPreSharedKey getPresharedKey(); + /** + * Releases all the instance back to pool. + * Generally this is used when card is reset. + */ + void releaseAllOperations(); + } diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index 845071eb..a7298172 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -51,6 +51,7 @@ #define INS_END_KM_CMD 0x7F #define SW_KM_OPR 0UL #define SB_KM_OPR 1UL +#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) namespace keymaster { namespace V4_1 { @@ -182,18 +183,6 @@ static T translateExtendedErrorsToHalErrors(T& errorCode) { return err; } -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response, bool - hasErrorCode) { - std::unique_ptr item(nullptr); - T errorCode = T::OK; - std::tie(item, errorCode) = cb.decodeData(response, hasErrorCode); - - if (T::OK != errorCode) - errorCode = translateExtendedErrorsToHalErrors(errorCode); - return {std::move(item), errorCode}; -} - /* Generate new operation handle */ static ErrorCode generateOperationHandle(uint64_t& oprHandle) { std::map>::iterator it; @@ -241,6 +230,64 @@ static void deleteOprHandleEntry(uint64_t halGeneratedOperationHandle) { operationTable.erase(halGeneratedOperationHandle); } +/* Clears all the strongbox operation handle entries from operation table */ +static void clearStrongboxOprHandleEntries(const std::unique_ptr& oprCtx) { + LOG(WARNING) << "secure element reset occurred. All the below operation handles for private key operations" + << " becomes invalid and the owners of these operations has to restart the operation again."; + auto it = operationTable.begin(); + while (it != operationTable.end()) { + if (it->second.second == SB_KM_OPR) { //Strongbox operation + LOG(WARNING) << "operation handle: " << it->first << " is invalid"; + oprCtx->clearOperationData(it->second.first); + it = operationTable.erase(it); + } else { + ++it; + } + } +} + +/** + * Returns the negative value of the same number. + */ +static inline int32_t get2sCompliment(uint32_t value) { + return static_cast(~value+1); +} + +/** + * Clears all the strongbox operation handle entries if secure element power reset happens. + * And also extracts the error code value after unmasking the power reset status flag. + */ +static uint32_t handleErrorCode(const std::unique_ptr& oprCtx, uint32_t errorCode) { + //Check if secure element is reset + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + //Clear the operation table for Strongbox operations entries. + clearStrongboxOprHandleEntries(oprCtx); + // Unmask the power reset status flag. + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return errorCode; +} + +template +static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response, bool + hasErrorCode, const std::unique_ptr& oprCtx) { + std::unique_ptr item(nullptr); + T errorCode = T::OK; + std::tie(item, errorCode) = cb.decodeData(response, hasErrorCode); + + uint32_t tempErrCode = handleErrorCode(oprCtx, static_cast(errorCode)); + + // SE sends errocode as unsigned value so convert the unsigned value + // into a signed value of same magnitude and copy back to errorCode. + errorCode = static_cast(get2sCompliment(tempErrCode)); + + if (T::OK != errorCode) + errorCode = translateExtendedErrorsToHalErrors(errorCode); + return {std::move(item), errorCode}; +} + ErrorCode encodeParametersVerified(const VerificationToken& verificationToken, std::vector& asn1ParamsVerified) { if (verificationToken.parametersVerified.size() > 0) { AuthorizationSet paramSet; @@ -373,58 +420,10 @@ uint16_t getStatus(std::vector& inputData) { return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); } -static bool isSEProvisioned() { - ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_GET_PROVISION_STATUS_CMD; - std::vector cborData; - std::vector response; - std::unique_ptr item; - CborConverter cborConverter; - std::vector apdu; - bool ret = false; - - errorCode = constructApduMessage(ins, cborData, apdu); - if(errorCode != ErrorCode::OK) return ret; - - if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { - LOG(ERROR) << " Failed to send GET_PROVISION_STATUS_CMD "; - return ret; - } - - if((response.size() <= 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { - return ret; - } - //Check if SE is provisioned. - std::tie(item, errorCode) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), - true); - if(item != NULL) { - uint64_t status; - - if(!cborConverter.getUint64(item, 1, status)) { - LOG(ERROR) << "Failed to parse the status from cbor data"; - return ret; - } - - if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { - ret = true; - } - } - return ret; -} - - ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response) { ErrorCode ret = ErrorCode::UNKNOWN_ERROR; std::vector apdu; - if (!isSEProvisioned()) { - LOG(ERROR) << "Javacard applet is not provisioned."; - return ret; - } ret = constructApduMessage(ins, inData, apdu); if(ret != ErrorCode::OK) return ret; @@ -439,7 +438,11 @@ ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& oprCtx) { ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; cppbor::Array array; std::unique_ptr item; @@ -454,7 +457,7 @@ static ErrorCode setAndroidSystemProperties(CborConverter& cborConverter_) { if (ErrorCode::OK == errorCode) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx); } if (ErrorCode::OK != errorCode) LOG(ERROR) << "Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t) errorCode; @@ -472,7 +475,7 @@ JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::A // Send Android system properties like os_version, os_patchlevel and vendor_patchlevel // to the Applet. Incase if setting system properties fails here, again try setting // it from computeSharedHmac. - if (ErrorCode::OK == setAndroidSystemProperties(cborConverter_)) { + if (ErrorCode::OK == setAndroidSystemProperties(cborConverter_, oprCtx_)) { isEachSystemPropertySet = true; } @@ -493,8 +496,8 @@ Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_ ErrorCode ret = sendData(Instruction::INS_GET_HW_INFO_CMD, input, resp); if (ret == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. - std::tie(item, ret) = cborConverter_.decodeData(std::vector(resp.begin(), resp.end()-2), - false); + std::tie(item, ret) = decodeData(cborConverter_, std::vector(resp.begin(), resp.end()-2), + false, oprCtx_); if (item != nullptr) { std::vector temp; if(!cborConverter_.getUint64(item, 0, securityLevel) || @@ -524,7 +527,7 @@ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingPa if (ErrorCode::OK == errorCode) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborData.begin(), cborData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getHmacSharingParameters(item, 1, hmacSharingParameters)) { errorCode = ErrorCode::UNKNOWN_ERROR; @@ -590,7 +593,7 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { std::vector bstr; if(!cborConverter_.getBinaryArray(item, 1, bstr)) { @@ -647,7 +650,7 @@ Return JavacardKeymaster4Device::addRngEntropy(const hidl_vec(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); } return errorCode; } @@ -675,11 +678,10 @@ Return JavacardKeymaster4Device::generateKey(const hidl_vec& std::vector cborData = array.encode(); errorCode = sendData(Instruction::INS_GENERATE_KEY_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { @@ -725,7 +727,7 @@ Return JavacardKeymaster4Device::importKey(const hidl_vec& k if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { @@ -780,7 +782,7 @@ Return JavacardKeymaster4Device::importWrappedKey(const hidl_vec& if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { @@ -809,11 +811,10 @@ Return JavacardKeymaster4Device::getKeyCharacteristics(const hidl_vec cborData = array.encode(); errorCode = sendData(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getKeyCharacteristics(item, 1, keyCharacteristics)) { keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); @@ -879,7 +880,7 @@ Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToA std::vector rootCert; //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getMultiBinaryArray(item, 1, temp)) { errorCode = ErrorCode::UNKNOWN_ERROR; @@ -891,7 +892,7 @@ Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToA //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { std::vector chain; if(!cborConverter_.getBinaryArray(item, 1, chain)) { @@ -929,7 +930,7 @@ Return JavacardKeymaster4Device::upgradeKey(const hidl_vec& keyBl if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getBinaryArray(item, 1, upgradedKeyBlob)) errorCode = ErrorCode::UNKNOWN_ERROR; @@ -952,7 +953,7 @@ Return JavacardKeymaster4Device::deleteKey(const hidl_vec& k if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); } return errorCode; } @@ -968,7 +969,7 @@ Return JavacardKeymaster4Device::deleteAllKeys() { if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); } return errorCode; } @@ -984,7 +985,7 @@ Return JavacardKeymaster4Device::destroyAttestationIds() { if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); } return errorCode; } @@ -1068,7 +1069,7 @@ Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec< if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getKeyParameters(item, 1, outParams) || !cborConverter_.getUint64(item, 2, operationHandle)) { @@ -1101,6 +1102,9 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co uint64_t operationHandle; UpdateOperationResponse response; if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" + << " secure element reset occurred. In case if secure element reset occured owner" + << "has to restart this operation again."; _hidl_cb(errorCode, inputConsumed, outParams, output); return Void(); } @@ -1157,7 +1161,7 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { /*Ignore inputConsumed from javacard SE since HAL consumes all the input */ //cborConverter_.getUint64(item, 1, inputConsumed); @@ -1202,6 +1206,9 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co FinishOperationResponse response; if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" + << " secure element reset occurred. In case if secure element reset occured owner" + << "has to restart this operation again."; _hidl_cb(errorCode, outParams, output); return Void(); } @@ -1285,7 +1292,7 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); if (item != nullptr) { //There is a change that this finish callback may gets called multiple times if the input data size //is larger the MAX_ALLOWED_INPUT_SIZE (Refer OperationContext) so parse and get the outParams only @@ -1321,6 +1328,8 @@ Return JavacardKeymaster4Device::abort(uint64_t halGeneratedOprHandle ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; uint64_t operationHandle; if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" + << " secure element reset occurred."; return errorCode; } AbortOperationRequest request; @@ -1344,7 +1353,7 @@ Return JavacardKeymaster4Device::abort(uint64_t halGeneratedOprHandle if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true); + true, oprCtx_); } } /* Delete the entry on this operationHandle */ @@ -1376,7 +1385,7 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device if(ret == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true); + cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); } return errorCode; } @@ -1393,7 +1402,7 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device if(ret == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true); + cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); } return errorCode; } diff --git a/HAL/keymaster/include/CborConverter.h b/HAL/keymaster/include/CborConverter.h index ecbe2783..45855244 100644 --- a/HAL/keymaster/include/CborConverter.h +++ b/HAL/keymaster/include/CborConverter.h @@ -65,7 +65,7 @@ class CborConverter } else if (MajorType::UINT == getType(item)) { uint64_t err; if(getUint64(item, err)) { - errorCode = static_cast(get2sCompliment(static_cast(err))); + errorCode = static_cast(err); } item = nullptr; /*Already read the errorCode. So no need of sending item to client */ } @@ -162,18 +162,13 @@ class CborConverter if (!getUint64(item, pos, errorVal)) { return ret; } - errorCode = static_cast(get2sCompliment(static_cast(errorVal))); + errorCode = static_cast(errorVal); ret = true; return ret; } private: - /** - * Returns the negative value of the same number. - */ - inline int32_t get2sCompliment(uint32_t value) { return static_cast(~value+1); } - /** * Get the type of the Item pointer. */ diff --git a/ProvisioningTool/Provision.cpp b/ProvisioningTool/Provision.cpp index 92d76dbc..4251cd80 100644 --- a/ProvisioningTool/Provision.cpp +++ b/ProvisioningTool/Provision.cpp @@ -35,6 +35,7 @@ #define APDU_P1 0x40 #define APDU_P2 0x00 #define APDU_RESP_STATUS_OK 0x9000 +#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) namespace keymaster { namespace V4_1 { @@ -53,6 +54,23 @@ enum class Instruction { INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+9, }; +//Extended error codes +enum ExtendedErrors { + SW_CONDITIONS_NOT_SATISFIED = -10001, + UNSUPPORTED_CLA = -10002, + INVALID_P1P2 = -10003, + UNSUPPORTED_INSTRUCTION = -10004, + CMD_NOT_ALLOWED = -10005, + SW_WRONG_LENGTH = -10006, + INVALID_DATA = -10007, + CRYPTO_ILLEGAL_USE = -10008, + CRYPTO_ILLEGAL_VALUE = -10009, + CRYPTO_INVALID_INIT = -10010, + CRYPTO_NO_SUCH_ALGORITHM = -10011, + CRYPTO_UNINITIALIZED_KEY = -10012, + GENERIC_UNKNOWN_ERROR = -10013 +}; + enum ProvisionStatus { NOT_PROVISIONED = 0x00, PROVISION_STATUS_ATTESTATION_KEY = 0x01, @@ -68,6 +86,79 @@ enum ProvisionStatus { static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut); static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response); static uint16_t getStatus(std::vector& inputData); +template +static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response); +template +static T translateExtendedErrorsToHalErrors(T& errorCode); + +template +static T translateExtendedErrorsToHalErrors(T& errorCode) { + T err; + switch(static_cast(errorCode)) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + err = T::UNKNOWN_ERROR; + break; + case CRYPTO_NO_SUCH_ALGORITHM: + err = T::UNSUPPORTED_ALGORITHM; + break; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + err = T::UNIMPLEMENTED; + break; + default: + err = static_cast(errorCode); + break; + } + return err; +} + +/** + * Returns the negative value of the same number. + */ +static inline int32_t get2sCompliment(uint32_t value) { + return static_cast(~value+1); +} + +/** + * This function separates the original error code from the + * power reset flag and returns the original error code. + */ +static uint32_t extractErrorCode(uint32_t errorCode) { + //Check if secure element is reset + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + LOG(ERROR) << "Secure element reset happened"; + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return errorCode; +} + +template +static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response) { + std::unique_ptr item(nullptr); + T errorCode = T::OK; + std::tie(item, errorCode) = cb.decodeData(response, true); + + uint32_t tempErrCode = extractErrorCode(static_cast(errorCode)); + + // SE sends errocode as unsigned value so convert the unsigned value + // into a signed value of same magnitude and copy back to errorCode. + errorCode = static_cast(get2sCompliment(tempErrCode)); + + if (T::OK != errorCode) + errorCode = translateExtendedErrorsToHalErrors(errorCode); + return {std::move(item), errorCode}; +} static inline X509* parseDerCertificate(std::vector& certData) { X509 *x509 = NULL; @@ -174,8 +265,7 @@ static ErrorCode sendProvisionData(std::unique_ptr 2)) { //Skip last 2 bytes in cborData, it contains status. - std::tie(item, ret) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), - true); + std::tie(item, ret) = decodeData(cborConverter, std::vector(response.begin(), response.end()-2)); } else { ret = ErrorCode::UNKNOWN_ERROR; } @@ -382,8 +472,7 @@ ErrorCode Provision::getProvisionStatus(uint64_t& status) { return errorCode; } //Check if SE is provisioned. - std::tie(item, errorCode) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), - true); + std::tie(item, errorCode) = decodeData(cborConverter, std::vector(response.begin(), response.end()-2)); if(item != NULL) { if(!cborConverter.getUint64(item, 1, status)) {