From 7264baf5f27d3e1f730b494f594567f0e5960205 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Mon, 7 Mar 2022 05:02:23 +0000 Subject: [PATCH] added upgrade support --- .../javacard/keymaster/KMAndroidSEApplet.java | 96 +- .../android/javacard/seprovider/KMAESKey.java | 9 +- .../seprovider/KMAndroidSEProvider.java | 563 +++--------- .../seprovider/KMDataStoreConstants.java | 12 + .../seprovider/KMECDeviceUniqueKey.java | 20 + .../javacard/seprovider/KMECPrivateKey.java | 9 +- .../javacard/seprovider/KMHmacKey.java | 11 +- .../javacard/seprovider/KMRkpMacKey.java | 5 + .../javacard/seprovider/KMSEProvider.java | 199 ++--- .../javacard/keymaster/KMKeymasterApplet.java | 137 +-- .../keymaster/KMKeymintDataStore.java | 844 ++++++++++++++++++ .../javacard/keymaster/KMRepository.java | 398 +-------- .../RemotelyProvisionedComponentDevice.java | 96 +- 13 files changed, 1313 insertions(+), 1086 deletions(-) create mode 100644 Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java create mode 100644 Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java create mode 100644 Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 39559f70..ffaea2fe 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -32,6 +32,10 @@ import javacard.security.CryptoException; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { + // Magic number version + private static final byte KM_MAGIC_NUMBER = (byte) 0x82; + // MSB byte is for Major version and LSB byte is for Minor version. + private static final short CURRENT_PACKAGE_VERSION = 0x0009; // 0.9 private static final byte KM_BEGIN_STATE = 0x00; private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; @@ -69,9 +73,14 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis private static byte keymasterState = ILLEGAL_STATE; private static byte provisionStatus = NOT_PROVISIONED; + // Package version. + protected short packageVersion; KMAndroidSEApplet() { super(new KMAndroidSEProvider()); + if (!UpgradeManager.isUpgrading()) { + packageVersion = CURRENT_PACKAGE_VERSION; + } } /** @@ -107,7 +116,7 @@ public void process(APDU apdu) { case INS_SET_BOOT_ENDED_CMD: //set the flag to mark boot ended - repository.setBootEndedStatus(true); + kmDataStore.setBootEndedStatus(true); sendError(apdu, KMError.OK); break; @@ -184,12 +193,12 @@ private static void processProvisionDeviceUniqueKey(APDU apdu) { short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); //Store the Device unique Key. - seProvider.createDeviceUniqueKey(false, scratchPad, (short) 0, pubKeyLen, scratchPad, + kmDataStore.createDeviceUniqueKey(scratchPad, (short) 0, pubKeyLen, scratchPad, pubKeyLen, privKeyLen); short bcc = generateBcc(false, scratchPad); short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); - ((KMAndroidSEProvider) seProvider).persistBootCertificateChain(scratchPad, (short) 0, len); + kmDataStore.persistBootCertificateChain(scratchPad, (short) 0, len); sendError(apdu, KMError.OK); } @@ -226,7 +235,7 @@ private static void processProvisionAdditionalCertChain(APDU apdu) { srcBuffer, null); // Compare the DK_Pub. short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); - KMDeviceUniqueKey uniqueKey = seProvider.getDeviceUniqueKey(false); + KMDeviceUniqueKey uniqueKey = kmDataStore.getDeviceUniqueKey(false); if (uniqueKey == null) { KMException.throwIt(KMError.STATUS_FAILED); } @@ -235,7 +244,7 @@ private static void processProvisionAdditionalCertChain(APDU apdu) { (0 != Util.arrayCompare(srcBuffer, (short) 0, srcBuffer, pubKeyLen, pubKeyLen))) { KMException.throwIt(KMError.STATUS_FAILED); } - seProvider.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); + kmDataStore.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); //reclaim memory repository.reclaimMemory(bufferLength); sendError(apdu, KMError.OK); @@ -269,7 +278,7 @@ public void setAttestationIds(short attIdVals) { KMException.throwIt(KMError.INVALID_ARGUMENT); } obj = KMByteTag.cast(obj).getValue(); - ((KMAndroidSEProvider) seProvider).setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), + kmDataStore.setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); index++; } @@ -288,7 +297,7 @@ private void processProvisionPreSharedSecretCmd(APDU apdu) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // Persist shared Hmac. - ((KMAndroidSEProvider) seProvider).createPresharedKey( + kmDataStore.createPresharedKey( KMByteBlob.cast(val).getBuffer(), KMByteBlob.cast(val).getStartOff(), KMByteBlob.cast(val).length()); @@ -345,7 +354,7 @@ private void processSetBootParamsCmd(APDU apdu) { short bootParam = KMArray.cast(args).get((short) 0); - ((KMAndroidSEProvider) seProvider).setBootPatchLevel(KMInteger.cast(bootParam).getBuffer(), + kmDataStore.setBootPatchLevel(KMInteger.cast(bootParam).getBuffer(), KMInteger.cast(bootParam).getStartOff(), KMInteger.cast(bootParam).length()); @@ -353,7 +362,7 @@ private void processSetBootParamsCmd(APDU apdu) { if (KMByteBlob.cast(bootParam).length() > BOOT_KEY_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - ((KMAndroidSEProvider) seProvider).setBootKey(KMByteBlob.cast(bootParam).getBuffer(), + kmDataStore.setBootKey(KMByteBlob.cast(bootParam).getBuffer(), KMByteBlob.cast(bootParam).getStartOff(), KMByteBlob.cast(bootParam).length()); @@ -361,22 +370,22 @@ private void processSetBootParamsCmd(APDU apdu) { if (KMByteBlob.cast(bootParam).length() > BOOT_HASH_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - ((KMAndroidSEProvider) seProvider).setVerifiedBootHash(KMByteBlob.cast(bootParam).getBuffer(), + kmDataStore.setVerifiedBootHash(KMByteBlob.cast(bootParam).getBuffer(), KMByteBlob.cast(bootParam).getStartOff(), KMByteBlob.cast(bootParam).length()); bootParam = KMArray.cast(args).get((short) 3); byte enumVal = KMEnum.cast(bootParam).getVal(); - ((KMAndroidSEProvider) seProvider).setBootState(enumVal); + kmDataStore.setBootState(enumVal); bootParam = KMArray.cast(args).get((short) 4); enumVal = KMEnum.cast(bootParam).getVal(); - ((KMAndroidSEProvider) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + kmDataStore.setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - Util.arrayFillNonAtomic(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE, (byte) 0); - seProvider.createComputedHmacKey(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE); + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE, (byte) 0); + kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); super.reboot(); sendError(apdu, KMError.OK); @@ -394,36 +403,55 @@ public void onCleanup() { @Override public void onConsolidate() { } + + private boolean isUpgradeAllowed(short version) { + boolean upgradeAllowed = false; + short oldMajorVersion = (short) ((version >> 8) & 0x00FF); + short oldMinorVersion = (short) (version & 0x00FF); + short currentMajorVersion = (short) (CURRENT_PACKAGE_VERSION >> 8 & 0x00FF); + short currentMinorVersion = (short) (CURRENT_PACKAGE_VERSION & 0x00FF); + // Downgrade of the Applet is not allowed. + // Upgrade is not allowed to a next version which is not immediate. + if ((short) (currentMajorVersion - oldMajorVersion) == 1) { + if (currentMinorVersion == 0) { + upgradeAllowed = true; + } + } else if ((short) (currentMajorVersion - oldMajorVersion) == 0) { + if (currentMinorVersion >= oldMinorVersion) { + upgradeAllowed = true; + } + } + return upgradeAllowed; + } @Override public void onRestore(Element element) { element.initRead(); - provisionStatus = element.readByte(); - keymasterState = element.readByte(); - repository.onRestore(element); - seProvider.onRestore(element); + byte magicNumber = element.readByte(); + if (magicNumber != KM_MAGIC_NUMBER) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short packageVersion = element.readShort(); + // Validate version. + if (0 != packageVersion && !isUpgradeAllowed(packageVersion)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + kmDataStore.onRestore(element); } @Override public Element onSave() { - // SEProvider count - short primitiveCount = seProvider.getBackupPrimitiveByteCount(); - short objectCount = seProvider.getBackupObjectCount(); - //Repository count - primitiveCount += repository.getBackupPrimitiveByteCount(); - objectCount += repository.getBackupObjectCount(); - //KMKeymasterApplet count - primitiveCount += computePrimitveDataSize(); - objectCount += computeObjectCount(); - - // Create element. + short primitiveCount = 3; + primitiveCount += kmDataStore.getBackupPrimitiveByteCount(); + short objectCount = kmDataStore.getBackupObjectCount(); + // Create element. Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, primitiveCount, objectCount); - element.write(provisionStatus); - element.write(keymasterState); - repository.onSave(element); - seProvider.onSave(element); - return element; + + element.write(KM_MAGIC_NUMBER); + element.write(packageVersion); + kmDataStore.onSave(element); + return element; } private short computePrimitveDataSize() { diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java index 7e34065e..0c3172c2 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java @@ -43,10 +43,11 @@ public static void onSave(Element element, KMAESKey kmKey) { element.write(kmKey.aesKey); } - public static KMAESKey onRestore(Element element) { - AESKey aesKey = (AESKey) element.readObject(); - KMAESKey kmKey = new KMAESKey(aesKey); - return kmKey; + public static KMAESKey onRestore(AESKey aesKey) { + if (aesKey == null) { + return null; + } + return new KMAESKey(aesKey); } public static short getBackupPrimitiveByteCount() { diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 522f4edf..25a654f1 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -18,6 +18,8 @@ import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; @@ -60,8 +62,6 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. - private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512;//First 2 bytes for length. - private static final short BCC_MAX_SIZE = 512; public static final short SHARED_SECRET_KEY_SIZE = 32; public static final byte POWER_RESET_FALSE = (byte) 0xAA; public static final byte POWER_RESET_TRUE = (byte) 0x00; @@ -92,35 +92,9 @@ public class KMAndroidSEProvider implements KMSEProvider { private KMRsaOAEPEncoding rsaOaepDecipher; private KMPoolManager poolMgr; - // Data - originally was in repository - private byte[] attIdBrand; - private byte[] attIdDevice; - private byte[] attIdProduct; - private byte[] attIdSerial; - private byte[] attIdImei; - private byte[] attIdMeId; - private byte[] attIdManufacturer; - private byte[] attIdModel; - - // Boot parameters - private byte[] verifiedHash; - private byte[] bootKey; - private byte[] bootPatchLevel; - private boolean deviceBootLocked; - private short bootState; - // Entropy private RandomData rng; - //For storing root certificate and intermediate certificates. - private byte[] certificateChain; - private KMAESKey masterKey; - private KMECPrivateKey attestationKey; - private KMECDeviceUniqueKey testKey; - private KMECDeviceUniqueKey deviceUniqueKey; - private KMHmacKey preSharedKey; - private KMHmacKey computedHmacKey; - private byte[] additionalCertChain; - private byte[] bcc; + private boolean isProvisionLocked; private static KMAndroidSEProvider androidSEProvider = null; @@ -155,23 +129,9 @@ public KMAndroidSEProvider() { // Temporary transient array created to use locally inside functions. tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); - + Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); // Random number generator initialisation. rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); - //Allocate buffer for certificate chain. - if (!isUpgrading()) { - certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; - additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; - bcc = new byte[BCC_MAX_SIZE]; - // Initialize attestationKey and preShared key with zeros. - Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); - // Create attestation key of P-256 curve. - createAttestationKey(tmpArray, (short) 0, (short) 32); - // Pre-shared secret key length is 32 bytes. - createPresharedKey(tmpArray, (short) 0, (short) SHARED_SECRET_KEY_SIZE); - // Initialize the Computed Hmac Key object. - createComputedHmacKey(tmpArray, (short)0, (short) 32); - } androidSEProvider = this; resetFlag = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_DESELECT); @@ -603,6 +563,16 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); return hmacSign(key, data, dataStart, dataLength, mac, macStart); } + + @Override + public short hmacSign(Object key, + byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + if(!(key instanceof KMHmacKey)) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + KMHmacKey hmacKey = (KMHmacKey) key; + return hmacSign(hmacKey.getKey(), data, dataStart, dataLength, mac, macStart); + } @Override public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, @@ -894,91 +864,13 @@ public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, return key.getKey(keyBuf, keyStart); } - public void clearCertificateChain() { - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(certificateChain, (short) 0, CERT_CHAIN_MAX_SIZE, (byte) 0); - JCSystem.commitTransaction(); - } - - //This function supports multi-part request data. - public void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen) { - // _____________________________________________________ - // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... - // |_________|________|_________|_______|________|_______ - // First two bytes holds the length of the total buffer. - // CBOR format: - // Next single byte holds the byte string header. - // Next 3 bytes holds the total length of the certificate chain. - if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - short persistedLen = Util.getShort(certificateChain, (short) 0); - if (persistedLen > totalLen) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - JCSystem.beginTransaction(); - Util.setShort(certificateChain, (short) 0, (short) (len + persistedLen)); - Util.arrayCopyNonAtomic(buf, offset, certificateChain, - (short) (persistedLen + 2), len); - JCSystem.commitTransaction(); - } - - public short readCertificateChain(byte[] buf, short offset) { - short len = Util.getShort(certificateChain, (short) 0); - Util.arrayCopyNonAtomic(certificateChain, (short) 2, buf, offset, len); - return len; - } - - public short getCertificateChainLength() { - return Util.getShort(certificateChain, (short) 0); - } - - @Override - public void onSave(Element element) { - element.write(certificateChain); - element.write(additionalCertChain); - element.write(bcc); - KMAESKey.onSave(element, masterKey); - KMECPrivateKey.onSave(element, attestationKey); - KMHmacKey.onSave(element, preSharedKey); - } - - @Override - public void onRestore(Element element) { - certificateChain = (byte[]) element.readObject(); - additionalCertChain = (byte[]) element.readObject(); - bcc = (byte[]) element.readObject(); - masterKey = KMAESKey.onRestore(element); - attestationKey = KMECPrivateKey.onRestore(element); - preSharedKey = KMHmacKey.onRestore(element); - } - - @Override - public short getBackupPrimitiveByteCount() { - short count = - (short) (KMAESKey.getBackupPrimitiveByteCount() + - KMECPrivateKey.getBackupPrimitiveByteCount() + - KMHmacKey.getBackupPrimitiveByteCount()); - return count; - } - - @Override - public short getBackupObjectCount() { - short count = - (short) (1 /*Certificate chain */ + - KMAESKey.getBackupObjectCount() + - KMECPrivateKey.getBackupObjectCount() + - KMHmacKey.getBackupObjectCount()); - return count; - } - @Override public boolean isUpgrading() { return UpgradeManager.isUpgrading(); } @Override - public KMMasterKey createMasterKey(short keySizeBits) { + public KMMasterKey createMasterKey(KMMasterKey masterKey, short keySizeBits) { try { if (masterKey == null) { AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, @@ -986,7 +878,7 @@ public KMMasterKey createMasterKey(short keySizeBits) { masterKey = new KMAESKey(key); short keyLen = (short) (keySizeBits / 8); getTrueRandomNumber(tmpArray, (short) 0, keyLen); - masterKey.setKey(tmpArray, (short) 0); + ((KMAESKey)masterKey).setKey(tmpArray, (short) 0); } return (KMMasterKey) masterKey; } finally { @@ -994,7 +886,8 @@ public KMMasterKey createMasterKey(short keySizeBits) { } } - public KMAttestationKey createAttestationKey(byte[] keyData, short offset, + @Override + public KMAttestationKey createAttestationKey(KMAttestationKey attestationKey, byte[] keyData, short offset, short length) { if (attestationKey == null) { // Strongbox supports only P-256 curve for EC key. @@ -1002,11 +895,12 @@ public KMAttestationKey createAttestationKey(byte[] keyData, short offset, initECKey(ecKeyPair); attestationKey = new KMECPrivateKey(ecKeyPair); } - attestationKey.setS(keyData, offset, length); + ((KMECPrivateKey) attestationKey).setS(keyData, offset, length); return (KMAttestationKey) attestationKey; } - public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length) { + @Override + public KMPreSharedKey createPreSharedKey(KMPreSharedKey preSharedKey, byte[] keyData, short offset, short length) { short lengthInBits = (short) (length * 8); if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); @@ -1016,12 +910,12 @@ public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short len false); preSharedKey = new KMHmacKey(key); } - preSharedKey.setKey(keyData, offset, length); + ((KMHmacKey)preSharedKey).setKey(keyData, offset, length); return (KMPreSharedKey) preSharedKey; } @Override - public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { + public KMComputedHmacKey createComputedHmacKey(KMComputedHmacKey computedHmacKey, byte[] keyData, short offset, short length) { if (length != COMPUTED_HMAC_KEY_SIZE) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } @@ -1030,23 +924,9 @@ public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, sho false); computedHmacKey = new KMHmacKey(key); } - computedHmacKey.setKey(keyData, offset, length); + ((KMHmacKey)computedHmacKey).setKey(keyData, offset, length); return (KMComputedHmacKey) computedHmacKey; } - - @Override - public KMMasterKey getMasterKey() { - return (KMMasterKey) masterKey; - } - - public KMAttestationKey getAttestationKey() { - return (KMAttestationKey) attestationKey; - } - - @Override - public KMPreSharedKey getPresharedKey() { - return (KMPreSharedKey) preSharedKey; - } @Override public short ecSign256(byte[] secret, short secretStart, short secretLength, @@ -1129,198 +1009,6 @@ public short getAttestationKeyAlgorithm() { return KMType.INVALID_VALUE; } - @Override - public short getAttestationId(short tag, byte[] buffer, short start) { - switch (tag) { - // Attestation Id Brand - case KMType.ATTESTATION_ID_BRAND: - Util.arrayCopyNonAtomic(attIdBrand, (short) 0, buffer, start, (short) attIdBrand.length); - return (short) attIdBrand.length; - // Attestation Id Device - case KMType.ATTESTATION_ID_DEVICE: - Util.arrayCopyNonAtomic(attIdDevice, (short) 0, buffer, start, (short) attIdDevice.length); - return (short) attIdDevice.length; - // Attestation Id Product - case KMType.ATTESTATION_ID_PRODUCT: - Util.arrayCopyNonAtomic(attIdProduct, (short) 0, buffer, start, - (short) attIdProduct.length); - return (short) attIdProduct.length; - // Attestation Id Serial - case KMType.ATTESTATION_ID_SERIAL: - Util.arrayCopyNonAtomic(attIdSerial, (short) 0, buffer, start, (short) attIdSerial.length); - return (short) attIdSerial.length; - // Attestation Id IMEI - case KMType.ATTESTATION_ID_IMEI: - Util.arrayCopyNonAtomic(attIdImei, (short) 0, buffer, start, (short) attIdImei.length); - return (short) attIdImei.length; - // Attestation Id MEID - case KMType.ATTESTATION_ID_MEID: - Util.arrayCopyNonAtomic(attIdMeId, (short) 0, buffer, start, (short) attIdMeId.length); - return (short) attIdMeId.length; - // Attestation Id Manufacturer - case KMType.ATTESTATION_ID_MANUFACTURER: - Util.arrayCopyNonAtomic(attIdManufacturer, (short) 0, buffer, start, - (short) attIdManufacturer.length); - return (short) attIdManufacturer.length; - // Attestation Id Model - case KMType.ATTESTATION_ID_MODEL: - Util.arrayCopyNonAtomic(attIdModel, (short) 0, buffer, start, (short) attIdModel.length); - return (short) attIdModel.length; - } - return (short) 0; - } - - public void setAttestationId(short tag, byte[] buffer, short start, short length) { - switch (tag) { - // Attestation Id Brand - case KMType.ATTESTATION_ID_BRAND: - JCSystem.beginTransaction(); - attIdBrand = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id Device - case KMType.ATTESTATION_ID_DEVICE: - JCSystem.beginTransaction(); - attIdDevice = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id Product - case KMType.ATTESTATION_ID_PRODUCT: - JCSystem.beginTransaction(); - attIdProduct = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id Serial - case KMType.ATTESTATION_ID_SERIAL: - JCSystem.beginTransaction(); - attIdSerial = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id IMEI - case KMType.ATTESTATION_ID_IMEI: - JCSystem.beginTransaction(); - attIdImei = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id MEID - case KMType.ATTESTATION_ID_MEID: - JCSystem.beginTransaction(); - attIdMeId = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id Manufacturer - case KMType.ATTESTATION_ID_MANUFACTURER: - JCSystem.beginTransaction(); - attIdManufacturer = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length); - JCSystem.commitTransaction(); - break; - // Attestation Id Model - case KMType.ATTESTATION_ID_MODEL: - JCSystem.beginTransaction(); - attIdModel = new byte[length]; - Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length); - JCSystem.commitTransaction(); - break; - } - } - - @Override - public void deleteAttestationIds() { - attIdBrand = null; - attIdDevice = null; - attIdProduct = null; - attIdSerial = null; - attIdImei = null; - attIdMeId = null; - attIdManufacturer = null; - attIdModel = null; - } - - public boolean isPowerReset() { - boolean flag = false; - if (resetFlag[0] == POWER_RESET_TRUE) { - resetFlag[0] = POWER_RESET_FALSE; - flag = true; - if (poolMgr != null) { - poolMgr.powerReset(); - } - } - return flag; - } - - @Override - public short getVerifiedBootHash(byte[] buffer, short start) { - Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length); - return (short) verifiedHash.length; - } - - @Override - public short getBootKey(byte[] buffer, short start) { - Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); - return (short) bootKey.length; - } - - @Override - public short getBootState() { - return bootState; - } - - @Override - public boolean isDeviceBootLocked() { - return deviceBootLocked; - } - - @Override - public short getBootPatchLevel(byte[] buffer, short start) { - Util.arrayCopyNonAtomic(bootPatchLevel, (short) 0, buffer, start, - (short) bootPatchLevel.length); - return (short) bootPatchLevel.length; - } - - public void setVerifiedBootHash(byte[] buffer, short start, short length) { - if (verifiedHash == null) { - verifiedHash = new byte[32]; - } - if (length != 32) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - Util.arrayCopyNonAtomic(buffer, start, verifiedHash, (short) 0, (short) 32); - } - - public void setBootKey(byte[] buffer, short start, short length) { - if (bootKey == null) { - bootKey = new byte[32]; - } - if (length != 32) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - Util.arrayCopyNonAtomic(buffer, start, bootKey, (short) 0, (short) 32); - } - - public void setBootState(short state) { - bootState = state; - } - - public void setDeviceLocked(boolean state) { - deviceBootLocked = state; - } - - public void setBootPatchLevel(byte[] buffer, short start, short length) { - if (bootPatchLevel == null) { - bootPatchLevel = new byte[4]; - } - if (length > 4 || length < 0) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) length); - } @Override public short hkdf(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, @@ -1420,8 +1108,9 @@ public short ecSign256(KMDeviceUniqueKey ecPrivKey, byte[] inputDataBuf, } } } - - private KMDeviceUniqueKey createDeviceUniqueKey(KMECDeviceUniqueKey key, + + @Override + public KMDeviceUniqueKey createDeviceUniqueKey(KMDeviceUniqueKey key, byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, short privKeyOff, short privKeyLen) { if (key == null) { @@ -1429,87 +1118,23 @@ private KMDeviceUniqueKey createDeviceUniqueKey(KMECDeviceUniqueKey key, initECKey(ecKeyPair); key = new KMECDeviceUniqueKey(ecKeyPair); } - key.setS(privKey, privKeyOff, privKeyLen); - key.setW(pubKey, pubKeyOff, pubKeyLen); + ((KMECDeviceUniqueKey) key).setS(privKey, privKeyOff, privKeyLen); + ((KMECDeviceUniqueKey) key).setW(pubKey, pubKeyOff, pubKeyLen); return (KMDeviceUniqueKey) key; } @Override - public KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, - byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, - short privKeyOff, short privKeyLen) { - KMDeviceUniqueKey key; - if (testMode) { - key = createDeviceUniqueKey(testKey, pubKey, pubKeyOff, - pubKeyLen, privKey, privKeyOff, privKeyLen); - if (testKey == null) { - testKey = (KMECDeviceUniqueKey) key; - } - } else { - key = createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, - pubKeyLen, privKey, privKeyOff, privKeyLen); - if (deviceUniqueKey == null) { - deviceUniqueKey = (KMECDeviceUniqueKey) key; - } - } - return key; - } - - @Override - public KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode) { - return ((KMDeviceUniqueKey) (testMode ? testKey : deviceUniqueKey)); - } - - @Override - public void persistAdditionalCertChain(byte[] buf, short offset, short len) { - // Input buffer contains encoded additional certificate chain as shown below. - // AdditionalDKSignatures = { - // + SignerName => DKCertChain - // } - // SignerName = tstr - // DKCertChain = [ - // 2* Certificate // Root -> Leaf. Root is the vendo r - // // self-signed cert, leaf contains DK_pu b - // ] - // Certificate = COSE_Sign1 of a public key - if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + public KMRkpMacKey createRkpMacKey(KMRkpMacKey rkpMacKey, byte[] keyData, + short offset, short length) { + if (rkpMacKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), + false); + rkpMacKey = new KMHmacKey(key); } - JCSystem.beginTransaction(); - Util.setShort(additionalCertChain, (short) 0, (short) len); - Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, - (short) 2, len); - JCSystem.commitTransaction(); - + ((KMHmacKey) rkpMacKey).setKey(keyData, offset, length); + return rkpMacKey; } - - @Override - public short getAdditionalCertChainLength() { - return Util.getShort(additionalCertChain, (short) 0); - } - - @Override - public byte[] getAdditionalCertChain() { - return additionalCertChain; - } - - - @Override - public byte[] getBootCertificateChain() { - return bcc; - } - - public void persistBootCertificateChain(byte[] buf, short offset, short len) { - if ((short) (len + 2) > BCC_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - JCSystem.beginTransaction(); - Util.setShort(bcc, (short) 0, (short) len); - Util.arrayCopyNonAtomic(buf, offset, bcc, - (short) 2, len); - JCSystem.commitTransaction(); - } - + public void setProvisionLocked(boolean locked) { JCSystem.beginTransaction(); isProvisionLocked = locked; @@ -1537,9 +1162,16 @@ public short messageDigest256(byte[] inBuff, short inOffset, return len; } - @Override - public KMComputedHmacKey getComputedHmacKey() { - return computedHmacKey; + public boolean isPowerReset() { + boolean flag = false; + if (resetFlag[0] == POWER_RESET_TRUE) { + resetFlag[0] = POWER_RESET_FALSE; + flag = true; + if (poolMgr != null) { + poolMgr.powerReset(); + } + } + return flag; } private byte mapPurpose(short purpose) { @@ -1555,4 +1187,103 @@ private byte mapPurpose(short purpose) { } return -1; } + + @Override + public void onSave(Element element, byte interfaceType, Object object) { + element.write(interfaceType); + if (object == null) { + element.write(null); + return; + } + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + KMAESKey.onSave(element, (KMAESKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + KMECPrivateKey.onSave(element, (KMECPrivateKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + KMECDeviceUniqueKey.onSave(element, (KMECDeviceUniqueKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + @Override + public Object onResore(Element element) { + if (element == null) { + return null; + } + byte interfaceType = element.readByte(); + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.onRestore((AESKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.onRestore((KeyPair) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + return KMECDeviceUniqueKey.onRestore((KeyPair) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return null; + } + + @Override + public short getBackupPrimitiveByteCount(byte interfaceType) { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + return KMECDeviceUniqueKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + return KMHmacKey.getBackupPrimitiveByteCount(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } + + @Override + public short getBackupObjectCount(byte interfaceType) { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + return KMECDeviceUniqueKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + return KMHmacKey.getBackupObjectCount(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } + } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java new file mode 100644 index 00000000..31917a75 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java @@ -0,0 +1,12 @@ +package com.android.javacard.seprovider; + +public class KMDataStoreConstants { + // INTERFACE Types + public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01; + public static final byte INTERFACE_TYPE_ATTESTATION_KEY = 0x02; + public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY = 0x03; + public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04; + public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05; + public static final byte INTERFACE_TYPE_RKP_MAC_KEY = 0x06; + +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java index 0f68de61..f7a96100 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java @@ -14,6 +14,8 @@ * limitations under the License. */ package com.android.javacard.seprovider; +import org.globalplatform.upgrade.Element; + import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; import javacard.security.KeyPair; @@ -49,5 +51,23 @@ public ECPrivateKey getPrivateKey() { public ECPublicKey getPublicKey() { return (ECPublicKey) ecKeyPair.getPublic(); } + + public static void onSave(Element element, KMECDeviceUniqueKey kmKey) { + element.write(kmKey.ecKeyPair); + } + + public static KMECDeviceUniqueKey onRestore(KeyPair ecKey) { + if (ecKey == null) { + return null; + } + return new KMECDeviceUniqueKey(ecKey); + } + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java index 33372f97..0f9a7f8a 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java @@ -47,10 +47,11 @@ public static void onSave(Element element, KMECPrivateKey kmKey) { element.write(kmKey.ecKeyPair); } - public static KMECPrivateKey onRestore(Element element) { - KeyPair ecKey = (KeyPair) element.readObject(); - KMECPrivateKey kmKey = new KMECPrivateKey(ecKey); - return kmKey; + public static KMECPrivateKey onRestore(KeyPair ecKey) { + if (ecKey == null) { + return null; + } + return new KMECPrivateKey(ecKey); } public static short getBackupPrimitiveByteCount() { diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index a863bf88..bd4523a7 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -19,7 +19,7 @@ import javacard.security.HMACKey; -public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { +public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey, KMRkpMacKey { private HMACKey hmacKey; @@ -47,10 +47,11 @@ public static void onSave(Element element, KMHmacKey kmKey) { element.write(kmKey.hmacKey); } - public static KMHmacKey onRestore(Element element) { - HMACKey hmacKey = (HMACKey) element.readObject(); - KMHmacKey kmKey = new KMHmacKey(hmacKey); - return kmKey; + public static KMHmacKey onRestore(HMACKey hmacKey) { + if (hmacKey == null) { + return null; + } + return new KMHmacKey(hmacKey); } public static short getBackupPrimitiveByteCount() { diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java new file mode 100644 index 00000000..62116d24 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java @@ -0,0 +1,5 @@ +package com.android.javacard.seprovider; + + +public interface KMRkpMacKey { +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 18932ee5..4e7a229d 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -15,13 +15,15 @@ */ package com.android.javacard.seprovider; +import org.globalplatform.upgrade.Element; + /** * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to * abstract the cipher, signature and backup and restore related functions. The instance of this * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there * can be only one provider in the applet package. */ -public interface KMSEProvider extends KMUpgradable { +public interface KMSEProvider { /** * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it @@ -273,6 +275,25 @@ short hmacSign( short dataLength, byte[] signature, short signatureStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. + * + * @param hmacKey is the KMHmacKey. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacSign( + Object hmacKey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); /** * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive @@ -554,17 +575,6 @@ KMOperation initAsymmetricOperation( short pubModStart, short pubModLength); - /** - * This operation creates the empty instance of KMAttestationCert for rsa or ec public key - * attestation certificate. It corresponds to attestKEy command from keymaster hal specifications. - * The attestation certificate implementation will comply keymaster hal specifications. - * - * @param rsaCert if true indicates that certificate will attest a rsa public key else if false it - * is for ec public key. - * @return An empty instance of KMAttestationCert implementation. - */ - //KMAttestationCert getAttestationCert(boolean rsaCert); - /** * This function tells if applet is upgrading or not. * @@ -577,10 +587,11 @@ KMOperation initAsymmetricOperation( * generated key is maintained by the SEProvider. This function should be called only once at the * time of installation. * + * @param instance of the masterkey. * @param keySizeBits key size in bits. * @return An instance of KMMasterKey. */ - KMMasterKey createMasterKey(short keySizeBits); + KMMasterKey createMasterKey(KMMasterKey masterKey, short keySizeBits); /** * This function creates an HMACKey and initializes the key with the provided input key data. @@ -590,14 +601,7 @@ KMOperation initAsymmetricOperation( * @param length length of the buffer. * @return An instance of the KMComputedHmacKey. */ - KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length); - - /** - * Returns the master key. - * - * @return Instance of the KMMasterKey - */ - KMMasterKey getMasterKey(); + KMComputedHmacKey createComputedHmacKey(KMComputedHmacKey computedHmacKey, byte[] keyData, short offset, short length); /** * Returns true if factory provisioned attestation key is supported. @@ -610,54 +614,6 @@ KMOperation initAsymmetricOperation( */ short getAttestationKeyAlgorithm(); - /** - * Returns the preshared key. - * - * @return Instance of the KMPreSharedKey. - */ - KMPreSharedKey getPresharedKey(); - - /** - * Returns the value of the attestation id. - * - * @param tag - attestation id tag key as defined KMType. - * @param buffer - memorey buffer in which value of the id must be copied - * @param start - start offset in the buffer - * @return length - length of the returned attestation id value. - */ - short getAttestationId(short tag, byte[] buffer, short start); - - /** - * Delete the attestation ids permanently. - */ - void deleteAttestationIds(); - - /** - * Get Verified Boot hash. Part of RoT. Part of data sent by the aosp bootloader. - */ - short getVerifiedBootHash(byte[] buffer, short start); - - /** - * Get Boot Key. Part of RoT. Part of data sent by the aosp bootloader. - */ - short getBootKey(byte[] buffer, short start); - - /** - * Get Boot state. Part of RoT. Part of data sent by the aosp bootloader. - */ - short getBootState(); - - /** - * Returns true if device bootloader is locked. Part of RoT. Part of data sent by the aosp - * bootloader. - */ - boolean isDeviceBootLocked(); - - /** - * Get Boot patch level. Part of data sent by the aosp bootloader. - */ - short getBootPatchLevel(byte[] buffer, short start); - /** * Creates an ECKey instance and sets the public and private keys to it. * @@ -670,69 +626,98 @@ KMOperation initAsymmetricOperation( * @param privKeyLen private key buffer length. * @return instance of KMDeviceUniqueKey. */ - KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + KMDeviceUniqueKey createDeviceUniqueKey(KMDeviceUniqueKey key, byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, short privKeyOff, short privKeyLen); - + + /** + * This is a one-shot operation the does digest of the input mesage. + * + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. + */ + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, + short outOffset); + /** - * Returns the instance KMDeviceUnique if it is created. + * This function creates an ECKey and initializes the ECPrivateKey with the provided input key + * data. The initialized Key is maintained by the SEProvider. This function should be called only + * while provisioning the attestation key. * - * @param testMode Indicates if current execution is for test or production. - * @return instance of KMDeviceUniqueKey if present; null otherwise. + * @param keyData buffer containing the ec private key. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMAttestationKey. */ - KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode); - + KMAttestationKey createAttestationKey(KMAttestationKey attestationKey, byte[] keyData, + short offset, + short length); + /** - * Persists the additional certificate chain in persistent memory. + * This function generates a HMAC key from the provided key buffers. * - * @param buf buffer containing the cbor encoded additional certificate chain. + * @param presharedKey instance of the presharedkey. + * @param key buffer containing the key data. * @param offset start offset of the buffer. - * @param len length of the buffer. + * @param length is the length of the key. + * @return instance of KMPresharedKey. */ - void persistAdditionalCertChain(byte[] buf, short offset, short len); - + KMPreSharedKey createPreSharedKey(KMPreSharedKey presharedKey, byte[] key, short offset, + short length); + /** - * Returns the additional certificate chain length. + * This function saves the key objects while upgrade. * - * @return length of the encoded additional certificate chain. + * @param element instance of the Element class where the objects to be stored. + * @param interfaceType the type interface of the parent object. + * @param object instance of the object to be saved. */ - short getAdditionalCertChainLength(); - + void onSave(Element element, byte interfaceType, Object object); + + /** + * This function restores the the object from element instance. + * + * @param element instance of the Element class. + * @return restored object. + */ + Object onResore(Element element); + /** - * Returns the additional certificate chain. + * This function returns the count of the primitive bytes required to + * be stored by the implementation of the interface type. * - * @return additional cert chain. + * @param interfaceType type interface of the parent object. + * @return count of the primitive bytes. */ - byte[] getAdditionalCertChain(); - + short getBackupPrimitiveByteCount(byte interfaceType); /** - * Returns the boot certificate chain. + * This function returns the object count required to be stored by the + * implementation of the interface type. * - * @return boot certificate chain. + * @param interfaceType type interface of the parent object. + * @return count of the objects. */ - byte[] getBootCertificateChain(); + short getBackupObjectCount(byte interfaceType); /** - * Returns the computed Hmac key. + * This function creates an HMACKey and initializes the key with the provided input key data. * - * @return Instance of the computed hmac key. + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMRkpMacKey. */ - KMComputedHmacKey getComputedHmacKey(); - + KMRkpMacKey createRkpMacKey(KMRkpMacKey createComputedHmacKey, byte[] keyData, + short offset, short length); + /** - * This is a one-shot operation the does digest of the input mesage. - * - * @param inBuff input buffer to be digested. - * @param inOffset start offset of the input buffer. - * @param inLength length of the input buffer. - * @param outBuff is the output buffer that contains the digested data. - * @param outOffset start offset of the digested output buffer. - * @return length of the digested data. + * Returns true if provision is locked. */ - short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, - short outOffset); - public boolean isProvisionLocked(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 295d6e96..fc0528fd 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -216,23 +216,26 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static KMRepository repository; protected static KMSEProvider seProvider; protected static KMOperationState[] opTable; + protected static KMKeymintDataStore kmDataStore; protected static short[] tmpVariables; protected static short[] data; protected static byte[] wrappingKey; + public static void install(byte[] bArray, short bOffset, byte bLength) { + new KMAndroidSEApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]); + } + /** * Registers this applet. */ protected KMKeymasterApplet(KMSEProvider seImpl) { seProvider = seImpl; boolean isUpgrading = seProvider.isUpgrading(); - if (!isUpgrading) { - seProvider.createMasterKey(MASTER_KEY_SIZE); - } repository = new KMRepository(isUpgrading); encoder = new KMEncoder(); decoder = new KMDecoder(); + kmDataStore = new KMKeymintDataStore(seProvider, repository); data = JCSystem.makeTransientShortArray(DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); tmpVariables = JCSystem.makeTransientShortArray(TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); @@ -244,18 +247,20 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { opTable[index] = new KMOperationState(); index++; } - KMType.initialize(); - - // initialize default values - initHmacNonceAndSeed(); - initSystemBootParams((short)0,(short)0,(short)0,(short)0); - rkp = new RemotelyProvisionedComponentDevice(encoder, decoder, repository, seProvider); + KMType.initialize(); + if (!isUpgrading) { + kmDataStore.createMasterKey(MASTER_KEY_SIZE); + // initialize default values + initHmacNonceAndSeed(); + initSystemBootParams((short)0,(short)0,(short)0,(short)0); + } + rkp = new RemotelyProvisionedComponentDevice(encoder, decoder, repository, seProvider, kmDataStore); } protected void initHmacNonceAndSeed(){ short nonce = repository.alloc((short)32); - seProvider.newRandomNumber(repository.getHeap(), nonce, KMRepository.HMAC_SEED_NONCE_SIZE); - repository.initHmacNonce(repository.getHeap(), nonce, KMRepository.HMAC_SEED_NONCE_SIZE); + seProvider.newRandomNumber(repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE); + kmDataStore.initHmacNonce(repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE); } private void releaseAllOperations(){ @@ -530,7 +535,7 @@ private void freeOperations() { } private void processEarlyBootEndedCmd(APDU apdu) { - repository.setEarlyBootEndedStatus(true); + kmDataStore.setEarlyBootEndedStatus(true); } private short deviceLockedCmd(APDU apdu){ @@ -551,13 +556,13 @@ private void processDeviceLockedCmd(APDU apdu) { passwordOnly = KMInteger.cast(passwordOnly).getByte(); validateVerificationToken(verToken, scratchPad); short verTime = KMVerificationToken.cast(verToken).getTimestamp(); - short lastDeviceLockedTime = repository.getDeviceTimeStamp(); + short lastDeviceLockedTime = kmDataStore.getDeviceTimeStamp(); if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) { Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); KMInteger.cast(verTime).getValue(scratchPad, (short) 0, (short) 8); - repository.setDeviceLock(true); - repository.setDeviceLockPasswordOnly(passwordOnly == 0x01); - repository.setDeviceLockTimestamp(scratchPad, (short) 0, (short) 8); + kmDataStore.setDeviceLock(true); + kmDataStore.setDeviceLockPasswordOnly(passwordOnly == 0x01); + kmDataStore.setDeviceLockTimestamp(scratchPad, (short) 0, (short) 8); } sendError(apdu, KMError.OK); } @@ -727,7 +732,7 @@ private void processGetHmacSharingParamCmd(APDU apdu) { // No Arguments // Create HMAC Sharing Parameters short params = KMHmacSharingParameters.instance(); - short nonce = repository.getHmacNonce(); + short nonce = kmDataStore.getHmacNonce(); short seed = KMByteBlob.instance((short) 0); KMHmacSharingParameters.cast(params).setNonce(nonce); KMHmacSharingParameters.cast(params).setSeed(seed); @@ -808,7 +813,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { //tmpVariables[7] = 0; short found = 0; //tmpVariables[9] - short nonce = repository.getHmacNonce(); + short nonce = kmDataStore.getHmacNonce(); while (paramIndex < paramsLen) { // read HmacSharingParam @@ -873,7 +878,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { //tmpVariables[6] short keyLen = seProvider.cmacKDF( - seProvider.getPresharedKey(), + kmDataStore.getPresharedKey(), ckdfLable, (short) 0, (short) ckdfLable.length, @@ -884,11 +889,11 @@ private void processComputeSharedHmacCmd(APDU apdu) { (short) 0); // persist the computed hmac key. - seProvider.createComputedHmacKey(scratchPad, (short) 0, keyLen); + kmDataStore.createComputedHmacKey(scratchPad, (short) 0, keyLen); // Generate sharingKey verification signature and store that in scratch pad. //tmpVariables[5] short signLen = - seProvider.hmacSign( + seProvider.hmacSign( scratchPad, (short) 0, keyLen, @@ -962,12 +967,12 @@ private void processUpgradeKeyCmd(APDU apdu) { parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); boolean isKeyUpgradeRequired = false; // Check if key requires upgrade. - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, kmDataStore.getOsVersion()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, kmDataStore.getOsPatch()); isKeyUpgradeRequired |= - isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); + isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, kmDataStore.getVendorPatchLevel()); // Get boot patch level. - seProvider.getBootPatchLevel(scratchPad, (short) 0); + kmDataStore.getBootPatchLevel(scratchPad, (short) 0); isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, KMInteger.uint_32(scratchPad, (short) 0)); @@ -1308,11 +1313,11 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara addTags(hwParameters, true, cert); addTags(swParameters, false, cert); // Add Device Boot locked status - cert.deviceLocked(seProvider.isDeviceBootLocked()); + cert.deviceLocked(kmDataStore.isDeviceBootLocked()); // VB data cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); cert.verifiedBootKey(getBootKey(scratchPad)); - cert.verifiedBootState((byte)seProvider.getBootState()); + cert.verifiedBootState((byte)kmDataStore.getBootState()); //TODO remove the following line makeKeyCharacteristics(scratchPad); @@ -1554,7 +1559,7 @@ private KMAttestationCert makeSelfSignedCert(short attPrivKey, short attPubKey, protected short getBootKey(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_KEY_SIZE, (byte)0); - short len = seProvider.getBootKey(scratchPad, (short) 0); + short len = kmDataStore.getBootKey(scratchPad, (short) 0); if(len != VERIFIED_BOOT_KEY_SIZE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -1563,7 +1568,7 @@ protected short getBootKey(byte[] scratchPad){ protected short getVerifiedBootHash(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_HASH_SIZE, (byte)0); - short len = seProvider.getVerifiedBootHash(scratchPad, (short) 0); + short len = kmDataStore.getVerifiedBootHash(scratchPad, (short) 0); if(len != VERIFIED_BOOT_HASH_SIZE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -1595,7 +1600,7 @@ private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) { attIdTag = KMKeyParameters.findTag(KMType.BYTES_TAG, attTags[index], data[KEY_PARAMETERS]); if (attIdTag != KMType.INVALID_VALUE) { attIdTagValue = KMByteTag.cast(attIdTag).getValue(); - storedAttIdLen = seProvider.getAttestationId(attTags[index],scratchPad, (short)0); + storedAttIdLen = kmDataStore.getAttestationId(attTags[index],scratchPad, (short)0); // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or // Attestation IDs are deleted. if (storedAttIdLen == 0) { @@ -1656,11 +1661,11 @@ private static void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { cert.makeUniqueId(scratchPad, (short) 0, KMInteger.cast(time).getBuffer(), KMInteger.cast(time).getStartOff(), KMInteger.cast(time).length(), KMByteBlob.cast(appId).getBuffer(), KMByteBlob.cast(appId).getStartOff(), KMByteBlob.cast(appId).length(), resetAfterRotation, - seProvider.getMasterKey()); + kmDataStore.getMasterKey()); } private void processDestroyAttIdsCmd(APDU apdu) { - seProvider.deleteAttestationIds(); + kmDataStore.deleteAttestationIds(); sendError(apdu, KMError.OK); } @@ -1974,9 +1979,9 @@ private void authorizeKeyUsageForCount(byte[] scratchPad) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (repository.isAuthTagPersisted(data[AUTH_TAG])) { + if (kmDataStore.isAuthTagPersisted(data[AUTH_TAG])) { // Get current counter, update and increment it. - short len = repository + short len = kmDataStore .getRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4)); if (len != 4) { KMException.throwIt(KMError.UNKNOWN_ERROR); @@ -1991,12 +1996,12 @@ private void authorizeKeyUsageForCount(byte[] scratchPad) { KMUtils.add(scratchPad, scratchPadOff, (short) (scratchPadOff + len), (short) (scratchPadOff + len * 2)); - repository + kmDataStore .setRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), len); } else { // Persist auth tag. - if (!repository.persistAuthTag(data[AUTH_TAG])) { + if (!kmDataStore.persistAuthTag(data[AUTH_TAG])) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); } } @@ -2009,19 +2014,19 @@ private void authorizeDeviceUnlock(byte[] scratchPad) { KMKeyParameters.findTag( KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]); - if (ptr != KMType.INVALID_VALUE && repository.getDeviceLock()) { + if (ptr != KMType.INVALID_VALUE && kmDataStore.getDeviceLock()) { if (!validateHwToken(data[HW_TOKEN], scratchPad)) { KMException.throwIt(KMError.DEVICE_LOCKED); } ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(); // Check if the current auth time stamp is greater than device locked time stamp - short ts = repository.getDeviceTimeStamp(); + short ts = kmDataStore.getDeviceTimeStamp(); if (KMInteger.compare(ptr, ts) <= 0) { KMException.throwIt(KMError.DEVICE_LOCKED); } // Now check if the device unlock requires password only authentication and whether // auth token is generated through password authentication or not. - if (repository.getDeviceLockPasswordOnly()) { + if (kmDataStore.getDeviceLockPasswordOnly()) { ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); ptr = KMEnum.cast(ptr).getVal(); if (((byte) ptr & KMType.PASSWORD) == 0) { @@ -2030,8 +2035,8 @@ private void authorizeDeviceUnlock(byte[] scratchPad) { } // Unlock the device // repository.deviceLockedFlag = false; - repository.setDeviceLock(false); - repository.clearDeviceLockTimeStamp(); + kmDataStore.setDeviceLock(false); + kmDataStore.clearDeviceLockTimeStamp(); } } @@ -2060,7 +2065,7 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scr ptr = KMVerificationToken.cast(verToken).getMac(); return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), + kmDataStore.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -2615,13 +2620,13 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) //Validate early boot //VTS expects error code EARLY_BOOT_ONLY during begin operation if eary boot ended tag is present - if (repository.getEarlyBootEndedStatus()) { + if (kmDataStore.getEarlyBootEndedStatus()) { KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); } //Validate bootloader only - if (repository.getBootEndedStatus()) { + if (kmDataStore.getBootEndedStatus()) { KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); } @@ -2767,7 +2772,7 @@ private void beginTrustedConfirmationOperation(KMOperationState op) { data[HW_PARAMETERS]) != KMType.INVALID_VALUE) { op.setTrustedConfirmationSigner( - seProvider.initTrustedConfirmationSymmetricOperation(seProvider.getComputedHmacKey())); + seProvider.initTrustedConfirmationSymmetricOperation(kmDataStore.getComputedHmacKey())); op.getTrustedConfirmationSigner().update( confirmationToken, @@ -2987,7 +2992,7 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { ptr = KMHardwareAuthToken.cast(hwToken).getMac(); return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), + kmDataStore.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -3028,7 +3033,7 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) ptr = KMHardwareAuthToken.cast(hwToken).getMac(); return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), + kmDataStore.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -3470,17 +3475,17 @@ private void processInitStrongBoxCmd(APDU apdu) { } public void reboot() { - repository.clearHmacNonce(); + kmDataStore.clearHmacNonce(); //flag to maintain the boot state - repository.setBootEndedStatus(false); + kmDataStore.setBootEndedStatus(false); //flag to maintain early boot ended state - repository.setEarlyBootEndedStatus(false); + kmDataStore.setEarlyBootEndedStatus(false); //Clear all the operation state. releaseAllOperations(); // Hmac is cleared, so generate a new Hmac nonce. initHmacNonceAndSeed(); // Clear all auth tags. - repository.removeAllAuthTags(); + kmDataStore.removeAllAuthTags(); } protected void initSystemBootParams(short osVersion, @@ -3494,21 +3499,21 @@ protected void initSystemBootParams(short osVersion, } protected void setOsVersion(short version){ - repository.setOsVersion( + kmDataStore.setOsVersion( KMInteger.cast(version).getBuffer(), KMInteger.cast(version).getStartOff(), KMInteger.cast(version).length()); } protected void setOsPatchLevel(short patch){ - repository.setOsPatch( + kmDataStore.setOsPatch( KMInteger.cast(patch).getBuffer(), KMInteger.cast(patch).getStartOff(), KMInteger.cast(patch).length()); } protected void setVendorPatchLevel(short patch){ - repository.setVendorPatchLevel( + kmDataStore.setVendorPatchLevel( KMInteger.cast(patch).getBuffer(), KMInteger.cast(patch).getStartOff(), KMInteger.cast(patch).length()); @@ -3859,7 +3864,7 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); if (len != KMType.INVALID_VALUE) { - short provOsVersion = repository.getOsVersion(); + short provOsVersion = kmDataStore.getOsVersion(); short status = KMInteger.unsignedByteArrayCompare( KMInteger.cast(provOsVersion).getBuffer(), @@ -3878,7 +3883,7 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); if (len != KMType.INVALID_VALUE) { - short osPatch = repository.getOsPatch(); + short osPatch = kmDataStore.getOsPatch(); short status = KMInteger.unsignedByteArrayCompare( KMInteger.cast(osPatch).getBuffer(), @@ -3895,7 +3900,7 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { } protected static short getBootPatchLevel(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad,(short)0, BOOT_PATCH_LVL_SIZE, (byte)0); - short len = seProvider.getBootPatchLevel(scratchPad,(short)0); + short len = kmDataStore.getBootPatchLevel(scratchPad,(short)0); if(len != BOOT_PATCH_LVL_SIZE){ KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -3903,9 +3908,9 @@ protected static short getBootPatchLevel(byte[] scratchPad){ } private static void makeKeyCharacteristics(byte[] scratchPad) { - short osVersion = repository.getOsVersion(); - short osPatch = repository.getOsPatch(); - short vendorPatch = repository.getVendorPatchLevel(); + 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); @@ -3998,11 +4003,11 @@ private short parseEncryptedKeyBlob(short keyBlob, short appId, short appData, b // Read RoT public static short readROT(byte[] scratchPad) { Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256,(byte)0); - short len = seProvider.getBootKey(scratchPad, (short)0); - len += seProvider.getVerifiedBootHash(scratchPad, (short)len); - short bootState = seProvider.getBootState(); + short len = kmDataStore.getBootKey(scratchPad, (short)0); + len += kmDataStore.getVerifiedBootHash(scratchPad, (short)len); + short bootState = kmDataStore.getBootState(); len = Util.setShort(scratchPad, len, bootState); - if(seProvider.isDeviceBootLocked()){ + if(kmDataStore.isDeviceBootLocked()){ scratchPad[len] = (byte)1; }else{ scratchPad[len] = (byte)0; @@ -4118,7 +4123,7 @@ private static short deriveKey(byte[] scratchPad) { // Consume only first 16 bytes as derived key. // Hmac sign. short len = seProvider.hmacKDF( - seProvider.getMasterKey(), + kmDataStore.getMasterKey(), repository.getHeap(), data[AUTH_DATA], data[AUTH_DATA_LENGTH], @@ -4307,7 +4312,7 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { if (!testMode && seProvider.isProvisionLocked()) { KMException.throwIt(KMError.STATUS_FAILED); } - KMDeviceUniqueKey deviceUniqueKey = seProvider.getDeviceUniqueKey(testMode); + KMDeviceUniqueKey deviceUniqueKey = kmDataStore.getDeviceUniqueKey(testMode); short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); short coseKey = KMCose.constructCoseKey( diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java new file mode 100644 index 00000000..94b7d6ec --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -0,0 +1,844 @@ +package com.android.javacard.keymaster; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.seprovider.KMAESKey; +import com.android.javacard.seprovider.KMAttestationKey; +import com.android.javacard.seprovider.KMComputedHmacKey; +import com.android.javacard.seprovider.KMDataStoreConstants; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMECDeviceUniqueKey; +import com.android.javacard.seprovider.KMECPrivateKey; +import com.android.javacard.seprovider.KMError; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMHmacKey; +import com.android.javacard.seprovider.KMMasterKey; +import com.android.javacard.seprovider.KMPreSharedKey; +import com.android.javacard.seprovider.KMRkpMacKey; +import com.android.javacard.seprovider.KMSEProvider; +import com.android.javacard.seprovider.KMType; +import com.android.javacard.seprovider.KMUpgradable; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.HMACKey; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; + +public class KMKeymintDataStore implements KMUpgradable { + + // Data table configuration + public static final short DATA_INDEX_SIZE = 17; + public static final short DATA_INDEX_ENTRY_SIZE = 4; + public static final short DATA_INDEX_ENTRY_LENGTH = 0; + public static final short DATA_INDEX_ENTRY_OFFSET = 2; + + //TODO reduced data table size from 2048 to 300. + public static final short DATA_MEM_SIZE = 300; + + // Data table offsets + public static final byte COMPUTED_HMAC_KEY = 0; + public static final byte HMAC_NONCE = 1; + public static final byte BOOT_OS_VERSION = 2; + public static final byte BOOT_OS_PATCH_LEVEL = 3; + public static final byte VENDOR_PATCH_LEVEL = 4; + public static final byte DEVICE_LOCKED_TIME = 5; + public static final byte DEVICE_LOCKED = 6; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + public static final byte AUTH_TAG_1 = 8; + public static final byte BOOT_ENDED_FLAG = 15; + public static final byte EARLY_BOOT_ENDED_FLAG = 16; + + // Data Item sizes + public static final short HMAC_SEED_NONCE_SIZE = 32; + public static final short COMPUTED_HMAC_KEY_SIZE = 32; + public static final short OS_VERSION_SIZE = 4; + public static final short OS_PATCH_SIZE = 4; + public static final short VENDOR_PATCH_SIZE = 4; + public static final short DEVICE_LOCK_TS_SIZE = 8; + public static final short MAX_BLOB_STORAGE = 8; + public static final short AUTH_TAG_LENGTH = 16; + public static final short AUTH_TAG_COUNTER_SIZE = 4; + public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); + private static final short MASTER_KEY_SIZE = 16; + private static final short SHARED_SECRET_KEY_SIZE = 32; + + private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512;//First 2 bytes for length. + private static final short BCC_MAX_SIZE = 512; + + // Data - originally was in repository + private byte[] attIdBrand; + private byte[] attIdDevice; + private byte[] attIdProduct; + private byte[] attIdSerial; + private byte[] attIdImei; + private byte[] attIdMeId; + private byte[] attIdManufacturer; + private byte[] attIdModel; + + // Boot parameters + private byte[] verifiedHash; + private byte[] bootKey; + private byte[] bootPatchLevel; + private boolean deviceBootLocked; + private short bootState; + + private byte[] dataTable; + private short dataIndex; + private KMSEProvider seProvider; + private KMRepository repository; + private byte[] additionalCertChain; + private byte[] bcc; + private KMMasterKey masterKey; + private KMDeviceUniqueKey testDeviceUniqueKey; + private KMDeviceUniqueKey deviceUniqueKey; + private KMPreSharedKey preSharedKey; + private KMComputedHmacKey computedHmacKey; + private KMRkpMacKey rkpMacKey; + + public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { + seProvider = provider; + repository = repo; + boolean isUpgrading = provider.isUpgrading(); + initDataTable(isUpgrading); + //Initialize the device locked status + if (!isUpgrading) { + additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; + bcc = new byte[BCC_MAX_SIZE]; + setDeviceLock(false); + setDeviceLockPasswordOnly(false); + } +// initializeCertificateDataBuffer(isUpgrading, factoryAttestSupport); + } + + private void initDataTable(boolean isUpgrading) { + if (!isUpgrading) { + if (dataTable == null) { + dataTable = new byte[DATA_MEM_SIZE]; + dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + } + } + } + + private short dataAlloc(short length) { + if (((short) (dataIndex + length)) > dataTable.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + dataIndex += length; + return (short) (dataIndex - length); + } + + private void clearDataEntry(short id) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (dataLen != 0) { + short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0); + JCSystem.commitTransaction(); + } + } + + private void writeDataEntry(short id, byte[] buf, short offset, short len) { + short dataPtr; + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (dataLen == 0) { + dataPtr = dataAlloc(len); + JCSystem.beginTransaction(); + Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); + Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len); + Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); + } else { + if (len != dataLen) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); + Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); + } + } + + private short readDataEntry(short id, byte[] buf, short offset) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (len != 0) { + Util.arrayCopyNonAtomic( + dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), + buf, + offset, + len); + } + return len; + } + + private short dataLength(short id) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + } + + public short readData(short id) { + short len = dataLength(id); + if (len != 0) { + short blob = KMByteBlob.instance(dataLength(id)); + readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + return blob; + } + return KMType.INVALID_VALUE; + } + + public short getHmacNonce() { + return readData(HMAC_NONCE); + } + + private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; + + public short getOsVersion() { + short blob = readData(BOOT_OS_VERSION); + if (blob != KMType.INVALID_VALUE) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } else { + return KMInteger.uint_32(zero, (short) 0); + } + } + + public short getVendorPatchLevel() { + short blob = readData(VENDOR_PATCH_LEVEL); + if (blob != KMType.INVALID_VALUE) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } else { + return KMInteger.uint_32(zero, (short) 0); + } + } + + public short getOsPatch() { + short blob = readData(BOOT_OS_PATCH_LEVEL); + if (blob != KMType.INVALID_VALUE) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } else { + return KMInteger.uint_32(zero, (short) 0); + } + } + + private boolean readBoolean(short id) { + short blob = readData(id); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return (byte) ((repository.getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; + } + + public boolean getDeviceLock() { + return readBoolean(DEVICE_LOCKED); + } + + public boolean getDeviceLockPasswordOnly() { + return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY); + } + + public boolean getEarlyBootEndedStatus() { + return readBoolean(EARLY_BOOT_ENDED_FLAG); + } + + public boolean getBootEndedStatus() { + return readBoolean(BOOT_ENDED_FLAG); + } + + public short getDeviceTimeStamp() { + short blob = readData(DEVICE_LOCKED_TIME); + if (blob != KMType.INVALID_VALUE) { + return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff()); + } else { + return KMInteger.uint_64(zero, (short) 0); + } + } + + public void setOsVersion(byte[] buf, short start, short len) { + if (len != OS_VERSION_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + writeDataEntry(BOOT_OS_VERSION, buf, start, len); + } + + public void setVendorPatchLevel(byte[] buf, short start, short len) { + if (len != VENDOR_PATCH_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len); + } + + private void writeBoolean(short id, boolean flag) { + short start = repository.alloc((short) 1); + if (flag) { + (repository.getHeap())[start] = (byte) 0x01; + } else { + (repository.getHeap())[start] = (byte) 0x00; + } + writeDataEntry(id, repository.getHeap(), start, (short) 1); + } + + public void setDeviceLock(boolean flag) { + writeBoolean(DEVICE_LOCKED, flag); + } + + public void setDeviceLockPasswordOnly(boolean flag) { + writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag); + } + + public void setDeviceLockTimestamp(byte[] buf, short start, short len) { + if (len != DEVICE_LOCK_TS_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len); + } + + public void setEarlyBootEndedStatus(boolean flag) { + writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); + } + + public void setBootEndedStatus(boolean flag) { + writeBoolean(BOOT_ENDED_FLAG, flag); + } + + public void clearDeviceLockTimeStamp() { + clearDataEntry(DEVICE_LOCKED_TIME); + } + + public void setOsPatch(byte[] buf, short start, short len) { + if (len != OS_PATCH_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len); + } + + private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) { + readDataEntry(tagId, buf, offset); + return (0 == buf[offset]); + } + + public void initHmacNonce(byte[] nonce, short offset, short len) { + if (len != HMAC_SEED_NONCE_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + writeDataEntry(HMAC_NONCE, nonce, offset, len); + } + + public void clearHmacNonce() { + clearDataEntry(HMAC_NONCE); + } + + public boolean persistAuthTag(short authTag) { + + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + + short authTagEntry = repository.alloc(AUTH_TAG_ENTRY_SIZE); + short scratchPadOff = repository.alloc(AUTH_TAG_ENTRY_SIZE); + byte[] scratchPad = repository.getHeap(); + writeAuthTagState(repository.getHeap(), authTagEntry, (byte) 1); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + repository.getHeap(), (short) (authTagEntry + 1), AUTH_TAG_LENGTH); + Util.setShort(repository.getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), + (short) 1); + short index = 0; + while (index < MAX_BLOB_STORAGE) { + if ((dataLength((short) (index + AUTH_TAG_1)) == 0) || + isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) { + + writeDataEntry((short) (index + AUTH_TAG_1), repository.getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE); + return true; + } + index++; + } + return false; + } + + public void removeAllAuthTags() { + short index = 0; + while (index < MAX_BLOB_STORAGE) { + clearDataEntry((short) (index + AUTH_TAG_1)); + index++; + } + } + + public boolean isAuthTagPersisted(short authTag) { + return (KMType.INVALID_VALUE != findTag(authTag)); + } + + private short findTag(short authTag) { + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + short index = 0; + short found; + short offset = repository.alloc(AUTH_TAG_ENTRY_SIZE); + while (index < MAX_BLOB_STORAGE) { + if (dataLength((short) (index + AUTH_TAG_1)) != 0) { + readDataEntry((short) (index + AUTH_TAG_1), + repository.getHeap(), offset); + found = + Util.arrayCompare( + repository.getHeap(), + (short) (offset + 1), + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AUTH_TAG_LENGTH); + if (found == 0) { + return (short) (index + AUTH_TAG_1); + } + } + index++; + } + return KMType.INVALID_VALUE; + } + + public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) { + short tag = findTag(authTag); + short blob; + if (tag != KMType.INVALID_VALUE) { + blob = readData(tag); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(blob).getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1), + out, + outOff, + AUTH_TAG_COUNTER_SIZE); + return AUTH_TAG_COUNTER_SIZE; + } + return (short) 0; + } + + public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) { + short tag = findTag(authTag); + if (tag != KMType.INVALID_VALUE) { + short dataPtr = readData(tag); + Util.arrayCopyNonAtomic( + buf, + off, + KMByteBlob.cast(dataPtr).getBuffer(), + (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1), + len); + writeDataEntry(tag, + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + KMByteBlob.cast(dataPtr).length()); + } + } + + public void persistAdditionalCertChain(byte[] buf, short offset, short len) { + // Input buffer contains encoded additional certificate chain as shown below. + // AdditionalDKSignatures = { + // + SignerName => DKCertChain + // } + // SignerName = tstr + // DKCertChain = [ + // 2* Certificate // Root -> Leaf. Root is the vendo r + // // self-signed cert, leaf contains DK_pu b + // ] + // Certificate = COSE_Sign1 of a public key + if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(additionalCertChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, + (short) 2, len); + JCSystem.commitTransaction(); + + } + + public short getAdditionalCertChainLength() { + return Util.getShort(additionalCertChain, (short) 0); + } + + public byte[] getAdditionalCertChain() { + return additionalCertChain; + } + + public byte[] getBootCertificateChain() { + return bcc; + } + + public void persistBootCertificateChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > BCC_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(bcc, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, bcc, + (short) 2, len); + JCSystem.commitTransaction(); + } + + private void writeAuthTagState(byte[] buf, short offset, byte state) { + buf[offset] = state; + } + + public KMMasterKey createMasterKey(short keySizeBits) { + if (masterKey == null) { + masterKey = seProvider.createMasterKey(masterKey, keySizeBits); + } + return (KMMasterKey) masterKey; + } + + public KMMasterKey getMasterKey() { + return masterKey; + } + + public void createPresharedKey(byte[] keyData, short offset, short length) { + if (length != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if (preSharedKey == null) { + preSharedKey = seProvider.createPreSharedKey(preSharedKey, keyData, offset, length); + } + } + + public KMPreSharedKey getPresharedKey() { + return preSharedKey; + } + + public void createComputedHmacKey(byte[] keyData, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if (computedHmacKey == null) { + computedHmacKey = seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length); + } else { + seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length); + } + } + + public KMComputedHmacKey getComputedHmacKey() { + return computedHmacKey; + } + + public KMDeviceUniqueKey createTestDeviceUniqueKey(byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, short privKeyOff, short privKeyLen) { + if (testDeviceUniqueKey == null) { + testDeviceUniqueKey = seProvider.createDeviceUniqueKey(testDeviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, + privKeyOff, privKeyLen); + } else { + seProvider.createDeviceUniqueKey(testDeviceUniqueKey, pubKey, pubKeyOff, pubKeyLen, privKey, + privKeyOff, + privKeyLen); + } + return testDeviceUniqueKey; + } + + public KMDeviceUniqueKey createDeviceUniqueKey(byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, short privKeyOff, + short privKeyLen) { + if (deviceUniqueKey == null) { + deviceUniqueKey = seProvider.createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, + privKeyOff, privKeyLen); + } else { + seProvider.createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, pubKeyLen, privKey, + privKeyOff, privKeyLen); + } + return deviceUniqueKey; + } + + public KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode) { + return ((KMDeviceUniqueKey) (testMode ? testDeviceUniqueKey : deviceUniqueKey)); + } + + public void createRkpMacKey(byte[] keydata, short offset, short length) { + if (rkpMacKey == null) { + rkpMacKey = seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length); + } else { + seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length); + } + } + + public KMRkpMacKey getRkpMacacKey() { + return rkpMacKey; + } + + public short getAttestationId(short tag, byte[] buffer, short start) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + Util.arrayCopyNonAtomic(attIdBrand, (short) 0, buffer, start, (short) attIdBrand.length); + return (short) attIdBrand.length; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + Util.arrayCopyNonAtomic(attIdDevice, (short) 0, buffer, start, (short) attIdDevice.length); + return (short) attIdDevice.length; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + Util.arrayCopyNonAtomic(attIdProduct, (short) 0, buffer, start, + (short) attIdProduct.length); + return (short) attIdProduct.length; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + Util.arrayCopyNonAtomic(attIdSerial, (short) 0, buffer, start, (short) attIdSerial.length); + return (short) attIdSerial.length; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + Util.arrayCopyNonAtomic(attIdImei, (short) 0, buffer, start, (short) attIdImei.length); + return (short) attIdImei.length; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + Util.arrayCopyNonAtomic(attIdMeId, (short) 0, buffer, start, (short) attIdMeId.length); + return (short) attIdMeId.length; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + Util.arrayCopyNonAtomic(attIdManufacturer, (short) 0, buffer, start, + (short) attIdManufacturer.length); + return (short) attIdManufacturer.length; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + Util.arrayCopyNonAtomic(attIdModel, (short) 0, buffer, start, (short) attIdModel.length); + return (short) attIdModel.length; + } + return (short) 0; + } + + public void setAttestationId(short tag, byte[] buffer, short start, short length) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + JCSystem.beginTransaction(); + attIdBrand = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + JCSystem.beginTransaction(); + attIdDevice = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + JCSystem.beginTransaction(); + attIdProduct = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + JCSystem.beginTransaction(); + attIdSerial = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + JCSystem.beginTransaction(); + attIdImei = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + JCSystem.beginTransaction(); + attIdMeId = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + JCSystem.beginTransaction(); + attIdManufacturer = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + JCSystem.beginTransaction(); + attIdModel = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length); + JCSystem.commitTransaction(); + break; + } + } + + public void deleteAttestationIds() { + attIdBrand = null; + attIdDevice = null; + attIdProduct = null; + attIdSerial = null; + attIdImei = null; + attIdMeId = null; + attIdManufacturer = null; + attIdModel = null; + } + + public short getVerifiedBootHash(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length); + return (short) verifiedHash.length; + } + + public short getBootKey(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); + return (short) bootKey.length; + } + + public short getBootState() { + return bootState; + } + + public boolean isDeviceBootLocked() { + return deviceBootLocked; + } + + public short getBootPatchLevel(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootPatchLevel, (short) 0, buffer, start, + (short) bootPatchLevel.length); + return (short) bootPatchLevel.length; + } + + public void setVerifiedBootHash(byte[] buffer, short start, short length) { + if (verifiedHash == null) { + verifiedHash = new byte[32]; + } + if (length != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, verifiedHash, (short) 0, (short) 32); + } + + public void setBootKey(byte[] buffer, short start, short length) { + if (bootKey == null) { + bootKey = new byte[32]; + } + if (length != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootKey, (short) 0, (short) 32); + } + + public void setBootState(short state) { + bootState = state; + } + + public void setDeviceLocked(boolean state) { + deviceBootLocked = state; + } + + public void setBootPatchLevel(byte[] buffer, short start, short length) { + if (bootPatchLevel == null) { + bootPatchLevel = new byte[4]; + } + if (length > 4 || length < 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) length); + } + + + @Override + public void onSave(Element element) { + // Prmitives + element.write(dataIndex); + element.write(deviceBootLocked); + element.write(bootState); + // Objects + element.write(dataTable); + // element.write(certificateData); + element.write(attIdBrand); + element.write(attIdDevice); + element.write(attIdProduct); + element.write(attIdSerial); + element.write(attIdImei); + element.write(attIdMeId); + element.write(attIdManufacturer); + element.write(attIdModel); + element.write(verifiedHash); + element.write(bootKey); + element.write(bootPatchLevel); + element.write(additionalCertChain); + element.write(bcc); + // Key Objects + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY, + computedHmacKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY, deviceUniqueKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY, rkpMacKey); + } + + @Override + public void onRestore(Element element) { + // Read Primitives + dataIndex = element.readShort(); + deviceBootLocked = element.readBoolean(); + bootState = element.readShort(); + // Read Objects + dataTable = (byte[]) element.readObject(); +// certificateData = (byte[]) element.readObject(); + attIdBrand = (byte[]) element.readObject(); + attIdDevice = (byte[]) element.readObject(); + attIdProduct = (byte[]) element.readObject(); + attIdSerial = (byte[]) element.readObject(); + attIdImei = (byte[]) element.readObject(); + attIdMeId = (byte[]) element.readObject(); + attIdManufacturer = (byte[]) element.readObject(); + attIdModel = (byte[]) element.readObject(); + verifiedHash = (byte[]) element.readObject(); + bootKey = (byte[]) element.readObject(); + bootPatchLevel = (byte[]) element.readObject(); + additionalCertChain = (byte[]) element.readObject(); + bcc = (byte[]) element.readObject(); + // Read Key Objects + masterKey = (KMMasterKey) seProvider.onResore(element); + computedHmacKey = (KMComputedHmacKey) seProvider.onResore(element); + preSharedKey = (KMPreSharedKey) seProvider.onResore(element); + deviceUniqueKey = (KMDeviceUniqueKey) seProvider.onResore(element); + rkpMacKey = (KMRkpMacKey) seProvider.onResore(element); + } + + @Override + public short getBackupPrimitiveByteCount() { + // Magic Number - 1 byte + // Package Version - 2 bytes + // dataIndex - 2 bytes + // deviceLocked - 1 byte + // deviceState = 2 bytes + // interface types - 4 bytes + return (short) (12 + + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + + seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY) + + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + + seProvider.getBackupPrimitiveByteCount( KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY) + + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + } + + @Override + public short getBackupObjectCount() { + // dataTable - 1 + // AttestationIds - 8 + // bootParameters - 3 + // AdditionalCertificateChain - 1 + // BCC - 1 + return (short) (14 + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY) + + seProvider.getBackupObjectCount( + KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 03791210..0a158878 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import org.globalplatform.upgrade.Element; import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMUpgradable; import org.globalplatform.upgrade.Element; @@ -29,50 +30,13 @@ * KMRepository class manages persistent and volatile memory usage by the applet. Note the * repository is only used by applet and it is not intended to be used by seProvider. */ -public class KMRepository implements KMUpgradable { +public class KMRepository { public static final short HEAP_SIZE = 15000; - // Data table configuration - public static final short DATA_INDEX_SIZE = 17; - public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_INDEX_ENTRY_LENGTH = 0; - public static final short DATA_INDEX_ENTRY_OFFSET = 2; - - //TODO reduced data table size from 2048 to 300. - public static final short DATA_MEM_SIZE = 300; - - // Data table offsets - public static final byte COMPUTED_HMAC_KEY = 0; - public static final byte HMAC_NONCE = 1; - public static final byte BOOT_OS_VERSION = 2; - public static final byte BOOT_OS_PATCH_LEVEL = 3; - public static final byte VENDOR_PATCH_LEVEL = 4; - public static final byte DEVICE_LOCKED_TIME = 5; - public static final byte DEVICE_LOCKED = 6; - public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; - // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 - public static final byte AUTH_TAG_1 = 8; - public static final byte BOOT_ENDED_FLAG = 15; - public static final byte EARLY_BOOT_ENDED_FLAG = 16; - - // Data Item sizes - public static final short HMAC_SEED_NONCE_SIZE = 32; - public static final short COMPUTED_HMAC_KEY_SIZE = 32; - public static final short OS_VERSION_SIZE = 4; - public static final short OS_PATCH_SIZE = 4; - public static final short VENDOR_PATCH_SIZE = 4; - public static final short DEVICE_LOCK_TS_SIZE = 8; - public static final short MAX_BLOB_STORAGE = 8; - public static final short AUTH_TAG_LENGTH = 16; - public static final short AUTH_TAG_COUNTER_SIZE = 4; - public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); - // Class Attributes private byte[] heap; private short[] heapIndex; - private byte[] dataTable; - private short dataIndex; private short reclaimIndex; // Singleton instance @@ -83,30 +47,12 @@ public static KMRepository instance() { } public KMRepository(boolean isUpgrading) { - newDataTable(isUpgrading); heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); heapIndex = JCSystem.makeTransientShortArray((short)1, JCSystem.CLEAR_ON_RESET); reclaimIndex = HEAP_SIZE; - - //Initialize the device locked status - if (!isUpgrading) { - setDeviceLock(false); - setDeviceLockPasswordOnly(false); - } repository = this; } - public void initHmacNonce(byte[] nonce, short offset, short len) { - if (len != HMAC_SEED_NONCE_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(HMAC_NONCE, nonce, offset, len); - } - - public void clearHmacNonce() { - clearDataEntry(HMAC_NONCE); - } - public void onUninstall() { // Javacard Runtime environment cleans up the data. @@ -165,348 +111,8 @@ public short alloc(short length) { return (short) (heapIndex[0] - length); } - private short dataAlloc(short length) { - if (((short) (dataIndex + length)) > dataTable.length) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - dataIndex += length; - return (short) (dataIndex - length); - } - - private void newDataTable(boolean isUpgrading) { - if (!isUpgrading) { - if (dataTable == null) { - dataTable = new byte[DATA_MEM_SIZE]; - dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - } - } - } - - private void clearDataEntry(short id) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (dataLen != 0) { - short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0); - JCSystem.commitTransaction(); - } - } - - private void writeDataEntry(short id, byte[] buf, short offset, short len) { - short dataPtr; - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (dataLen == 0) { - dataPtr = dataAlloc(len); - JCSystem.beginTransaction(); - Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); - Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len); - Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); - JCSystem.commitTransaction(); - } else { - if (len != dataLen) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); - JCSystem.beginTransaction(); - Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); - JCSystem.commitTransaction(); - } - } - - private short readDataEntry(short id, byte[] buf, short offset) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (len != 0) { - Util.arrayCopyNonAtomic( - dataTable, - Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), - buf, - offset, - len); - } - return len; - } - - private short dataLength(short id) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - } - public byte[] getHeap() { return heap; } - public short getHmacNonce() { - return readData(HMAC_NONCE); - } - - public short readData(short id) { - short len = dataLength(id); - if (len != 0) { - short blob = KMByteBlob.instance(dataLength(id)); - readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - return blob; - } - return KMType.INVALID_VALUE; - } - - private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; - - public short getOsVersion() { - short blob = readData(BOOT_OS_VERSION); - if (blob != KMType.INVALID_VALUE) { - return KMInteger.uint_32( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_32(zero, (short) 0); - } - } - - public short getVendorPatchLevel() { - short blob = readData(VENDOR_PATCH_LEVEL); - if (blob != KMType.INVALID_VALUE) { - return KMInteger.uint_32( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_32(zero, (short) 0); - } - } - - public short getOsPatch() { - short blob = readData(BOOT_OS_PATCH_LEVEL); - if (blob != KMType.INVALID_VALUE) { - return KMInteger.uint_32( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_32(zero, (short) 0); - } - } - - private boolean readBoolean(short id) { - short blob = readData(id); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; - } - - public boolean getDeviceLock() { - return readBoolean(DEVICE_LOCKED); - } - - public boolean getDeviceLockPasswordOnly() { - return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY); - } - - public boolean getEarlyBootEndedStatus() { - return readBoolean(EARLY_BOOT_ENDED_FLAG); - } - - public boolean getBootEndedStatus() { - return readBoolean(BOOT_ENDED_FLAG); - } - - public short getDeviceTimeStamp() { - short blob = readData(DEVICE_LOCKED_TIME); - if (blob != KMType.INVALID_VALUE) { - return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_64(zero, (short) 0); - } - } - - public void setOsVersion(byte[] buf, short start, short len) { - if (len != OS_VERSION_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_OS_VERSION, buf, start, len); - } - - public void setVendorPatchLevel(byte[] buf, short start, short len) { - if (len != VENDOR_PATCH_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len); - } - - private void writeBoolean(short id, boolean flag) { - short start = alloc((short) 1); - if (flag) { - (getHeap())[start] = (byte) 0x01; - } else { - (getHeap())[start] = (byte) 0x00; - } - writeDataEntry(id, getHeap(), start, (short) 1); - } - - public void setDeviceLock(boolean flag) { - writeBoolean(DEVICE_LOCKED, flag); - } - - public void setDeviceLockPasswordOnly(boolean flag) { - writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag); - } - - public void setDeviceLockTimestamp(byte[] buf, short start, short len) { - if (len != DEVICE_LOCK_TS_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len); - } - - public void setEarlyBootEndedStatus(boolean flag) { - writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); - } - - public void setBootEndedStatus(boolean flag) { - writeBoolean(BOOT_ENDED_FLAG, flag); - } - - public void clearDeviceLockTimeStamp() { - clearDataEntry(DEVICE_LOCKED_TIME); - } - - public void setOsPatch(byte[] buf, short start, short len) { - if (len != OS_PATCH_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len); - } - - private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) { - readDataEntry(tagId, buf, offset); - return (0 == buf[offset]); - } - - private void writeAuthTagState(byte[] buf, short offset, byte state) { - buf[offset] = state; - } - - public boolean persistAuthTag(short authTag) { - - if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - - short authTagEntry = alloc(AUTH_TAG_ENTRY_SIZE); - short scratchPadOff = alloc(AUTH_TAG_ENTRY_SIZE); - byte[] scratchPad = getHeap(); - writeAuthTagState(getHeap(), authTagEntry, (byte) 1); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - getHeap(), (short) (authTagEntry + 1), AUTH_TAG_LENGTH); - Util.setShort(getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), - (short) 1); - short index = 0; - while (index < MAX_BLOB_STORAGE) { - if ((dataLength((short) (index + AUTH_TAG_1)) == 0) || - isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) { - - writeDataEntry((short) (index + AUTH_TAG_1), getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE); - return true; - } - index++; - } - return false; - } - - public void removeAllAuthTags() { - short index = 0; - while (index < MAX_BLOB_STORAGE) { - clearDataEntry((short) (index + AUTH_TAG_1)); - index++; - } - } - - public boolean isAuthTagPersisted(short authTag) { - return (KMType.INVALID_VALUE != findTag(authTag)); - } - - private short findTag(short authTag) { - if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - short index = 0; - short found; - short offset = alloc(AUTH_TAG_ENTRY_SIZE); - while (index < MAX_BLOB_STORAGE) { - if (dataLength((short) (index + AUTH_TAG_1)) != 0) { - readDataEntry((short) (index + AUTH_TAG_1), - getHeap(), offset); - found = - Util.arrayCompare( - getHeap(), - (short) (offset + 1), - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - AUTH_TAG_LENGTH); - if (found == 0) { - return (short) (index + AUTH_TAG_1); - } - } - index++; - } - return KMType.INVALID_VALUE; - } - - public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) { - short tag = findTag(authTag); - short blob; - if (tag != KMType.INVALID_VALUE) { - blob = readData(tag); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(blob).getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1), - out, - outOff, - AUTH_TAG_COUNTER_SIZE); - return AUTH_TAG_COUNTER_SIZE; - } - return (short) 0; - } - - public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) { - short tag = findTag(authTag); - if (tag != KMType.INVALID_VALUE) { - short dataPtr = readData(tag); - Util.arrayCopyNonAtomic( - buf, - off, - KMByteBlob.cast(dataPtr).getBuffer(), - (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1), - len); - writeDataEntry(tag, - KMByteBlob.cast(dataPtr).getBuffer(), - KMByteBlob.cast(dataPtr).getStartOff(), - KMByteBlob.cast(dataPtr).length()); - } - } - - @Override - public void onSave(Element ele) { - ele.write(dataIndex); - ele.write(dataTable); - } - - @Override - public void onRestore(Element ele) { - dataIndex = ele.readShort(); - dataTable = (byte[]) ele.readObject(); - } - - @Override - public short getBackupPrimitiveByteCount() { - // dataIndex - return (short) 2; - } - - @Override - public short getBackupObjectCount() { - // dataTable - return (short) 1; - } } diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 4a68b718..58a67f9e 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -129,25 +129,36 @@ public class RemotelyProvisionedComponentDevice { private static final byte UPDATE = 0x02; private static final byte FINISH = 0x04; private static final byte GET_RESPONSE = 0x06; + + //RKP mac key size + private static final short RKP_MAC_KEY_SIZE = 32; + // variables private byte[] data; private KMEncoder encoder; private KMDecoder decoder; private KMRepository repository; private KMSEProvider seProvider; + private KMKeymintDataStore storeDataInst; private Object[] operation; private short[] dataIndex; public static Object[] authorizedEekRoots; public RemotelyProvisionedComponentDevice(KMEncoder encoder, KMDecoder decoder, - KMRepository repository, KMSEProvider seProvider) { + KMRepository repository, KMSEProvider seProvider, KMKeymintDataStore storeDInst) { this.encoder = encoder; this.decoder = decoder; this.repository = repository; this.seProvider = seProvider; + this.storeDataInst = storeDInst; data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); + // Initialize RKP mac key + short offset = repository.alloc((short) RKP_MAC_KEY_SIZE); + byte[] buffer = repository.getHeap(); + seProvider.getTrueRandomNumber(buffer, offset, RKP_MAC_KEY_SIZE); + storeDataInst.createRkpMacKey(buffer, offset, RKP_MAC_KEY_SIZE); operation[0] = null; createAuthorizedEEKRoot(); } @@ -541,7 +552,7 @@ public void process(short ins, APDU apdu) throws Exception { } private boolean isAdditionalCertificateChainPresent() { - return (seProvider.getAdditionalCertChainLength() == 0 ? false : true); + return (storeDataInst.getAdditionalCertChainLength() == 0 ? false : true); } private short processFinalData(byte[] scratchPad) { @@ -565,35 +576,6 @@ private void updateOutputProcessingState(byte state) { data[dataEntryIndex] = state; } - - private short getHmacKey(boolean testMode, byte[] scratchPad) { - short macKey = KMByteBlob.instance(MAC_KEY_SIZE); - Util.arrayFillNonAtomic(KMByteBlob.cast(macKey).getBuffer(), - KMByteBlob.cast(macKey).getStartOff(), MAC_KEY_SIZE, (byte) 0); - if (!testMode) { - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) (2 * MAC_KEY_SIZE), (byte) 0); - short len = - seProvider.hkdf( - scratchPad, //ikm - (short) 0, // ikm offset - MAC_KEY_SIZE, // ikm size - scratchPad, // salt - MAC_KEY_SIZE, // salt offset - MAC_KEY_SIZE, // salt length - KMCose.MAC_DERIVE_KEY_CTX, - (short) 0, - (short) KMCose.MAC_DERIVE_KEY_CTX.length, - KMByteBlob.cast(macKey).getBuffer(), - KMByteBlob.cast(macKey).getStartOff(), - MAC_KEY_SIZE - ); - if (len != MAC_KEY_SIZE) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - } - return macKey; - } - /** * Validates the CoseMac message and extracts the CoseKey from it. * @@ -607,9 +589,7 @@ private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { short coseHeadersExp = KMCoseHeaders.exp(); // Exp for coseky short coseKeyExp = KMCoseKey.exp(); - // Get the mackey. - short macKey = getHmacKey(testMode, scratchPad); - + // validate protected Headers short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); ptr = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr).getBuffer(), @@ -645,9 +625,7 @@ private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { short encodedLen = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short hmacLen = seProvider.hmacSign(KMByteBlob.cast(macKey).getBuffer(), - KMByteBlob.cast(macKey).getStartOff(), - (short) 32, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen); + short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen); if (hmacLen != KMByteBlob.cast( KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) { @@ -834,10 +812,10 @@ private KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, byte[] scratch (short) 128, lengths); deviceUniqueKey = - seProvider.createDeviceUniqueKey(true, scratchPad, (short) 128, lengths[1], + storeDataInst.createTestDeviceUniqueKey(scratchPad, (short) 128, lengths[1], scratchPad, (short) 0, lengths[0]); } else { - deviceUniqueKey = seProvider.getDeviceUniqueKey(false); + deviceUniqueKey = storeDataInst.getDeviceUniqueKey(false); } return deviceUniqueKey; } @@ -939,7 +917,7 @@ private void updateItem(short[] deviceIds, short[] meta, byte[] item, short valu } private short getAttestationId(short attestId, byte[] scratchpad) { - short attIdTagLen = seProvider.getAttestationId(attestId, scratchpad, (short) 0); + short attIdTagLen = storeDataInst.getAttestationId(attestId, scratchpad, (short) 0); if (attIdTagLen != 0) { return KMTextString.instance(scratchpad, (short) 0, attIdTagLen); } @@ -947,7 +925,7 @@ private short getAttestationId(short attestId, byte[] scratchpad) { } private short getVerifiedBootHash(byte[] scratchPad) { - short len = seProvider.getVerifiedBootHash(scratchPad, (short) 0); + short len = storeDataInst.getVerifiedBootHash(scratchPad, (short) 0); if (len != 0) { return KMByteBlob.instance(scratchPad, (short) 0, len); } @@ -956,7 +934,7 @@ private short getVerifiedBootHash(byte[] scratchPad) { private short getBootloaderState() { short bootloaderState; - if (seProvider.isDeviceBootLocked()) { + if (storeDataInst.isDeviceBootLocked()) { bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length); } else { bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length); @@ -965,7 +943,7 @@ private short getBootloaderState() { } private short getVbState() { - short state = seProvider.getBootState(); + short state = storeDataInst.getBootState(); short vbState = KMType.INVALID_VALUE; if (state == KMType.VERIFIED_BOOT) { vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length); @@ -983,17 +961,17 @@ private short getBootParams(byte bootParam, byte[] scratchPad) { short value = KMType.INVALID_VALUE; switch (bootParam) { case OS_VERSION_ID: - value = repository.getOsVersion(); + value = storeDataInst.getOsVersion(); break; case SYSTEM_PATCH_LEVEL_ID: - value = repository.getOsPatch(); + value = storeDataInst.getOsPatch(); break; case BOOT_PATCH_LEVEL_ID: - short len = seProvider.getBootPatchLevel(scratchPad, (short) 0); + short len = storeDataInst.getBootPatchLevel(scratchPad, (short) 0); value = KMByteBlob.instance(scratchPad, (short) 0, len); break; case VENDOR_PATCH_LEVEL_ID: - value = repository.getVendorPatchLevel(); + value = storeDataInst.getVendorPatchLevel(); break; default: KMException.throwIt(KMError.INVALID_ARGUMENT); @@ -1198,7 +1176,7 @@ private void updateAdditionalCertChainProcessedLength(short processedLen) { } private short processAdditionalCertificateChain(byte[] scratchPad) { - byte[] persistedData = seProvider.getAdditionalCertChain(); + byte[] persistedData = storeDataInst.getAdditionalCertChain(); short totalAccLen = Util.getShort(persistedData, (short) 0); if (totalAccLen == 0) { // No Additional certificate chain present. @@ -1230,7 +1208,7 @@ private short processBcc(byte[] scratchPad) { len = KMKeymasterApplet .encodeToApduBuffer(bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); } else { - byte[] bcc = seProvider.getBootCertificateChain(); + byte[] bcc = storeDataInst.getBootCertificateChain(); len = Util.getShort(bcc, (short) 0); Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len); } @@ -1329,8 +1307,6 @@ private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, sho short len = KMKeymasterApplet .encodeToApduBuffer(coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); short payload = KMByteBlob.instance(scratchPad, (short) 0, len); - // Get the mackey. - short macKey = getHmacKey(testMode, scratchPad); // Prepare protected header, which is required to construct the COSE_MAC0 short headerPtr = KMCose.constructHeaders( KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), @@ -1348,9 +1324,7 @@ private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, sho len = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); // HMAC Sign. - short hmacLen = seProvider - .hmacSign(KMByteBlob.cast(macKey).getBuffer(), KMByteBlob.cast(macKey).getStartOff(), - (short) 32, scratchPad, (short) 0, len, scratchPad, len); + short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, len, scratchPad, len); // Create COSE_MAC0 object short coseMac0 = KMCose @@ -1455,4 +1429,18 @@ public short encodeES256CoseSignSignature(byte[] input, short offset, short len, return length; } + private short rkpHmacSign(boolean testMode, byte[] data, short dataStart, short dataLength, byte[] signature, + short signatureStart) { + short result; + if(testMode) { + short macKey = KMByteBlob.instance(MAC_KEY_SIZE); + Util.arrayFillNonAtomic(KMByteBlob.cast(macKey).getBuffer(), + KMByteBlob.cast(macKey).getStartOff(), MAC_KEY_SIZE, (byte) 0); + result = seProvider.hmacSign(KMByteBlob.cast(macKey).getBuffer(), KMByteBlob.cast(macKey).getStartOff(), MAC_KEY_SIZE, data, dataStart, dataLength, signature, signatureStart); + } else { + result = seProvider.hmacSign(storeDataInst.getRkpMacacKey(), data, dataStart, dataLength, signature, signatureStart); + } + return result; + } + }