diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index bc599510..9e9c2c6c 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -446,6 +446,10 @@ public class KMFunctionalTest { (byte) 0xe9, (byte) 0x77, (byte) 0x4c, (byte) 0x45, (byte) 0xc3, (byte) 0xa3, (byte) 0xcf, (byte) 0x0d, (byte) 0x16, (byte) 0x10, (byte) 0xe4, (byte) 0x79, (byte) 0x43, (byte) 0x3a, (byte) 0x21, (byte) 0x5a, (byte) 0x30, (byte) 0xcf}; + private static final int OS_VERSION = 1; + 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 CardSimulator simulator; private KMEncoder encoder; @@ -657,7 +661,8 @@ private void provisionCmd(CardSimulator simulator) { provisionSharedSecret(simulator); provisionAttestIds(simulator); // set bootup parameters - setBootParams(simulator, (short) 1, (short) 1, (short) 0, (short) 0); + setBootParams(simulator, (short) OS_VERSION, (short) OS_PATCH_LEVEL, + (short) VENDOR_PATCH_LEVEL, (short) BOOT_PATCH_LEVEL); provisionLocked(simulator); } @@ -2699,18 +2704,63 @@ public void testUpgradeKey() { osPatch = KMIntegerTag.cast(osPatch).getValue(); Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 1); Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 1); - setBootParams(simulator, (short) 2, (short) 2, (short) 1, (short) 1); - ret = upgradeKey(KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), null, null); - keyBlobPtr = KMArray.cast(ret).get((short) 1); - ret = getKeyCharacteristics(keyBlobPtr); - keyCharacteristics = KMArray.cast(ret).get((short) 1); - hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - osVersion = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, hwParams); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - osPatch = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, hwParams); - osPatch = KMIntegerTag.cast(osPatch).getValue(); - Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 2); - Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 2); + short NO_UPGRADE = 0x01; + short UPGRADE = 0x02; + short[][] test_data = { + {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.OK }, + {OS_VERSION+1, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, + {OS_VERSION, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, + {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, + {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL+1, UPGRADE, KMError.OK }, + {OS_VERSION+1, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL+1, UPGRADE, KMError.OK }, + {OS_VERSION+1, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, + {OS_VERSION+1, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, + {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL-1, NO_UPGRADE, KMError.INVALID_ARGUMENT }, + {OS_VERSION-1/*0*/, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, + {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.INVALID_ARGUMENT }, + {OS_VERSION, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.INVALID_ARGUMENT }, + {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++) { + setBootParams(simulator, (short) test_data[i][0], (short) test_data[i][1], + (short) test_data[i][2], (short) test_data[i][3]); + ret = upgradeKey( + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + null, null, test_data[i][5]); + if (test_data[i][5] != KMError.OK) + continue; + keyBlobPtr = KMArray.cast(ret).get((short) 1); + if (test_data[i][4] == UPGRADE) + Assert.assertNotEquals(KMByteBlob.cast(keyBlobPtr).length(), 0); + else + Assert.assertEquals(KMByteBlob.cast(keyBlobPtr).length(), 0); + if (KMByteBlob.cast(keyBlobPtr).length() != 0) { + ret = getKeyCharacteristics(keyBlobPtr); + keyCharacteristics = KMArray.cast(ret).get((short) 1); + hwParams = KMKeyCharacteristics.cast(keyCharacteristics) + .getHardwareEnforced(); + osVersion = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, + hwParams); + osVersion = KMIntegerTag.cast(osVersion).getValue(); + osPatch = KMKeyParameters.findTag(KMType.UINT_TAG, + KMType.OS_PATCH_LEVEL, hwParams); + osPatch = KMIntegerTag.cast(osPatch).getValue(); + short ptr = KMKeyParameters.findTag(KMType.UINT_TAG, + KMType.VENDOR_PATCH_LEVEL, hwParams); + short vendorPatchLevel = KMIntegerTag.cast(ptr).getValue(); + ptr = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, + hwParams); + short bootPatchLevel = KMIntegerTag.cast(ptr).getValue(); + Assert.assertEquals(KMInteger.cast(osVersion).getShort(), + test_data[i][0]); + Assert.assertEquals(KMInteger.cast(osPatch).getShort(), + test_data[i][1]); + Assert.assertEquals(KMInteger.cast(vendorPatchLevel).getShort(), + test_data[i][2]); + Assert.assertEquals(KMInteger.cast(bootPatchLevel).getShort(), + test_data[i][3]); + } + } cleanUp(); } @@ -2724,7 +2774,7 @@ public void testDestroyAttIds() { cleanUp(); } - private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData) { + private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData, short expectedErr) { short tagCount = 0; short clientIdTag = 0; short appDataTag = 0; @@ -2753,15 +2803,21 @@ private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData) { CommandAPDU apdu = encodeApdu((byte) INS_UPGRADE_KEY_CMD, arr); // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - return ret; + if (KMError.OK == expectedErr) { + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + ret = decoder.decode(ret, respBuf, (short) 0, len); + Assert.assertEquals(expectedErr, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); + return ret; + } else { + short ret = KMInteger.exp(); + ret = decoder.decode(ret, respBuf, (short) 0, len); + Assert.assertEquals(expectedErr, KMInteger.cast(ret).getShort()); + return ret; + } } @Test diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b79cabaa..8f40f775 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1096,6 +1096,31 @@ private void processComputeSharedHmacCmd(APDU apdu) { sendOutgoing(apdu); } + private boolean isKeyUpgradeRequired(short tag, short systemParam) { + // validate the tag and check if key needs upgrade. + tmpVariables[0] = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); + tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); + tmpVariables[1] = KMInteger.uint_8((byte) 0); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // 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)) { + // Key needs upgrade. + return true; + } else if ((KMInteger.compare(tmpVariables[0], systemParam) == -1)) { + // Each os version or patch level associated with the key must be less than it's + // corresponding value stored in Javacard, then only upgrade is allowed otherwise it + // is invalid argument. + return true; + } else if (KMInteger.compare(tmpVariables[0], systemParam) == 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + return false; + } + private void processUpgradeKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); @@ -1123,61 +1148,14 @@ private void processUpgradeKeyCmd(APDU apdu) { } // parse existing key blob parseEncryptedKeyBlob(scratchPad); - // validate characteristics to be upgraded. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - tmpVariables[1] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - tmpVariables[2] = repository.getOsVersion(); - tmpVariables[3] = repository.getOsPatch(); - tmpVariables[4] = KMInteger.uint_8((byte) 0); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // 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 (KMInteger.compare(tmpVariables[0], tmpVariables[2]) != -1 - && KMInteger.compare(tmpVariables[2], tmpVariables[4]) != 0) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - tmpVariables[5] = KMError.INVALID_ARGUMENT; - } - } - if (tmpVariables[1] != KMType.INVALID_VALUE) { - // The key characteristics should have had os patch level < os patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(tmpVariables[1], tmpVariables[3]) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - tmpVariables[5] = KMError.INVALID_ARGUMENT; - } - } - //Compare vendor patch levels - tmpVariables[1] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, data[HW_PARAMETERS]); - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - tmpVariables[2] = repository.getVendorPatchLevel(); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - // The key characteristics should have had vendor patch level < vendor patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(tmpVariables[1], tmpVariables[2]) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - tmpVariables[5] = KMError.INVALID_ARGUMENT; - } - } - //Compare boot patch levels - tmpVariables[1] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, data[HW_PARAMETERS]); - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - tmpVariables[2] = repository.getBootPatchLevel(); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - // The key characteristics should have had boot patch level < boot patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(tmpVariables[1], tmpVariables[2]) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - tmpVariables[5] = KMError.INVALID_ARGUMENT; - } - } - - if (tmpVariables[5] != KMError.INVALID_ARGUMENT) { + boolean isKeyUpgradeRequired = false; + // Check if key requires upgrade. + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, repository.getBootPatchLevel()); + + if (isKeyUpgradeRequired) { // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); // create new key blob with current os version etc.