From 4a037fec9d1e76c4f4b736d644fb73c0149509bd Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Fri, 8 Apr 2022 00:38:16 +0000 Subject: [PATCH 1/9] Support for backward compatibility for KeyBlobs --- .../android/javacard/keymaster/KMDecoder.java | 17 + .../javacard/keymaster/KMKeymasterApplet.java | 395 +++++++++++------- 2 files changed, 252 insertions(+), 160 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 4cacce96..ac17b9eb 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -719,6 +719,23 @@ private void incrementStartOff(short inc) { } } + public short readKeyblobVersion(byte[] buf, short bufOffset, short bufLen) { + bufferRef[0] = buf; + scratchBuf[START_OFFSET] = bufOffset; + scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); + short arrayLen = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (arrayLen == 0) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short version = KMType.INVALID_VALUE; + try { + version = decodeInteger(KMInteger.exp()); + } catch(Exception e) { + // Fail to decode Integer. It can happen if it is an old KeyBlob. + } + return version; + } + public short readCertificateChainLengthAndHeaderLen(byte[] buf, short bufOffset, short bufLen) { bufferRef[0] = buf; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e9600777..6bd0b66f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -193,23 +193,30 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte TEE_PARAMETERS = 34; public static final byte SB_PARAMETERS = 35; public static final byte CONFIRMATION_TOKEN = 36; + public static final byte KEY_BLOB_VERSION_DATA_OFFSET = 37; // Constant // AddRngEntropy protected static final short MAX_SEED_SIZE = 2048; // Keyblob constants - public static final byte KEY_BLOB_SECRET = 0; - public static final byte KEY_BLOB_NONCE = 1; - public static final byte KEY_BLOB_AUTH_TAG = 2; - public static final byte KEY_BLOB_PARAMS = 3; - public static final byte KEY_BLOB_PUB_KEY = 4; + public static final byte KEY_BLOB_VERSION = 0; + public static final byte KEY_BLOB_SECRET = 1; + public static final byte KEY_BLOB_NONCE = 2; + public static final byte KEY_BLOB_AUTH_TAG = 3; + public static final byte KEY_BLOB_PARAMS = 4; + public static final byte KEY_BLOB_PUB_KEY = 5; // AES GCM constants public static final byte AES_GCM_AUTH_TAG_LENGTH = 16; public static final byte AES_GCM_NONCE_LENGTH = 12; // ComputeHMAC constants private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; protected static final short MAX_CERT_SIZE = 2048; + // Keyblob version goes into keyblob and will affect all + // the keyblobs if it is changed. + public static final short KEYBLOB_VERSION = 1; + public static final byte SYM_KEY_BLOB_SIZE = 5; + public static final byte ASYM_KEY_BLOB_SIZE = 6; protected static RemotelyProvisionedComponentDevice rkp; protected static KMEncoder encoder; @@ -699,10 +706,11 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { if (!KMByteBlob.cast(data[APP_DATA]).isValid()) { data[APP_DATA] = KMType.INVALID_VALUE; } - // Parse Key Blob - parseEncryptedKeyBlob(data[KEY_BLOB],data[APP_ID], data[APP_DATA], scratchPad); - // Check Version and Patch Level - checkVersionAndPatchLevel(scratchPad); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } // Remove custom tags from key characteristics short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); if(teeParams != KMType.INVALID_VALUE) { @@ -742,17 +750,35 @@ private short deleteKeyCmd(APDU apdu){ return receiveIncoming(apdu, cmd); } - private short keyBlob(){ - short keyBlob = KMArray.instance((short) 5); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + private short keyBlob(short version) { + short keyBlob; + short keyBlobExp = KMByteBlob.exp(); short keyChar = KMKeyCharacteristics.exp(); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + if (version == 0) { + // Old KeyBlobs have 5 elements. + keyBlob = KMArray.instance((short) 5); + KMArray.cast(keyBlob).add((short) 0, keyBlobExp); + KMArray.cast(keyBlob).add((short) 1, keyBlobExp); + KMArray.cast(keyBlob).add((short) 2, keyBlobExp); + KMArray.cast(keyBlob).add((short) 3, keyChar); + KMArray.cast(keyBlob).add((short) 4, keyBlobExp); + } else { + keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION, KMInteger.exp()); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, keyBlobExp); + } return keyBlob; } + private static short keyBlobInstance(boolean isSymmetricKey) { + byte arrayLen = isSymmetricKey ? SYM_KEY_BLOB_SIZE : ASYM_KEY_BLOB_SIZE; + return KMArray.instance(arrayLen); + } + private void processDeleteKeyCmd(APDU apdu) { // Send ok sendError(apdu, KMError.OK); @@ -892,27 +918,58 @@ private short upgradeKeyCmd(APDU apdu){ return receiveIncoming(apdu, cmd); } - private boolean isKeyUpgradeRequired(short tag, short systemParam) { - // validate the tag and check if key needs upgrade. - short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); - tagValue = KMIntegerTag.cast(tagValue).getValue(); - short zero = KMInteger.uint_8((byte) 0); - if (tagValue != 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(tagValue, systemParam) == 1 - && KMInteger.compare(systemParam, zero) == 0)) { - // Key needs upgrade. - return true; - } else if ((KMInteger.compare(tagValue, 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(tagValue, systemParam) == 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, byte[] scratchPad) { + // Check if the KeyBlob is compatible. If there is any change in the KeyBlob the version + // Parameter in the KeyBlob has should be updated to the next version. + short version = readKeyBlobVersion(keyBlob); + if (version > KEYBLOB_VERSION || version < 0) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + if (version == 0 || version < KEYBLOB_VERSION) { + return true; + } + parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version); + // Get boot patch level. + kmDataStore.getBootPatchLevel(scratchPad, (short) 0); + short bootPatchLevel = KMInteger.uint_32(scratchPad, (short) 0); + // Fill the key-value properties in the scratchpad + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0); + Util.setShort(scratchPad, (short) 0, KMType.OS_VERSION); + Util.setShort(scratchPad, (short) 2, kmDataStore.getOsVersion()); + Util.setShort(scratchPad, (short) 4, KMType.OS_PATCH_LEVEL); + Util.setShort(scratchPad, (short) 6, kmDataStore.getOsPatch()); + Util.setShort(scratchPad, (short) 8, KMType.VENDOR_PATCH_LEVEL); + Util.setShort(scratchPad, (short) 10, kmDataStore.getVendorPatchLevel()); + Util.setShort(scratchPad, (short) 12, KMType.BOOT_PATCH_LEVEL); + Util.setShort(scratchPad, (short) 14, bootPatchLevel); + short index = 0; + short tag; + short systemParam; + while(index < 16) { + tag = Util.getShort(scratchPad, index); + systemParam = Util.getShort(scratchPad, (short) (index + 2)); + // validate the tag and check if key needs upgrade. + short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); + tagValue = KMIntegerTag.cast(tagValue).getValue(); + short zero = KMInteger.uint_8((byte) 0); + if (tagValue != 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(tagValue, systemParam) == 1 + && KMInteger.compare(systemParam, zero) == 0)) { + // Key needs upgrade. + return true; + } else if ((KMInteger.compare(tagValue, 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(tagValue, systemParam) == 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } } + index += 4; } return false; } @@ -922,7 +979,7 @@ private void processUpgradeKeyCmd(APDU apdu) { short cmd = upgradeKeyCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); + short keyBlob = KMArray.cast(cmd).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); //tmpVariables[0] short appId = @@ -935,18 +992,19 @@ private void processUpgradeKeyCmd(APDU apdu) { if (appData != KMTag.INVALID_VALUE) { data[APP_DATA] = KMByteTag.cast(appData).getValue(); } - // parse existing key blob - parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); - boolean isKeyUpgradeRequired = false; - // Check if key requires upgrade. - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, kmDataStore.getOsVersion()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, kmDataStore.getOsPatch()); - isKeyUpgradeRequired |= - isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, kmDataStore.getVendorPatchLevel()); - // Get boot patch level. - kmDataStore.getBootPatchLevel(scratchPad, (short) 0); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, - KMInteger.uint_32(scratchPad, (short) 0)); + + data[KEY_BLOB] = KMType.INVALID_VALUE; + // Check if the KeyBlob requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself, but if there is a difference in the KeyBlob version isKeyUpgradeRequired() + // does not parse the KeyBlob. + boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad); + // Check if KeyBlob is already parsed in isKeyUpgradeRequired() function, If not parsed + // parse the KeyBlob. + if (data[KEY_BLOB] == KMType.INVALID_VALUE) { + // Parse existing key blob + parseEncryptedKeyBlob(keyBlob, data[APP_ID], data[APP_DATA], scratchPad, + readKeyBlobVersion(keyBlob)); + } if (isKeyUpgradeRequired) { // copy origin @@ -983,8 +1041,11 @@ private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scra data[APP_DATA] = appData; data[KEY_PARAMETERS] = wrapParams; data[KEY_BLOB] = keyBlob; - // parse the wrapping key blob - parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } validateWrappingKeyBlob(); } @@ -1238,9 +1299,13 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara short origBlob = data[KEY_BLOB]; short pubKey = data[PUB_KEY]; short privKey = data[SECRET]; - short keyBlob = parseEncryptedKeyBlob(attKeyBlob, appId, appData, scratchPad); - short attestationKeySecret = KMArray.cast(keyBlob).get(KEY_BLOB_SECRET); - short attestParam = KMArray.cast(keyBlob).get(KEY_BLOB_PARAMS); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(attKeyBlob, appId, appData, scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + short attestationKeySecret = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); + short attestParam = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PARAMS); attestParam = KMKeyCharacteristics.cast(attestParam).getStrongboxEnforced(); short attKeyPurpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestParam); // If the attest key's purpose is not "attest key" then error. @@ -1254,7 +1319,7 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, attestParam); if (KMEnumTag.cast(alg).getValue() == KMType.RSA) { - short attestationKeyPublic = KMArray.cast(keyBlob).get(KEY_BLOB_PUB_KEY); + short attestationKeyPublic = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); cert.rsaAttestKey(attestationKeySecret, attestationKeyPublic, KMType.ATTESTATION_CERT); } else { cert.ecAttestKey(attestationKeySecret, KMType.ATTESTATION_CERT); @@ -2038,8 +2103,11 @@ private void processBeginOperationCmd(APDU apdu) { if (data[APP_DATA] != KMTag.INVALID_VALUE) { data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); } - // Parse the encrypted blob and decrypt it. - parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } KMTag.assertPresence(data[SB_PARAMETERS],KMType.ENUM_TAG,KMType.ALGORITHM,KMError.UNSUPPORTED_ALGORITHM); short algorithm = KMEnumTag.getValue(KMType.ALGORITHM,data[SB_PARAMETERS]); // If Blob usage tag is present in key characteristics then it should be standalone. @@ -2982,7 +3050,7 @@ private void importECKeys(byte[] scratchPad) { // add scratch pad to key parameters updateKeyParameters(scratchPad, index); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3024,7 +3092,7 @@ private void importHmacKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate HMAC Key parameters validateHmacKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); } private void importTDESKey(byte[] scratchPad) { @@ -3064,7 +3132,7 @@ private void importTDESKey(byte[] scratchPad) { KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list updateKeyParameters(scratchPad, index); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); } private void validateAesKeySize(short keySizeBits) { @@ -3107,7 +3175,7 @@ private void importAESKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate AES Key parameters validateAESKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); } private void importRSAKey(byte[] scratchPad) { @@ -3183,7 +3251,7 @@ private void importRSAKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate RSA Key parameters validateRSAKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3539,7 +3607,7 @@ private static void generateRSAKey(byte[] scratchPad) { KMByteBlob.cast(data[PUB_KEY]).length(), lengths); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3578,7 +3646,7 @@ private static void generateAESKey(byte[] scratchPad) { short len = seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); } private static void validateECKeys() { @@ -3600,7 +3668,7 @@ private static void generateECKeys(byte[] scratchPad) { (short) 128, lengths); data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); - data[KEY_BLOB] = KMArray.instance((short) 5); + data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3616,7 +3684,7 @@ private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(); short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); } private static void validateHmacKey() { @@ -3650,48 +3718,9 @@ private static void generateHmacKey(byte[] scratchPad) { // generate HMAC Key short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = KMArray.instance((short) 4); + data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); } - private void checkVersionAndPatchLevel(byte[] scratchPad) { - short len = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - if (len != KMType.INVALID_VALUE) { - short provOsVersion = kmDataStore.getOsVersion(); - short status = - KMInteger.unsignedByteArrayCompare( - KMInteger.cast(provOsVersion).getBuffer(), - KMInteger.cast(provOsVersion).getStartOff(), - scratchPad, - (short) 0, - len); - if (status == -1) { - // If the key characteristics has os version > current os version - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (status == 1) { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } - len = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - if (len != KMType.INVALID_VALUE) { - short osPatch = kmDataStore.getOsPatch(); - short status = - KMInteger.unsignedByteArrayCompare( - KMInteger.cast(osPatch).getBuffer(), - KMInteger.cast(osPatch).getStartOff(), - scratchPad, - (short) 0, - len); - if (status == -1) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (status == 1) { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } - } protected static short getBootPatchLevel(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad,(short)0, BOOT_PATCH_LVL_SIZE, (byte)0); short len = kmDataStore.getBootPatchLevel(scratchPad,(short)0); @@ -3726,16 +3755,17 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { // make hidden key params list data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_VERSION); // make authorization data - makeAuthData(scratchPad); + makeAuthData(KEYBLOB_VERSION); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); // create key blob array KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_VERSION, data[KEY_BLOB_VERSION_DATA_OFFSET]); - //TODO remove the following temporary creation of keyblob. short tempChar = KMKeyCharacteristics.instance(); short emptyParam = KMArray.instance((short) 0); emptyParam = KMKeyParameters.instance(emptyParam); @@ -3743,7 +3773,6 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam); KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar); - //KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, data[KEY_CHARACTERISTICS]); // allocate reclaimable memory. short buffer = repository.alloc((short) 1024); @@ -3751,43 +3780,91 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), buffer, keyBlob); } - private short parseEncryptedKeyBlob(short keyBlob, short appId, short appData, byte[] scratchPad) { - short parsedBlob = KMType.INVALID_VALUE; - short rot = readROT(scratchPad); - if (rot == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } + private short readKeyBlobVersion(short keyBlob) { + short version = KMType.INVALID_VALUE; try { - parsedBlob = decoder.decodeArray(keyBlob(), - KMByteBlob.cast(keyBlob).getBuffer(), - KMByteBlob.cast(keyBlob).getStartOff(), - KMByteBlob.cast(keyBlob).length()); - if (KMArray.cast(parsedBlob).length() < 4) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); + version = decoder.readKeyblobVersion( + KMByteBlob.cast(keyBlob).getBuffer(), + KMByteBlob.cast(keyBlob).getStartOff(), + KMByteBlob.cast(keyBlob).length()); + if (version == KMType.INVALID_VALUE) { + // If Version is not present. Then it is either an old KeyBlob or + // corrupted KeyBlob. + version = 0; } + version = KMInteger.cast(version).getShort(); + } catch(Exception e) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + return version; + } - // initialize data - data[SECRET] = KMArray.cast(parsedBlob).get(KEY_BLOB_SECRET); - data[NONCE]= KMArray.cast(parsedBlob).get(KEY_BLOB_NONCE); - data[AUTH_TAG] = KMArray.cast(parsedBlob).get(KEY_BLOB_AUTH_TAG); - data[KEY_CHARACTERISTICS] = KMArray.cast(parsedBlob).get(KEY_BLOB_PARAMS); + private void readKeyBlobParams(short version, short parsedKeyBlob) { + data[KEY_BLOB] = parsedKeyBlob; + // initialize data + if (version == 0) { + data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0); + data[NONCE]= KMArray.cast(parsedKeyBlob).get((short) 1); + data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3); data[PUB_KEY] = KMType.INVALID_VALUE; - if (KMArray.cast(parsedBlob).length() == 5) { - data[PUB_KEY] = KMArray.cast(parsedBlob).get(KEY_BLOB_PUB_KEY); + if (KMArray.cast(parsedKeyBlob).length() == (short) 5) { + data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4); } + } else { + data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET); + data[NONCE]= KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE); + data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE) { + data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY); + } + } + } + + private short decodeKeyBlob(short version, short keyBlob) { + // Decode KeyBlob and read the KeyBlob params based on the version. + short parsedBlob = decoder.decodeArray(keyBlob(version), + KMByteBlob.cast(keyBlob).getBuffer(), + KMByteBlob.cast(keyBlob).getStartOff(), + KMByteBlob.cast(keyBlob).length()); + short minArraySize = (version > 0) ? SYM_KEY_BLOB_SIZE : (short) 4; + // KeyBlob size should not be less than the minimum KeyBlob size. + if (KMArray.cast(parsedBlob).length() < minArraySize) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + readKeyBlobParams(version, parsedBlob); + return parsedBlob; + } - data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); - data[SB_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced(); - data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced(); - data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); - - data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, rot, scratchPad); - data[KEY_BLOB] = parsedBlob; - // make auth data - makeAuthData(scratchPad); - // Decrypt Secret and verify auth tag - decryptSecret(scratchPad); - KMArray.cast(parsedBlob).add(KEY_BLOB_SECRET, data[SECRET]); + private void processDecryptSecret(short version, short appId, short appData, byte[] scratchPad) { + data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); + data[SB_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced(); + data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced(); + data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); + + data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad); + // make auth data + makeAuthData(version); + // Decrypt Secret and verify auth tag + decryptSecret(scratchPad); + short keyBlobSecretOff = (version > 0) ? KEY_BLOB_SECRET : (short) 0; + KMArray.cast(data[KEY_BLOB]).add(keyBlobSecretOff, data[SECRET]); + } + + private short parseEncryptedKeyBlob(short keyBlob, short appId, short appData, + byte[] scratchPad, short version) { + short parsedBlob = KMType.INVALID_VALUE; + // make root of trust blob + data[ROT] = readROT(scratchPad); + if (data[ROT] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + try { + parsedBlob = decodeKeyBlob(version, keyBlob); + processDecryptSecret(version, appId, appData, scratchPad); } catch (Exception e) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } @@ -3837,14 +3914,8 @@ private void decryptSecret(byte[] scratchPad) { private static void encryptSecret(byte[] scratchPad) { // make nonce - data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); + data[NONCE] = KMByteBlob.instance(AES_GCM_NONCE_LENGTH); data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).length()); seProvider.newRandomNumber( KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), @@ -3876,31 +3947,35 @@ private static void encryptSecret(byte[] scratchPad) { data[SECRET] = KMByteBlob.instance(scratchPad, (short)0, len); } - private static void makeAuthData(byte[] scratchPad) { - short arrayLen = 2; - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - arrayLen = 3; + private static void makeAuthData(short version) { + short pos = 0; + short arrayLen = (version > 0) ? (short) 3 : (short) 2; + short maxArraySize = (version > 0) ? (short) ASYM_KEY_BLOB_SIZE : (short) 5; + if (KMArray.cast(data[KEY_BLOB]).length() == maxArraySize) { + arrayLen += 1; + } + short params = KMArray.instance(arrayLen); + KMArray.cast(params).add(pos++, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + KMArray.cast(params).add(pos++, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (version > 0) { + KMArray.cast(params).add(pos++, data[KEY_BLOB_VERSION_DATA_OFFSET]); } - short params = KMArray.instance((short) arrayLen); - KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); - // KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); - if (3 == arrayLen) { - KMArray.cast(params).add((short) 2, data[PUB_KEY]); + if (KMArray.cast(data[KEY_BLOB]).length() == maxArraySize) { + KMArray.cast(params).add(pos, data[PUB_KEY]); } short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE); short index = 0; short len = 0; short paramsLen = KMArray.cast(params).length(); - Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0); while (index < paramsLen) { short tag = KMArray.cast(params).get(index); len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); - Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + Util.arrayCopyNonAtomic(repository.getHeap(), authIndex, repository.getHeap(), (short) (authIndex + len + 32), (short) 32); len = seProvider.messageDigest256(repository.getHeap(), - (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), authIndex); if (len != 32) { KMException.throwIt(KMError.UNKNOWN_ERROR); } From 6ebee0e9a9f085afd833827734af0a1cb4eddfa6 Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Fri, 8 Apr 2022 01:37:20 +0000 Subject: [PATCH 2/9] Corrected the comment --- .../src/com/android/javacard/keymaster/KMKeymasterApplet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 6bd0b66f..8da171a7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -919,8 +919,8 @@ private short upgradeKeyCmd(APDU apdu){ } private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, byte[] scratchPad) { - // Check if the KeyBlob is compatible. If there is any change in the KeyBlob the version - // Parameter in the KeyBlob has should be updated to the next version. + // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version + // Parameter in the KeyBlob should be updated to the next version. short version = readKeyBlobVersion(keyBlob); if (version > KEYBLOB_VERSION || version < 0) { KMException.throwIt(KMError.INVALID_KEY_BLOB); From 072ca0f0bd552e07ad509a6fc3f4dd9b36c396ae Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Fri, 8 Apr 2022 04:20:14 +0000 Subject: [PATCH 3/9] Updated review comments --- .../javacard/keymaster/KMKeymasterApplet.java | 258 ++++++++++-------- 1 file changed, 148 insertions(+), 110 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 8da171a7..e0b1ae32 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -199,7 +199,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // AddRngEntropy protected static final short MAX_SEED_SIZE = 2048; - // Keyblob constants + // Keyblob offsets. public static final byte KEY_BLOB_VERSION = 0; public static final byte KEY_BLOB_SECRET = 1; public static final byte KEY_BLOB_NONCE = 2; @@ -215,8 +215,11 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // Keyblob version goes into keyblob and will affect all // the keyblobs if it is changed. public static final short KEYBLOB_VERSION = 1; - public static final byte SYM_KEY_BLOB_SIZE = 5; - public static final byte ASYM_KEY_BLOB_SIZE = 6; + // KeyBlob array size constants. + public static final byte SYM_KEY_BLOB_SIZE_V1 = 5; + public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6; + public static final byte SYM_KEY_BLOB_SIZE_V0 = 4; + public static final byte ASYM_KEY_BLOB_SIZE_V0 = 5; protected static RemotelyProvisionedComponentDevice rkp; protected static KMEncoder encoder; @@ -229,7 +232,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static short[] tmpVariables; protected static short[] data; protected static byte[] wrappingKey; - + /** * Registers this applet. */ @@ -696,9 +699,9 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); data[APP_ID] = KMArray.cast(cmd).get((short) 1); data[APP_DATA] = KMArray.cast(cmd).get((short) 2); - if (KMByteBlob.cast(data[APP_ID]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE + if (KMByteBlob.cast(data[APP_ID]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE || KMByteBlob.cast(data[APP_DATA]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if (!KMByteBlob.cast(data[APP_ID]).isValid()) { data[APP_ID] = KMType.INVALID_VALUE; @@ -751,31 +754,36 @@ private short deleteKeyCmd(APDU apdu){ } private short keyBlob(short version) { - short keyBlob; + short keyBlob = KMType.INVALID_VALUE; short keyBlobExp = KMByteBlob.exp(); short keyChar = KMKeyCharacteristics.exp(); - if (version == 0) { - // Old KeyBlobs have 5 elements. - keyBlob = KMArray.instance((short) 5); - KMArray.cast(keyBlob).add((short) 0, keyBlobExp); - KMArray.cast(keyBlob).add((short) 1, keyBlobExp); - KMArray.cast(keyBlob).add((short) 2, keyBlobExp); - KMArray.cast(keyBlob).add((short) 3, keyChar); - KMArray.cast(keyBlob).add((short) 4, keyBlobExp); - } else { - keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION, KMInteger.exp()); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, keyBlobExp); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, keyBlobExp); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, keyBlobExp); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, keyBlobExp); + switch(version) { + case (short) 0: + // Old KeyBlob has a maximum of 5 elements. + keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V0); + KMArray.cast(keyBlob).add((short) 0, keyBlobExp); + KMArray.cast(keyBlob).add((short) 1, keyBlobExp); + KMArray.cast(keyBlob).add((short) 2, keyBlobExp); + KMArray.cast(keyBlob).add((short) 3, keyChar); + KMArray.cast(keyBlob).add((short) 4, keyBlobExp); + break; + case (short) 1: + keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION, KMInteger.exp()); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, keyBlobExp); + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); } return keyBlob; } - private static short keyBlobInstance(boolean isSymmetricKey) { - byte arrayLen = isSymmetricKey ? SYM_KEY_BLOB_SIZE : ASYM_KEY_BLOB_SIZE; + private static short createKeyBlobInstance(boolean isSymmetricKey) { + byte arrayLen = isSymmetricKey ? SYM_KEY_BLOB_SIZE_V1 : ASYM_KEY_BLOB_SIZE_V1; return KMArray.instance(arrayLen); } @@ -1025,6 +1033,7 @@ private void processUpgradeKeyCmd(APDU apdu) { private void processExportKeyCmd(APDU apdu) { sendError(apdu, KMError.UNIMPLEMENTED); } + private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scratchPad) { // Read App Id and App Data if any from un wrapping key params short appId = @@ -1956,7 +1965,7 @@ private void processUpdateOperationCmd(APDU apdu) { KMByteBlob.cast(data[INPUT_DATA]).length()); // update trusted confirmation operation updateTrustedConfirmationOperation(op); - + data[OUTPUT_DATA] = KMType.INVALID_VALUE; } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { // Update for encrypt/decrypt using RSA will not be supported because to do this op state @@ -2454,13 +2463,13 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); } - - //Validate bootloader only + + //Validate bootloader only if (kmDataStore.getBootEndedStatus()) { KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); } - + // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in // key params then fail if it is not a Decrypt operation data[IV] = KMType.INVALID_VALUE; @@ -2607,11 +2616,11 @@ private void beginTrustedConfirmationOperation(KMOperationState op) { op.getTrustedConfirmationSigner().update( confirmationToken, (short) 0, - (short) confirmationToken.length); + (short) confirmationToken.length); } - + } - + private void beginSignVerifyOperation(KMOperationState op) { switch (op.getAlgorithm()) { case KMType.RSA: @@ -2920,7 +2929,7 @@ private void validateImportKey(short params, short keyFmt){ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, params); // ATTEST_KEY cannot be combined with any other purpose. if (attKeyPurpose != KMType.INVALID_VALUE - && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } @@ -2932,7 +2941,7 @@ private void validateImportKey(short params, short keyFmt){ //Check if the tags are supported. if (KMKeyParameters.hasUnsupportedTags(params)) { KMException.throwIt(KMError.UNSUPPORTED_TAG); - } + } // Algorithm must be present KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); @@ -3050,7 +3059,7 @@ private void importECKeys(byte[] scratchPad) { // add scratch pad to key parameters updateKeyParameters(scratchPad, index); - data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3092,7 +3101,7 @@ private void importHmacKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate HMAC Key parameters validateHmacKey(); - data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); } private void importTDESKey(byte[] scratchPad) { @@ -3132,7 +3141,7 @@ private void importTDESKey(byte[] scratchPad) { KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list updateKeyParameters(scratchPad, index); - data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); } private void validateAesKeySize(short keySizeBits) { @@ -3152,7 +3161,7 @@ private void importAESKey(byte[] scratchPad) { if (keysize != KMType.INVALID_VALUE) { if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } + } validateAesKeySize(keysize); } else { // add the key size to scratchPad @@ -3175,7 +3184,7 @@ private void importAESKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate AES Key parameters validateAESKey(); - data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); } private void importRSAKey(byte[] scratchPad) { @@ -3229,7 +3238,7 @@ private void importRSAKey(byte[] scratchPad) { } else { if (2048 != kSize) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } + } // add the key size to scratchPad keysize = KMInteger.uint_16((short) 2048); keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); @@ -3251,7 +3260,7 @@ private void importRSAKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate RSA Key parameters validateRSAKey(scratchPad); - data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3317,7 +3326,7 @@ public void reboot() { //flag to maintain the boot state kmDataStore.setBootEndedStatus(false); //flag to maintain early boot ended state - kmDataStore.setEarlyBootEndedStatus(false); + kmDataStore.setEarlyBootEndedStatus(false); //Clear all the operation state. releaseAllOperations(); // Hmac is cleared, so generate a new Hmac nonce. @@ -3373,11 +3382,11 @@ private void processGenerateKey(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0); // ROLLBACK_RESISTANCE not supported. KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - + // As per specification Early boot keys may be created after early boot ended. // Algorithm must be present KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); - + //Check if the tags are supported. if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { KMException.throwIt(KMError.UNSUPPORTED_TAG); @@ -3386,7 +3395,7 @@ private void processGenerateKey(APDU apdu) { KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); // ATTEST_KEY cannot be combined with any other purpose. if (attKeyPurpose != KMType.INVALID_VALUE - && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } @@ -3453,47 +3462,48 @@ public void getAttestKeyInputParameters(short arrPtr, short[] data, byte keyBlob data[attestKeyParamsOff] = KMType.INVALID_VALUE; data[attestKeyIssuerOff] = KMType.INVALID_VALUE; } - + private void processAttestKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. - short cmd = generateAttestKeyCmd(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); + short cmd = generateAttestKeyCmd(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 2); data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 3); data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 4); - - data[CERTIFICATE] = KMArray.instance((short) 0); //by default the cert is empty. + + data[CERTIFICATE] = KMArray.instance((short) 0); // by default the cert is empty. // Check for app id and app data. - data[APP_ID] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - data[APP_DATA] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + data[APP_ID] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + data[APP_DATA] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); if (data[APP_ID] != KMTag.INVALID_VALUE) { data[APP_ID] = KMByteTag.cast(data[APP_ID]).getValue(); } if (data[APP_DATA] != KMTag.INVALID_VALUE) { data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); } - // parse key blob - parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired + // function itself. + if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } // The key which is being attested should be asymmetric i.e. RSA or EC short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); if (alg != KMType.RSA && alg != KMType.EC) { KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); } - // Build certificate - generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad); + // Build certificate + generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad); - short resp = KMArray.instance((short) 2); - KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(resp).add((short) 1, data[CERTIFICATE]); - sendOutgoing(apdu, resp); + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[CERTIFICATE]); + sendOutgoing(apdu, resp); } - + private short getAttestationMode(short attKeyBlob, short attChallenge){ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); short mode = KMType.NO_CERT; @@ -3607,7 +3617,7 @@ private static void generateRSAKey(byte[] scratchPad) { KMByteBlob.cast(data[PUB_KEY]).length(), lengths); - data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3646,7 +3656,7 @@ private static void generateAESKey(byte[] scratchPad) { short len = seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); } private static void validateECKeys() { @@ -3668,7 +3678,7 @@ private static void generateECKeys(byte[] scratchPad) { (short) 128, lengths); data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); - data[KEY_BLOB] = keyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3684,7 +3694,7 @@ private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(); short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); } private static void validateHmacKey() { @@ -3718,7 +3728,7 @@ private static void generateHmacKey(byte[] scratchPad) { // generate HMAC Key short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = keyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); } protected static short getBootPatchLevel(byte[] scratchPad){ @@ -3757,7 +3767,7 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_VERSION); // make authorization data - makeAuthData(KEYBLOB_VERSION); + makeAuthData(KEYBLOB_VERSION, scratchPad); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); // create key blob array @@ -3802,25 +3812,30 @@ private short readKeyBlobVersion(short keyBlob) { private void readKeyBlobParams(short version, short parsedKeyBlob) { data[KEY_BLOB] = parsedKeyBlob; // initialize data - if (version == 0) { - data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0); - data[NONCE]= KMArray.cast(parsedKeyBlob).get((short) 1); - data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2); - data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3); - data[PUB_KEY] = KMType.INVALID_VALUE; - if (KMArray.cast(parsedKeyBlob).length() == (short) 5) { - data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4); - } - } else { - data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET); - data[NONCE]= KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE); - data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG); - data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS); - data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION); - data[PUB_KEY] = KMType.INVALID_VALUE; - if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE) { - data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY); - } + switch (version) { + case (short) 0: + data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0); + data[NONCE]= KMArray.cast(parsedKeyBlob).get((short) 1); + data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V0) { + data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4); + } + break; + case (short) 1: + data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET); + data[NONCE]= KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE); + data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) { + data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY); + } + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); } } @@ -3830,7 +3845,7 @@ private short decodeKeyBlob(short version, short keyBlob) { KMByteBlob.cast(keyBlob).getBuffer(), KMByteBlob.cast(keyBlob).getStartOff(), KMByteBlob.cast(keyBlob).length()); - short minArraySize = (version > 0) ? SYM_KEY_BLOB_SIZE : (short) 4; + short minArraySize = (version > 0) ? SYM_KEY_BLOB_SIZE_V1 : (short) 4; // KeyBlob size should not be less than the minimum KeyBlob size. if (KMArray.cast(parsedBlob).length() < minArraySize) { KMException.throwIt(KMError.INVALID_KEY_BLOB); @@ -3847,7 +3862,7 @@ private void processDecryptSecret(short version, short appId, short appData, byt data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad); // make auth data - makeAuthData(version); + makeAuthData(version, scratchPad); // Decrypt Secret and verify auth tag decryptSecret(scratchPad); short keyBlobSecretOff = (version > 0) ? KEY_BLOB_SECRET : (short) 0; @@ -3884,7 +3899,7 @@ public static short readROT(byte[] scratchPad) { scratchPad[len] = (byte)0; } len++; - return KMByteBlob.instance(scratchPad,(short)0,len); + return KMByteBlob.instance(scratchPad, (short) 0, len); } private void decryptSecret(byte[] scratchPad) { @@ -3947,25 +3962,48 @@ private static void encryptSecret(byte[] scratchPad) { data[SECRET] = KMByteBlob.instance(scratchPad, (short)0, len); } - private static void makeAuthData(short version) { - short pos = 0; - short arrayLen = (version > 0) ? (short) 3 : (short) 2; - short maxArraySize = (version > 0) ? (short) ASYM_KEY_BLOB_SIZE : (short) 5; - if (KMArray.cast(data[KEY_BLOB]).length() == maxArraySize) { - arrayLen += 1; + private static void makeAuthData(short version, byte[] scratchPad) { + // Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY. + // VERSION is included only for KeyBlobs having version >= 1. + // PUB_KEY is included for only ASYMMETRIC KeyBlobs. + short index = 0; + short arrayLen = 0; + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); + switch (version) { + case (short) 0: + // If the KeyBlob size is 5 then it contains an Asymmetric Key, otherwise + // it contains a Symmetric Key. For Asymmetric Keys include the PUB_KEY. + arrayLen = 2; + if (KMArray.cast(data[KEY_BLOB]).length() == ASYM_KEY_BLOB_SIZE_V0) { + arrayLen = 3; + Util.setShort(scratchPad, (short) 4, data[PUB_KEY]); + } + Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + break; + case (short) 1: + // If the KeyBlob size is 6 then it contains an Asymmetric Key, otherwise + // it contains a Symmetric Key. For Asymmetric Keys include the PUB_KEY. + arrayLen = 3; + if (KMArray.cast(data[KEY_BLOB]).length() == ASYM_KEY_BLOB_SIZE_V1) { + arrayLen = 4; + Util.setShort(scratchPad, (short) 6, data[PUB_KEY]); + } + Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + Util.setShort(scratchPad, (short) 4, data[KEY_BLOB_VERSION_DATA_OFFSET]); + break; + default: + KMException.throwIt(KMError.INVALID_KEY_BLOB); } short params = KMArray.instance(arrayLen); - KMArray.cast(params).add(pos++, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); - KMArray.cast(params).add(pos++, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); - if (version > 0) { - KMArray.cast(params).add(pos++, data[KEY_BLOB_VERSION_DATA_OFFSET]); - } - if (KMArray.cast(data[KEY_BLOB]).length() == maxArraySize) { - KMArray.cast(params).add(pos, data[PUB_KEY]); + while (index < arrayLen) { + KMArray.cast(params).add(index, Util.getShort(scratchPad, (short) (index * 2))); + index++; } short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE); - short index = 0; + index = 0; short len = 0; short paramsLen = KMArray.cast(params).length(); Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0); @@ -4161,7 +4199,7 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); encodedLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - + short signatureLen = rkp.encodeES256CoseSignSignature( KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), @@ -4169,7 +4207,7 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)), scratchPad, (short) (keySize + encodedLen)); - + if (!seProvider.ecVerify256(scratchPad, (short) 0, keySize, scratchPad, keySize, encodedLen, scratchPad, (short) (keySize + encodedLen), signatureLen)) { KMException.throwIt(KMError.STATUS_FAILED); @@ -4290,7 +4328,7 @@ private void finishTrustedConfirmationOperation(KMOperationState op) { } } } - + private void processGetHeapProfileData(APDU apdu) { // No Arguments // prepare the response From a962bfec8b71284b9881da8b084486fe71496a50 Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Fri, 8 Apr 2022 04:25:10 +0000 Subject: [PATCH 4/9] Renamed KEY_BLOB_VERSION to KEY_BLOB_VERSION_OFFSET --- .../android/javacard/keymaster/KMKeymasterApplet.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e0b1ae32..71cdabf4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -200,7 +200,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static final short MAX_SEED_SIZE = 2048; // Keyblob offsets. - public static final byte KEY_BLOB_VERSION = 0; + public static final byte KEY_BLOB_VERSION_OFFSET = 0; public static final byte KEY_BLOB_SECRET = 1; public static final byte KEY_BLOB_NONCE = 2; public static final byte KEY_BLOB_AUTH_TAG = 3; @@ -769,7 +769,7 @@ private short keyBlob(short version) { break; case (short) 1: keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION, KMInteger.exp()); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp()); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, keyBlobExp); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, keyBlobExp); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, keyBlobExp); @@ -3774,7 +3774,7 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_VERSION, data[KEY_BLOB_VERSION_DATA_OFFSET]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_VERSION_OFFSET, data[KEY_BLOB_VERSION_DATA_OFFSET]); short tempChar = KMKeyCharacteristics.instance(); short emptyParam = KMArray.instance((short) 0); @@ -3828,7 +3828,8 @@ private void readKeyBlobParams(short version, short parsedKeyBlob) { data[NONCE]= KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE); data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG); data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS); - data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get( + KEY_BLOB_VERSION_OFFSET); data[PUB_KEY] = KMType.INVALID_VALUE; if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) { data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY); From 0c54e402df54080b234de51d14821881eb8b20d0 Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Fri, 8 Apr 2022 04:27:53 +0000 Subject: [PATCH 5/9] Renamed keyblob to createKeyBlobExp --- .../src/com/android/javacard/keymaster/KMKeymasterApplet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 71cdabf4..9fcb201f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -753,7 +753,7 @@ private short deleteKeyCmd(APDU apdu){ return receiveIncoming(apdu, cmd); } - private short keyBlob(short version) { + private short createKeyBlobExp(short version) { short keyBlob = KMType.INVALID_VALUE; short keyBlobExp = KMByteBlob.exp(); short keyChar = KMKeyCharacteristics.exp(); @@ -3842,7 +3842,7 @@ private void readKeyBlobParams(short version, short parsedKeyBlob) { private short decodeKeyBlob(short version, short keyBlob) { // Decode KeyBlob and read the KeyBlob params based on the version. - short parsedBlob = decoder.decodeArray(keyBlob(version), + short parsedBlob = decoder.decodeArray(createKeyBlobExp(version), KMByteBlob.cast(keyBlob).getBuffer(), KMByteBlob.cast(keyBlob).getStartOff(), KMByteBlob.cast(keyBlob).length()); From 9623e80dea17f75770c08a59a90b4ba3b5e10805 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Fri, 8 Apr 2022 17:17:44 +0000 Subject: [PATCH 6/9] bugfix in keyblob version changes --- .../javacard/keymaster/KMKeymasterApplet.java | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 9fcb201f..584d2da5 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -930,10 +930,7 @@ private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version // Parameter in the KeyBlob should be updated to the next version. short version = readKeyBlobVersion(keyBlob); - if (version > KEYBLOB_VERSION || version < 0) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - if (version == 0 || version < KEYBLOB_VERSION) { + if (version < KEYBLOB_VERSION) { return true; } parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version); @@ -976,6 +973,8 @@ private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, } else if (KMInteger.compare(tagValue, systemParam) == 1) { KMException.throwIt(KMError.INVALID_ARGUMENT); } + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); } index += 4; } @@ -1006,15 +1005,14 @@ private void processUpgradeKeyCmd(APDU apdu) { // function itself, but if there is a difference in the KeyBlob version isKeyUpgradeRequired() // does not parse the KeyBlob. boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad); - // Check if KeyBlob is already parsed in isKeyUpgradeRequired() function, If not parsed - // parse the KeyBlob. - if (data[KEY_BLOB] == KMType.INVALID_VALUE) { - // Parse existing key blob - parseEncryptedKeyBlob(keyBlob, data[APP_ID], data[APP_DATA], scratchPad, - readKeyBlobVersion(keyBlob)); - } - if (isKeyUpgradeRequired) { + // Check if KeyBlob is already parsed in isKeyUpgradeRequired() function, If not parsed + // parse the KeyBlob. + if (data[KEY_BLOB] == KMType.INVALID_VALUE) { + // Parse existing key blob + parseEncryptedKeyBlob(keyBlob, data[APP_ID], data[APP_DATA], scratchPad, + readKeyBlobVersion(keyBlob)); + } // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); makeKeyCharacteristics(scratchPad); @@ -3801,8 +3799,12 @@ private short readKeyBlobVersion(short keyBlob) { // If Version is not present. Then it is either an old KeyBlob or // corrupted KeyBlob. version = 0; + } else { + version = KMInteger.cast(version).getShort(); + if (version > KEYBLOB_VERSION || version < 0) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } } - version = KMInteger.cast(version).getShort(); } catch(Exception e) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } @@ -3840,7 +3842,7 @@ private void readKeyBlobParams(short version, short parsedKeyBlob) { } } - private short decodeKeyBlob(short version, short keyBlob) { + private void decodeKeyBlob(short version, short keyBlob) { // Decode KeyBlob and read the KeyBlob params based on the version. short parsedBlob = decoder.decodeArray(createKeyBlobExp(version), KMByteBlob.cast(keyBlob).getBuffer(), @@ -3852,7 +3854,6 @@ private short decodeKeyBlob(short version, short keyBlob) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } readKeyBlobParams(version, parsedBlob); - return parsedBlob; } private void processDecryptSecret(short version, short appId, short appData, byte[] scratchPad) { @@ -3870,21 +3871,19 @@ private void processDecryptSecret(short version, short appId, short appData, byt KMArray.cast(data[KEY_BLOB]).add(keyBlobSecretOff, data[SECRET]); } - private short parseEncryptedKeyBlob(short keyBlob, short appId, short appData, + private void parseEncryptedKeyBlob(short keyBlob, short appId, short appData, byte[] scratchPad, short version) { - short parsedBlob = KMType.INVALID_VALUE; // make root of trust blob data[ROT] = readROT(scratchPad); if (data[ROT] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } try { - parsedBlob = decodeKeyBlob(version, keyBlob); + decodeKeyBlob(version, keyBlob); processDecryptSecret(version, appId, appData, scratchPad); } catch (Exception e) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } - return parsedBlob; } // Read RoT @@ -3963,6 +3962,15 @@ private static void encryptSecret(byte[] scratchPad) { data[SECRET] = KMByteBlob.instance(scratchPad, (short)0, len); } + private static boolean isAsymetricKeyBlob(short hardwareParams) { + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams); + if(KMEnumTag.cast(alg).getValue() == KMType.RSA || KMEnumTag.cast(alg).getValue() == KMType.EC ) + { + return true; + } + return false; + } + private static void makeAuthData(short version, byte[] scratchPad) { // Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY. // VERSION is included only for KeyBlobs having version >= 1. @@ -3970,12 +3978,13 @@ private static void makeAuthData(short version, byte[] scratchPad) { short index = 0; short arrayLen = 0; Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); + boolean isAsymKeyBlob = isAsymetricKeyBlob(data[HW_PARAMETERS]); switch (version) { case (short) 0: // If the KeyBlob size is 5 then it contains an Asymmetric Key, otherwise // it contains a Symmetric Key. For Asymmetric Keys include the PUB_KEY. arrayLen = 2; - if (KMArray.cast(data[KEY_BLOB]).length() == ASYM_KEY_BLOB_SIZE_V0) { + if (isAsymKeyBlob) { arrayLen = 3; Util.setShort(scratchPad, (short) 4, data[PUB_KEY]); } @@ -3986,7 +3995,7 @@ private static void makeAuthData(short version, byte[] scratchPad) { // If the KeyBlob size is 6 then it contains an Asymmetric Key, otherwise // it contains a Symmetric Key. For Asymmetric Keys include the PUB_KEY. arrayLen = 3; - if (KMArray.cast(data[KEY_BLOB]).length() == ASYM_KEY_BLOB_SIZE_V1) { + if (isAsymKeyBlob) { arrayLen = 4; Util.setShort(scratchPad, (short) 6, data[PUB_KEY]); } From d19b377bcb9ab68093d610daa9b1dfe2dd953628 Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Fri, 8 Apr 2022 18:49:25 +0000 Subject: [PATCH 7/9] Added keyType constant and used in the code --- .../javacard/keymaster/KMKeymasterApplet.java | 100 ++++++++++-------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 584d2da5..91f907df 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -220,6 +220,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6; public static final byte SYM_KEY_BLOB_SIZE_V0 = 4; public static final byte ASYM_KEY_BLOB_SIZE_V0 = 5; + // Key type constants + public static final byte SYM_KEY_TYPE = 0; + public static final byte ASYM_KEY_TYPE = 1; protected static RemotelyProvisionedComponentDevice rkp; protected static KMEncoder encoder; @@ -782,8 +785,18 @@ private short createKeyBlobExp(short version) { return keyBlob; } - private static short createKeyBlobInstance(boolean isSymmetricKey) { - byte arrayLen = isSymmetricKey ? SYM_KEY_BLOB_SIZE_V1 : ASYM_KEY_BLOB_SIZE_V1; + private static short createKeyBlobInstance(byte keyType) { + short arrayLen = 0; + switch (keyType) { + case ASYM_KEY_TYPE: + arrayLen = ASYM_KEY_BLOB_SIZE_V1; + break; + case SYM_KEY_TYPE: + arrayLen = SYM_KEY_BLOB_SIZE_V1; + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } return KMArray.instance(arrayLen); } @@ -3057,7 +3070,7 @@ private void importECKeys(byte[] scratchPad) { // add scratch pad to key parameters updateKeyParameters(scratchPad, index); - data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3099,7 +3112,7 @@ private void importHmacKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate HMAC Key parameters validateHmacKey(); - data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private void importTDESKey(byte[] scratchPad) { @@ -3139,7 +3152,7 @@ private void importTDESKey(byte[] scratchPad) { KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list updateKeyParameters(scratchPad, index); - data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private void validateAesKeySize(short keySizeBits) { @@ -3182,7 +3195,7 @@ private void importAESKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate AES Key parameters validateAESKey(); - data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private void importRSAKey(byte[] scratchPad) { @@ -3258,7 +3271,7 @@ private void importRSAKey(byte[] scratchPad) { updateKeyParameters(scratchPad, index); // validate RSA Key parameters validateRSAKey(scratchPad); - data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3615,7 +3628,7 @@ private static void generateRSAKey(byte[] scratchPad) { KMByteBlob.cast(data[PUB_KEY]).length(), lengths); - data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3654,7 +3667,7 @@ private static void generateAESKey(byte[] scratchPad) { short len = seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private static void validateECKeys() { @@ -3676,7 +3689,7 @@ private static void generateECKeys(byte[] scratchPad) { (short) 128, lengths); data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); - data[KEY_BLOB] = createKeyBlobInstance(false /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3692,7 +3705,7 @@ private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(); short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } private static void validateHmacKey() { @@ -3726,7 +3739,7 @@ private static void generateHmacKey(byte[] scratchPad) { // generate HMAC Key short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); - data[KEY_BLOB] = createKeyBlobInstance(true /* isSymmetricKey */); + data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE); } protected static short getBootPatchLevel(byte[] scratchPad){ @@ -3962,63 +3975,60 @@ private static void encryptSecret(byte[] scratchPad) { data[SECRET] = KMByteBlob.instance(scratchPad, (short)0, len); } - private static boolean isAsymetricKeyBlob(short hardwareParams) { - short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams); - if(KMEnumTag.cast(alg).getValue() == KMType.RSA || KMEnumTag.cast(alg).getValue() == KMType.EC ) - { - return true; - } - return false; + private static byte getKeyType(short hardwareParams) { + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams); + if (KMEnumTag.cast(alg).getValue() == KMType.RSA + || KMEnumTag.cast(alg).getValue() == KMType.EC) { + return ASYM_KEY_TYPE; + } + return SYM_KEY_TYPE; } private static void makeAuthData(short version, byte[] scratchPad) { - // Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY. + // For KeyBlob V1: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY. + // For KeyBlob V0: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS and PUB_KEY. // VERSION is included only for KeyBlobs having version >= 1. // PUB_KEY is included for only ASYMMETRIC KeyBlobs. short index = 0; - short arrayLen = 0; + short numParams = 0; Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); - boolean isAsymKeyBlob = isAsymetricKeyBlob(data[HW_PARAMETERS]); + byte keyType = getKeyType(data[HW_PARAMETERS]); + // Copy the relevant parameters in the scratchPad in the order + // 1. HW_PARAMETERS + // 2. HIDDEN_PARAMTERS + // 3. VERSION ( Only Version >= 1) + // 4. PUB_KEY ( Only for Asymmetric Keys) switch (version) { case (short) 0: - // If the KeyBlob size is 5 then it contains an Asymmetric Key, otherwise - // it contains a Symmetric Key. For Asymmetric Keys include the PUB_KEY. - arrayLen = 2; - if (isAsymKeyBlob) { - arrayLen = 3; - Util.setShort(scratchPad, (short) 4, data[PUB_KEY]); - } + numParams = 2; Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + // For Asymmetric Keys include the PUB_KEY. + if (keyType == ASYM_KEY_TYPE) { + numParams = 3; + Util.setShort(scratchPad, (short) 4, data[PUB_KEY]); + } break; case (short) 1: - // If the KeyBlob size is 6 then it contains an Asymmetric Key, otherwise - // it contains a Symmetric Key. For Asymmetric Keys include the PUB_KEY. - arrayLen = 3; - if (isAsymKeyBlob) { - arrayLen = 4; - Util.setShort(scratchPad, (short) 6, data[PUB_KEY]); - } + numParams = 3; Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); Util.setShort(scratchPad, (short) 4, data[KEY_BLOB_VERSION_DATA_OFFSET]); + // For Asymmetric Keys include the PUB_KEY. + if (keyType == ASYM_KEY_TYPE) { + numParams = 4; + Util.setShort(scratchPad, (short) 6, data[PUB_KEY]); + } break; default: KMException.throwIt(KMError.INVALID_KEY_BLOB); } - short params = KMArray.instance(arrayLen); - while (index < arrayLen) { - KMArray.cast(params).add(index, Util.getShort(scratchPad, (short) (index * 2))); - index++; - } - short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE); index = 0; short len = 0; - short paramsLen = KMArray.cast(params).length(); Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0); - while (index < paramsLen) { - short tag = KMArray.cast(params).get(index); + while (index < numParams) { + short tag = Util.getShort(scratchPad, (short) (index * 2)); len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); Util.arrayCopyNonAtomic(repository.getHeap(), authIndex, repository.getHeap(), (short) (authIndex + len + 32), (short) 32); From b1338add5d0160721cd15fa31aa5d0e397e3bd06 Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Sat, 9 Apr 2022 08:05:17 +0000 Subject: [PATCH 8/9] Incorporated review comments --- .../javacard/keymaster/KMKeyParameters.java | 1 - .../javacard/keymaster/KMKeymasterApplet.java | 86 ++++++++++--------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 051f2872..58cb7e82 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -167,7 +167,6 @@ public static short makeSbEnforced(short keyParamsPtr, byte origin, short bootPatchObjPtr, byte[] scratchPad) { final short[] hwEnforcedTagArr = { // HW Enforced - KMType.ENUM_TAG, KMType.ORIGIN, KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, KMType.ENUM_TAG, KMType.ALGORITHM, KMType.UINT_TAG, KMType.KEYSIZE, diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 91f907df..10dce720 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -212,9 +212,11 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // ComputeHMAC constants private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; protected static final short MAX_CERT_SIZE = 2048; - // Keyblob version goes into keyblob and will affect all - // the keyblobs if it is changed. - public static final short KEYBLOB_VERSION = 1; + // KEYBLOB_CURRENT_VERSION goes into KeyBlob and will affect all + // the KeyBlobs if it is changed. please increment this + // version number whenever you change anything related to + // KeyBlob (structure, encryption algorithm etc). + public static final short KEYBLOB_CURRENT_VERSION = 1; // KeyBlob array size constants. public static final byte SYM_KEY_BLOB_SIZE_V1 = 5; public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6; @@ -750,34 +752,28 @@ private void processDeleteAllKeysCmd(APDU apdu) { sendError(apdu, KMError.OK); } - private short deleteKeyCmd(APDU apdu){ - short cmd = KMArray.instance((short) 1); - KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); - return receiveIncoming(apdu, cmd); - } - private short createKeyBlobExp(short version) { short keyBlob = KMType.INVALID_VALUE; - short keyBlobExp = KMByteBlob.exp(); + short byteBlobExp = KMByteBlob.exp(); short keyChar = KMKeyCharacteristics.exp(); switch(version) { case (short) 0: // Old KeyBlob has a maximum of 5 elements. keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V0); - KMArray.cast(keyBlob).add((short) 0, keyBlobExp); - KMArray.cast(keyBlob).add((short) 1, keyBlobExp); - KMArray.cast(keyBlob).add((short) 2, keyBlobExp); + KMArray.cast(keyBlob).add((short) 0, byteBlobExp); + KMArray.cast(keyBlob).add((short) 1, byteBlobExp); + KMArray.cast(keyBlob).add((short) 2, byteBlobExp); KMArray.cast(keyBlob).add((short) 3, keyChar); - KMArray.cast(keyBlob).add((short) 4, keyBlobExp); + KMArray.cast(keyBlob).add((short) 4, byteBlobExp); break; case (short) 1: keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp()); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, keyBlobExp); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, keyBlobExp); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, byteBlobExp); KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); - KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, keyBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, byteBlobExp); break; default: KMException.throwIt(KMError.INVALID_KEY_BLOB); @@ -943,13 +939,11 @@ private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version // Parameter in the KeyBlob should be updated to the next version. short version = readKeyBlobVersion(keyBlob); - if (version < KEYBLOB_VERSION) { + parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version); + if (version < KEYBLOB_CURRENT_VERSION) { return true; } - parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version); - // Get boot patch level. - kmDataStore.getBootPatchLevel(scratchPad, (short) 0); - short bootPatchLevel = KMInteger.uint_32(scratchPad, (short) 0); + short bootPatchLevel = getBootPatchLevel(scratchPad); // Fill the key-value properties in the scratchpad Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0); Util.setShort(scratchPad, (short) 0, KMType.OS_VERSION); @@ -1019,16 +1013,21 @@ private void processUpgradeKeyCmd(APDU apdu) { // does not parse the KeyBlob. boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad); if (isKeyUpgradeRequired) { - // Check if KeyBlob is already parsed in isKeyUpgradeRequired() function, If not parsed - // parse the KeyBlob. - if (data[KEY_BLOB] == KMType.INVALID_VALUE) { - // Parse existing key blob - parseEncryptedKeyBlob(keyBlob, data[APP_ID], data[APP_DATA], scratchPad, - readKeyBlobVersion(keyBlob)); - } // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); - makeKeyCharacteristics(scratchPad); + byte keyType = getKeyType(data[HW_PARAMETERS]); + switch (keyType) { + case ASYM_KEY_TYPE: + data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + break; + case SYM_KEY_TYPE: + data[KEY_BLOB] = KMArray.instance(SYM_KEY_BLOB_SIZE_V1); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + makeKeyCharacteristics(data[HW_PARAMETERS], scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); } else { @@ -3498,9 +3497,7 @@ private void processAttestKeyCmd(APDU apdu) { } // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired // function itself. - if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad, KEYBLOB_CURRENT_VERSION); // The key which is being attested should be asymmetric i.e. RSA or EC short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); if (alg != KMType.RSA && alg != KMType.EC) { @@ -3751,15 +3748,15 @@ protected static short getBootPatchLevel(byte[] scratchPad){ return KMInteger.uint_32(scratchPad, (short)0); } - private static void makeKeyCharacteristics(byte[] scratchPad) { + private static void makeKeyCharacteristics(short params, byte[] scratchPad) { short osVersion = kmDataStore.getOsVersion(); short osPatch = kmDataStore.getOsPatch(); short vendorPatch = kmDataStore.getVendorPatchLevel(); short bootPatch = getBootPatchLevel(scratchPad); data[SB_PARAMETERS] = KMKeyParameters.makeSbEnforced( - data[KEY_PARAMETERS], (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad); - data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(data[KEY_PARAMETERS],scratchPad); - data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS],scratchPad); + params, (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad); + data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(params, scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(params,scratchPad); data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setStrongboxEnforced(data[SB_PARAMETERS]); @@ -3767,6 +3764,11 @@ private static void makeKeyCharacteristics(byte[] scratchPad) { KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]); } + private static void makeKeyCharacteristics(byte[] scratchPad) { + makeKeyCharacteristics(data[KEY_PARAMETERS], scratchPad); + } + + private static void createEncryptedKeyBlob(byte[] scratchPad) { // make root of trust blob data[ROT] = readROT(scratchPad); @@ -3776,9 +3778,9 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { // make hidden key params list data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); - data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_VERSION); + data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_CURRENT_VERSION); // make authorization data - makeAuthData(KEYBLOB_VERSION, scratchPad); + makeAuthData(KEYBLOB_CURRENT_VERSION, scratchPad); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); // create key blob array @@ -3814,7 +3816,7 @@ private short readKeyBlobVersion(short keyBlob) { version = 0; } else { version = KMInteger.cast(version).getShort(); - if (version > KEYBLOB_VERSION || version < 0) { + if (version > KEYBLOB_CURRENT_VERSION || version < 0) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } } @@ -3995,7 +3997,7 @@ private static void makeAuthData(short version, byte[] scratchPad) { byte keyType = getKeyType(data[HW_PARAMETERS]); // Copy the relevant parameters in the scratchPad in the order // 1. HW_PARAMETERS - // 2. HIDDEN_PARAMTERS + // 2. HIDDEN_PARAMETERS // 3. VERSION ( Only Version >= 1) // 4. PUB_KEY ( Only for Asymmetric Keys) switch (version) { From 2b0134ac0315b0051c2f7e52b0b7667d4886f058 Mon Sep 17 00:00:00 2001 From: Subrahmanyaman Date: Sat, 9 Apr 2022 23:05:37 +0000 Subject: [PATCH 9/9] Added upgradeKeyCharacteristics function --- .../javacard/keymaster/KMKeymasterApplet.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 10dce720..8addee71 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1027,7 +1027,10 @@ private void processUpgradeKeyCmd(APDU apdu) { default: KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } - makeKeyCharacteristics(data[HW_PARAMETERS], scratchPad); + // Update the system properties to the latest values and also re-create the KeyBlob's + // KeyCharacteristics to make sure all the values are up-to-date with the latest applet + // changes. + upgradeKeyBlobKeyCharacteristics(data[HW_PARAMETERS], scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); } else { @@ -3748,15 +3751,36 @@ protected static short getBootPatchLevel(byte[] scratchPad){ return KMInteger.uint_32(scratchPad, (short)0); } - private static void makeKeyCharacteristics(short params, byte[] scratchPad) { + // This function is only called from processUpgradeKey command. + // 1. Update the latest values of OSVersion, OSPatch, VendorPatch and BootPatch in the + // KeyBlob's KeyCharacteristics. + // 2. Re-create KeyBlob's KeyCharacteristics from HW_PARAMS to make sure we don't miss + // anything which happens in these functions makeSbEnforced and makeTeeEnforced in + // the future. Like validations, addition of custom tags e.t.c. + // 3. No need to create Keystore Enforced list here as it is not required to be included in + // the KeyBlob's KeyCharacteristics. + // 4. No need to create KeyCharacteristics as upgradeKey does not require to return any + // KeyCharacteristics back. + private static void upgradeKeyBlobKeyCharacteristics(short hwParams, byte[] scratchPad) { short osVersion = kmDataStore.getOsVersion(); short osPatch = kmDataStore.getOsPatch(); short vendorPatch = kmDataStore.getVendorPatchLevel(); short bootPatch = getBootPatchLevel(scratchPad); data[SB_PARAMETERS] = KMKeyParameters.makeSbEnforced( - params, (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad); - data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(params, scratchPad); - data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(params,scratchPad); + hwParams, (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad); + data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(hwParams, scratchPad); + data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); + } + + private static void makeKeyCharacteristics(byte[] scratchPad) { + short osVersion = kmDataStore.getOsVersion(); + short osPatch = kmDataStore.getOsPatch(); + short vendorPatch = kmDataStore.getVendorPatchLevel(); + short bootPatch = getBootPatchLevel(scratchPad); + data[SB_PARAMETERS] = KMKeyParameters.makeSbEnforced( + data[KEY_PARAMETERS], (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad); + data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(data[KEY_PARAMETERS], scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS],scratchPad); data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setStrongboxEnforced(data[SB_PARAMETERS]); @@ -3764,10 +3788,6 @@ private static void makeKeyCharacteristics(short params, byte[] scratchPad) { KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]); } - private static void makeKeyCharacteristics(byte[] scratchPad) { - makeKeyCharacteristics(data[KEY_PARAMETERS], scratchPad); - } - private static void createEncryptedKeyBlob(byte[] scratchPad) { // make root of trust blob