Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class KMFunctionalTest {
private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 6; //0x06
private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 7; //0x07
private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 8; //0x08
private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 9; //0x09
// Top 32 commands are reserved for provisioning.
private static final byte INS_END_KM_PROVISION_CMD = 0x20;

Expand Down Expand Up @@ -473,40 +474,52 @@ private void init() {
provisionCmd(simulator);
}

private void setBootParams(CardSimulator simulator, short osVersion,
short osPatchLevel, short vendorPatchLevel, short bootPatchLevel) {
private void setAndroidOSSystemProperties(CardSimulator simulator, short osVersion,
short osPatchLevel, short vendorPatchLevel) {
// Argument 1 OS Version
short versionPtr = KMInteger.uint_16(osVersion);
// short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG,
// KMType.OS_VERSION,versionPatchPtr);
// Argument 2 OS Patch level
short patchPtr = KMInteger.uint_16(osPatchLevel);
short vendorpatchPtr = KMInteger.uint_16((short) vendorPatchLevel);
// Arguments
short arrPtr = KMArray.instance((short) 3);
KMArray vals = KMArray.cast(arrPtr);
vals.add((short) 0, versionPtr);
vals.add((short) 1, patchPtr);
vals.add((short) 2, vendorpatchPtr);
CommandAPDU apdu = encodeApdu((byte) INS_SET_VERSION_PATCHLEVEL_CMD, arrPtr);
// print(commandAPDU.getBytes());
ResponseAPDU response = simulator.transmitCommand(apdu);
Assert.assertEquals(0x9000, response.getSW());

}

private void setBootParams(CardSimulator simulator, short bootPatchLevel) {
// Argument 0 boot patch level
short bootpatchPtr = KMInteger.uint_16((short) bootPatchLevel);
// Argument 3 Verified Boot Key
// Argument 1 Verified Boot Key
byte[] bootKeyHash = "00011122233344455566677788899900".getBytes();
short bootKeyPtr = KMByteBlob.instance(bootKeyHash, (short) 0,
(short) bootKeyHash.length);
// Argument 4 Verified Boot Hash
// Argument 2 Verified Boot Hash
short bootHashPtr = KMByteBlob.instance(bootKeyHash, (short) 0,
(short) bootKeyHash.length);
// Argument 5 Verified Boot State
// Argument 3 Verified Boot State
short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE,
KMType.VERIFIED_BOOT);
// Argument 6 Device Locked
// Argument 4 Device Locked
short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED,
KMType.DEVICE_LOCKED_FALSE);
// Arguments
short arrPtr = KMArray.instance((short) 8);
short arrPtr = KMArray.instance((short) 5);
KMArray vals = KMArray.cast(arrPtr);
vals.add((short) 0, versionPtr);
vals.add((short) 1, patchPtr);
vals.add((short) 2, vendorpatchPtr);
vals.add((short) 3, bootpatchPtr);
vals.add((short) 4, bootKeyPtr);
vals.add((short) 5, bootHashPtr);
vals.add((short) 6, bootStatePtr);
vals.add((short) 7, deviceLockedPtr);
vals.add((short) 0, bootpatchPtr);
vals.add((short) 1, bootKeyPtr);
vals.add((short) 2, bootHashPtr);
vals.add((short) 3, bootStatePtr);
vals.add((short) 4, deviceLockedPtr);
CommandAPDU apdu = encodeApdu((byte) INS_SET_BOOT_PARAMS_CMD, arrPtr);
// print(commandAPDU.getBytes());
ResponseAPDU response = simulator.transmitCommand(apdu);
Expand Down Expand Up @@ -661,8 +674,10 @@ private void provisionCmd(CardSimulator simulator) {
provisionSharedSecret(simulator);
provisionAttestIds(simulator);
// set bootup parameters
setBootParams(simulator, (short) OS_VERSION, (short) OS_PATCH_LEVEL,
(short) VENDOR_PATCH_LEVEL, (short) BOOT_PATCH_LEVEL);
setBootParams(simulator, (short) BOOT_PATCH_LEVEL);
// set android system properties
setAndroidOSSystemProperties(simulator, (short) OS_VERSION, (short) OS_PATCH_LEVEL,
(short) VENDOR_PATCH_LEVEL);
provisionLocked(simulator);
}

Expand Down Expand Up @@ -2722,11 +2737,12 @@ 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++) {
setBootParams(simulator, (short) test_data[i][0], (short) test_data[i][1],
(short) test_data[i][2], (short) test_data[i][3]);
setAndroidOSSystemProperties(simulator, (short) test_data[i][0], (short) test_data[i][1],
(short) test_data[i][2]);
setBootParams(simulator, (short) test_data[i][3]);
ret = upgradeKey(
KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length),
null, null, test_data[i][5]);
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);
Expand All @@ -2738,27 +2754,27 @@ public void testUpgradeKey() {
ret = getKeyCharacteristics(keyBlobPtr);
keyCharacteristics = KMArray.cast(ret).get((short) 1);
hwParams = KMKeyCharacteristics.cast(keyCharacteristics)
.getHardwareEnforced();
.getHardwareEnforced();
osVersion = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION,
hwParams);
hwParams);
osVersion = KMIntegerTag.cast(osVersion).getValue();
osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,
KMType.OS_PATCH_LEVEL, hwParams);
KMType.OS_PATCH_LEVEL, hwParams);
osPatch = KMIntegerTag.cast(osPatch).getValue();
short ptr = KMKeyParameters.findTag(KMType.UINT_TAG,
KMType.VENDOR_PATCH_LEVEL, hwParams);
KMType.VENDOR_PATCH_LEVEL, hwParams);
short vendorPatchLevel = KMIntegerTag.cast(ptr).getValue();
ptr = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL,
hwParams);
hwParams);
short bootPatchLevel = KMIntegerTag.cast(ptr).getValue();
Assert.assertEquals(KMInteger.cast(osVersion).getShort(),
test_data[i][0]);
test_data[i][0]);
Assert.assertEquals(KMInteger.cast(osPatch).getShort(),
test_data[i][1]);
test_data[i][1]);
Assert.assertEquals(KMInteger.cast(vendorPatchLevel).getShort(),
test_data[i][2]);
test_data[i][2]);
Assert.assertEquals(KMInteger.cast(bootPatchLevel).getShort(),
test_data[i][3]);
test_data[i][3]);
}
}
cleanUp();
Expand Down
119 changes: 71 additions & 48 deletions Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe
private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 6; //0x06
private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 7; //0x07
private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 8; //0x08
private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 9; //0x09
// Top 32 commands are reserved for provisioning.
private static final byte INS_END_KM_PROVISION_CMD = 0x20;

Expand Down Expand Up @@ -467,6 +468,9 @@ && isProvisioningComplete())) {
case INS_GET_CERT_CHAIN_CMD:
processGetCertChainCmd(apdu);
break;
case INS_SET_VERSION_PATCHLEVEL_CMD:
processSetVersionAndPatchLevels(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
Expand Down Expand Up @@ -641,6 +645,47 @@ private void processAddRngEntropyCmd(APDU apdu) {
sendError(apdu, KMError.OK);
}

private void processSetVersionAndPatchLevels(APDU apdu) {
receiveIncoming(apdu);
byte[] scratchPad = apdu.getBuffer();
// Argument 1 OS Version
tmpVariables[0] = KMInteger.exp();
// Argument 2 OS Patch level
tmpVariables[1] = KMInteger.exp();
// Argument 3 Vendor Patch level
tmpVariables[2] = KMInteger.exp();
// Array of expected arguments
short argsProto = KMArray.instance((short) 3);
KMArray.cast(argsProto).add((short) 0, tmpVariables[0]);
KMArray.cast(argsProto).add((short) 1, tmpVariables[1]);
KMArray.cast(argsProto).add((short) 2, tmpVariables[2]);
// Decode the arguments
short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]);
//reclaim memory
repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]);

tmpVariables[0] = KMArray.cast(args).get((short) 0);
tmpVariables[1] = KMArray.cast(args).get((short) 1);
tmpVariables[2] = KMArray.cast(args).get((short) 2);

repository.setOsVersion(
KMInteger.cast(tmpVariables[0]).getBuffer(),
KMInteger.cast(tmpVariables[0]).getStartOff(),
KMInteger.cast(tmpVariables[0]).length());

repository.setOsPatch(
KMInteger.cast(tmpVariables[1]).getBuffer(),
KMInteger.cast(tmpVariables[1]).getStartOff(),
KMInteger.cast(tmpVariables[1]).length());

repository.setVendorPatchLevel(
KMInteger.cast(tmpVariables[2]).getBuffer(),
KMInteger.cast(tmpVariables[2]).getStartOff(),
KMInteger.cast(tmpVariables[2]).length());

sendError(apdu, KMError.OK);
}

private void processGetCertChainCmd(APDU apdu) {
// Make the response
tmpVariables[0] = seProvider.getCertificateChainLength();
Expand Down Expand Up @@ -3135,32 +3180,23 @@ private void updateKeyParameters(byte[] ptrArr, short len) {
private void processSetBootParamsCmd(APDU apdu) {
receiveIncoming(apdu);
byte[] scratchPad = apdu.getBuffer();
// Argument 1 OS Version
// Argument 0 Boot Patch level
tmpVariables[0] = KMInteger.exp();
// Argument 2 OS Patch level
tmpVariables[1] = KMInteger.exp();
// Argument 3 Vendor Patch level
tmpVariables[2] = KMInteger.exp();
// Argument 4 Boot Patch level
tmpVariables[3] = KMInteger.exp();
// Argument 5 Verified Boot Key
tmpVariables[4] = KMByteBlob.exp();
// Argument 6 Verified Boot Hash
tmpVariables[5] = KMByteBlob.exp();
// Argument 7 Verified Boot State
tmpVariables[6] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE);
// Argument 8 Device Locked
tmpVariables[7] = KMEnum.instance(KMType.DEVICE_LOCKED);
// Array of expected arguments
short argsProto = KMArray.instance((short) 8);
// Argument 1 Verified Boot Key
tmpVariables[1] = KMByteBlob.exp();
// Argument 2 Verified Boot Hash
tmpVariables[2] = KMByteBlob.exp();
// Argument 3 Verified Boot State
tmpVariables[3] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE);
// Argument 4 Device Locked
tmpVariables[4] = KMEnum.instance(KMType.DEVICE_LOCKED);
// Array of e4pected arguments
short argsProto = KMArray.instance((short) 5);
KMArray.cast(argsProto).add((short) 0, tmpVariables[0]);
KMArray.cast(argsProto).add((short) 1, tmpVariables[1]);
KMArray.cast(argsProto).add((short) 2, tmpVariables[2]);
KMArray.cast(argsProto).add((short) 3, tmpVariables[3]);
KMArray.cast(argsProto).add((short) 4, tmpVariables[4]);
KMArray.cast(argsProto).add((short) 5, tmpVariables[5]);
KMArray.cast(argsProto).add((short) 6, tmpVariables[6]);
KMArray.cast(argsProto).add((short) 7, tmpVariables[7]);
// Decode the arguments
short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]);
//reclaim memory
Expand All @@ -3171,51 +3207,38 @@ private void processSetBootParamsCmd(APDU apdu) {
tmpVariables[2] = KMArray.cast(args).get((short) 2);
tmpVariables[3] = KMArray.cast(args).get((short) 3);
tmpVariables[4] = KMArray.cast(args).get((short) 4);
tmpVariables[5] = KMArray.cast(args).get((short) 5);
tmpVariables[6] = KMArray.cast(args).get((short) 6);
tmpVariables[7] = KMArray.cast(args).get((short) 7);
if (KMByteBlob.cast(tmpVariables[4]).length() > KMRepository.BOOT_KEY_MAX_SIZE) {
if (KMByteBlob.cast(tmpVariables[1]).length() > KMRepository.BOOT_KEY_MAX_SIZE) {
KMException.throwIt(KMError.INVALID_ARGUMENT);
}
if (KMByteBlob.cast(tmpVariables[5]).length() > KMRepository.BOOT_HASH_MAX_SIZE) {
if (KMByteBlob.cast(tmpVariables[2]).length() > KMRepository.BOOT_HASH_MAX_SIZE) {
KMException.throwIt(KMError.INVALID_ARGUMENT);
}

repository.setOsVersion(
repository.setBootPatchLevel(
KMInteger.cast(tmpVariables[0]).getBuffer(),
KMInteger.cast(tmpVariables[0]).getStartOff(),
KMInteger.cast(tmpVariables[0]).length());
repository.setOsPatch(
KMInteger.cast(tmpVariables[1]).getBuffer(),
KMInteger.cast(tmpVariables[1]).getStartOff(),
KMInteger.cast(tmpVariables[1]).length());

repository.setVendorPatchLevel(
KMInteger.cast(tmpVariables[2]).getBuffer(),
KMInteger.cast(tmpVariables[2]).getStartOff(),
KMInteger.cast(tmpVariables[2]).length());

repository.setBootPatchLevel(
KMInteger.cast(tmpVariables[3]).getBuffer(),
KMInteger.cast(tmpVariables[3]).getStartOff(),
KMInteger.cast(tmpVariables[3]).length());

repository.setVerifiedBootKey(
KMByteBlob.cast(tmpVariables[4]).getBuffer(),
KMByteBlob.cast(tmpVariables[4]).getStartOff(),
KMByteBlob.cast(tmpVariables[4]).length());
KMByteBlob.cast(tmpVariables[1]).getBuffer(),
KMByteBlob.cast(tmpVariables[1]).getStartOff(),
KMByteBlob.cast(tmpVariables[1]).length());

repository.setVerifiedBootHash(
KMByteBlob.cast(tmpVariables[5]).getBuffer(),
KMByteBlob.cast(tmpVariables[5]).getStartOff(),
KMByteBlob.cast(tmpVariables[5]).length());
KMByteBlob.cast(tmpVariables[2]).getBuffer(),
KMByteBlob.cast(tmpVariables[2]).getStartOff(),
KMByteBlob.cast(tmpVariables[2]).length());

byte enumVal = KMEnum.cast(tmpVariables[6]).getVal();
byte enumVal = KMEnum.cast(tmpVariables[3]).getVal();
repository.setBootState(enumVal);

enumVal = KMEnum.cast(tmpVariables[7]).getVal();
enumVal = KMEnum.cast(tmpVariables[4]).getVal();
repository.setBootloaderLocked(enumVal == KMType.DEVICE_LOCKED_TRUE);

// Clear Android system properties expect boot patch level as it is
// already set.
repository.clearAndroidSystemProperties();

// Clear the Computed SharedHmac and Hmac nonce from persistent memory.
repository.clearComputedHmac();
repository.clearHmacNonce();
Expand Down
14 changes: 11 additions & 3 deletions Applet/src/com/android/javacard/keymaster/KMRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public class KMRepository implements KMUpgradable {
public static final byte CERT_ISSUER = 10;
public static final byte CERT_EXPIRY_TIME = 11;
public static final byte BOOT_OS_VERSION = 12;
public static final byte BOOT_OS_PATCH = 13;
public static final byte BOOT_OS_PATCH_LEVEL = 13;
public static final byte VENDOR_PATCH_LEVEL = 14;
public static final byte BOOT_PATCH_LEVEL = 15;
public static final byte BOOT_VERIFIED_BOOT_KEY = 16;
Expand Down Expand Up @@ -532,7 +532,7 @@ public short getBootPatchLevel() {
}

public short getOsPatch() {
short blob = readData(BOOT_OS_PATCH);
short blob = readData(BOOT_OS_PATCH_LEVEL);
if (blob != 0) {
return KMInteger.uint_32(
KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
Expand Down Expand Up @@ -638,6 +638,14 @@ public void setBootPatchLevel(byte[] buf, short start, short len) {
writeDataEntry(BOOT_PATCH_LEVEL, buf, start, len);
}

public void clearAndroidSystemProperties() {
clearDataEntry(BOOT_OS_VERSION);
clearDataEntry(BOOT_OS_PATCH_LEVEL);
clearDataEntry(VENDOR_PATCH_LEVEL);
// Don't clear BOOT_PATCH_LEVEL as it is part of
// boot parameters.
}

public void setBootloaderLocked(boolean flag) {
short start = alloc(DEVICE_LOCK_FLAG_SIZE);
if (flag) {
Expand Down Expand Up @@ -683,7 +691,7 @@ public void setOsPatch(byte[] buf, short start, short len) {
if (len != OS_PATCH_SIZE) {
KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
}
writeDataEntry(BOOT_OS_PATCH, buf, start, len);
writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len);
}

public void setVerifiedBootKey(byte[] buf, short start, short len) {
Expand Down
Loading