diff options
Diffstat (limited to 'ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java')
-rw-r--r-- | ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java | 1075 |
1 files changed, 1075 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java new file mode 100644 index 0000000..65117eb --- /dev/null +++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -0,0 +1,1075 @@ +package com.android.javacard.keymaster; + +import com.android.javacard.seprovider.KMDataStoreConstants; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMKey; +import com.android.javacard.seprovider.KMSEProvider; +import com.android.javacard.seprovider.KMUpgradable; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import org.globalplatform.upgrade.Element; + +/** + * This is a storage class which helps in storing the provisioned data, ROT, OS version, Boot patch + * level, Vendor Patchlevel, HMAC nonce, computed shared secret, 8 auth tags, device-locked, + * device-locked timestamp and device-locked password only. Only the provisioned data is restored + * back during applet upgrades and the remaining data is flushed. + */ +public class KMKeymintDataStore implements KMUpgradable { + + // Data table configuration + public static final short KM_APPLET_PACKAGE_VERSION_1 = 0x0100; + public static final short KM_APPLET_PACKAGE_VERSION_2 = 0x0200; + public static final short KM_APPLET_PACKAGE_VERSION_3 = 0x0300; + public static final short KM_APPLET_PACKAGE_VERSION_4 = 0x0400; + public static final byte DATA_INDEX_SIZE = 17; + public static final byte DATA_INDEX_ENTRY_SIZE = 4; + public static final byte DATA_INDEX_ENTRY_LENGTH = 0; + public static final byte DATA_INDEX_ENTRY_OFFSET = 2; + public static final short DATA_MEM_SIZE = 300; + // Data table offsets + public static final byte HMAC_NONCE = 0; + public static final byte BOOT_OS_VERSION = 1; + public static final byte BOOT_OS_PATCH_LEVEL = 2; + public static final byte VENDOR_PATCH_LEVEL = 3; + public static final byte DEVICE_LOCKED_TIME = 4; + public static final byte DEVICE_LOCKED = 5; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 6; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + public static final byte AUTH_TAG_1 = 7; + public static final byte DEVICE_STATUS_FLAG = 15; + public static final byte EARLY_BOOT_ENDED_FLAG = 16; + // Data Item sizes + public static final byte HMAC_SEED_NONCE_SIZE = 32; + public static final byte COMPUTED_HMAC_KEY_SIZE = 32; + public static final byte OS_VERSION_SIZE = 4; + public static final byte OS_PATCH_SIZE = 4; + public static final byte VENDOR_PATCH_SIZE = 4; + public static final byte DEVICE_LOCK_TS_SIZE = 8; + public static final byte MAX_BLOB_STORAGE = 8; + public static final byte AUTH_TAG_LENGTH = 16; + public static final byte AUTH_TAG_COUNTER_SIZE = 4; + public static final byte AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); + // Device boot states. Applet starts executing the + // core commands once all the states are set. The commands + // that are allowed irrespective of these states are: + // All the provision commands + // INS_GET_HW_INFO_CMD + // INS_ADD_RNG_ENTROPY_CMD + // INS_COMPUTE_SHARED_HMAC_CMD + // INS_GET_HMAC_SHARING_PARAM_CMD + public static final byte SET_BOOT_PARAMS_SUCCESS = 0x01; + public static final byte SET_SYSTEM_PROPERTIES_SUCCESS = 0x02; + public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x04; + // Old Data table offsets + private static final byte OLD_PROVISIONED_STATUS_OFFSET = 18; + private static final byte SHARED_SECRET_KEY_SIZE = 32; + private static final byte DEVICE_STATUS_FLAG_SIZE = 1; + private static final short UDS_CERT_CHAIN_MAX_SIZE = 2500; // First 2 bytes for length. + private static final short DICE_CERT_CHAIN_MAX_SIZE = 512; + private static KMKeymintDataStore kmDataStore; + // Secure Boot Mode + public byte secureBootMode; + // Data - originally was in repository + private byte[] attIdBrand; + private byte[] attIdDevice; + private byte[] attIdProduct; + private byte[] attIdSerial; + private byte[] attIdImei; + private byte[] attIdSecondImei; + 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; + // Challenge for Root of trust + private byte[] challenge; + + /* + * Applets upgrading to KeyMint3.0 may not have the second imei provisioned. + * So this flag is used to ignore the SECOND_IMEI tag if the previous Applet's + * KeyMint version is less than 3.0. + */ + public boolean ignoreSecondImei; + private short dataIndex; + private byte[] dataTable; + private KMSEProvider seProvider; + private KMRepository repository; + private byte[] udsCertChain; + private byte[] diceCertChain; + private KMKey masterKey; + private KMKey deviceUniqueKeyPair; + private KMKey preSharedKey; + private KMKey computedHmacKey; + private KMKey rkpMacKey; + private byte[] oemRootPublicKey; + private short provisionStatus; + + public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { + seProvider = provider; + repository = repo; + boolean isUpgrading = provider.isUpgrading(); + initDataTable(); + // Initialize the device locked status + if (!isUpgrading) { + udsCertChain = new byte[UDS_CERT_CHAIN_MAX_SIZE]; + diceCertChain = new byte[DICE_CERT_CHAIN_MAX_SIZE]; + oemRootPublicKey = new byte[65]; + } + setDeviceLockPasswordOnly(false); + setDeviceLock(false); + kmDataStore = this; + } + + public static KMKeymintDataStore instance() { + return kmDataStore; + } + + private void initDataTable() { + 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 readDataEntry(byte[] dataTable, 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); + } + + public short getOsVersion() { + short blob = readData(BOOT_OS_VERSION); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } + + public short getVendorPatchLevel() { + short blob = readData(VENDOR_PATCH_LEVEL); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } + + public short getOsPatch() { + short blob = readData(BOOT_OS_PATCH_LEVEL); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } + + 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 void setDeviceLock(boolean flag) { + writeBoolean(DEVICE_LOCKED, flag); + } + + public boolean getDeviceLockPasswordOnly() { + return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY); + } + + public void setDeviceLockPasswordOnly(boolean flag) { + writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag); + } + + public boolean getEarlyBootEndedStatus() { + return readBoolean(EARLY_BOOT_ENDED_FLAG); + } + + public void setEarlyBootEndedStatus(boolean flag) { + writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); + } + + public short getDeviceTimeStamp() { + short blob = readData(DEVICE_LOCKED_TIME); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return KMInteger.uint_64( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + } + + 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 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 clearDeviceBootStatus() { + clearDataEntry(DEVICE_STATUS_FLAG); + } + + public void setDeviceBootStatus(byte initStatus) { + short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE); + byte[] buf = repository.getHeap(); + getDeviceBootStatus(buf, offset); + buf[offset] |= initStatus; + writeDataEntry(DEVICE_STATUS_FLAG, buf, offset, DEVICE_STATUS_FLAG_SIZE); + repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE); + } + + public boolean isDeviceReady() { + boolean result = false; + short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE); + byte[] buf = repository.getHeap(); + getDeviceBootStatus(buf, offset); + byte bootCompleteStatus = + (SET_BOOT_PARAMS_SUCCESS + | SET_SYSTEM_PROPERTIES_SUCCESS + | NEGOTIATED_SHARED_SECRET_SUCCESS); + if (bootCompleteStatus == (buf[offset] & bootCompleteStatus)) { + result = true; + } + repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE); + return result; + } + + public short getDeviceBootStatus(byte[] scratchpad, short offset) { + scratchpad[offset] = 0; + return readDataEntry(DEVICE_STATUS_FLAG, scratchpad, offset); + } + + 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 persistUdsCertChain(byte[] buf, short offset, short len) { + // Input buffer contains encoded Uds certificate chain as shown below. + // UdsDKSignatures = { + // + 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) > UDS_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(udsCertChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, udsCertChain, (short) 2, len); + JCSystem.commitTransaction(); + } + + public short getUdsCertChainLength() { + return Util.getShort(udsCertChain, (short) 0); + } + + public byte[] getUdsCertChain() { + return udsCertChain; + } + + public byte[] getDiceCertificateChain() { + return diceCertChain; + } + + public void persistBootCertificateChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > DICE_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(diceCertChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, diceCertChain, (short) 2, len); + JCSystem.commitTransaction(); + } + + private void writeAuthTagState(byte[] buf, short offset, byte state) { + buf[offset] = state; + } + + // The master key should only be generated during applet installation and + // during a device factory reset event. + public KMKey createMasterKey(short keySizeBits) { + if (masterKey == null) { + masterKey = seProvider.createMasterKey(masterKey, keySizeBits); + } + return (KMKey) masterKey; + } + + public KMKey regenerateMasterKey() { + return seProvider.createMasterKey(masterKey, KMKeymasterApplet.MASTER_KEY_SIZE); + } + + public KMKey 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 KMKey getPresharedKey() { + if (preSharedKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + 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 KMKey getComputedHmacKey() { + if (computedHmacKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + return computedHmacKey; + } + + public KMKey createRkpDeviceUniqueKeyPair( + byte[] pubKey, + short pubKeyOff, + short pubKeyLen, + byte[] privKey, + short privKeyOff, + short privKeyLen) { + if (deviceUniqueKeyPair == null) { + deviceUniqueKeyPair = + seProvider.createRkpDeviceUniqueKeyPair( + deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen); + } else { + seProvider.createRkpDeviceUniqueKeyPair( + deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen); + } + return deviceUniqueKeyPair; + } + + public KMKey getRkpDeviceUniqueKeyPair() { + return ((KMKey) deviceUniqueKeyPair); + } + + 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 KMKey getRkpMacKey() { + if (rkpMacKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + return rkpMacKey; + } + + public short getAttestationId(short tag, byte[] buffer, short start) { + byte[] attestId = null; + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + attestId = attIdBrand; + break; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + attestId = attIdDevice; + break; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + attestId = attIdProduct; + break; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + attestId = attIdSerial; + break; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + attestId = attIdImei; + break; + // Attestation Id SECOND IMEI + case KMType.ATTESTATION_ID_SECOND_IMEI: + attestId = attIdSecondImei; + break; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + attestId = attIdMeId; + break; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + attestId = attIdManufacturer; + break; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + attestId = attIdModel; + break; + } + if (attestId == null) { + /* Ignore the SECOND_IMEI tag if the previous Applet's KeyMint version is less than 3.0 and + * no SECOND_IMEI is provisioned. + */ + if (kmDataStore.ignoreSecondImei && tag == KMType.ATTESTATION_ID_SECOND_IMEI) { + return (short) 0; + } + KMException.throwIt(KMError.CANNOT_ATTEST_IDS); + } + Util.arrayCopyNonAtomic(attestId, (short) 0, buffer, start, (short) attestId.length); + return (short) attestId.length; + } + + 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 SECOND IMEI + case KMType.ATTESTATION_ID_SECOND_IMEI: + JCSystem.beginTransaction(); + attIdSecondImei = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdSecondImei, (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; + attIdSecondImei = null; + attIdMeId = null; + attIdManufacturer = null; + attIdModel = null; + // Trigger garbage collection. + JCSystem.requestObjectDeletion(); + } + + public short getVerifiedBootHash(byte[] buffer, short start) { + if (verifiedHash == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length); + return (short) verifiedHash.length; + } + + public short getBootKey(byte[] buffer, short start) { + if (bootKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); + return (short) bootKey.length; + } + + public short getBootState() { + return bootState; + } + + public void setBootState(short state) { + bootState = state; + } + + public boolean isDeviceBootLocked() { + return deviceBootLocked; + } + + public short getBootPatchLevel() { + if (bootPatchLevel == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + return KMInteger.uint_32(bootPatchLevel, (short) 0); + } + + public void setVerifiedBootHash(byte[] buffer, short start, short length) { + if (verifiedHash == null) { + verifiedHash = new byte[32]; + } + if (length != KMKeymasterApplet.VERIFIED_BOOT_HASH_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopy(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 != KMKeymasterApplet.VERIFIED_BOOT_KEY_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopy(buffer, start, bootKey, (short) 0, (short) 32); + } + + 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.arrayCopy(buffer, start, bootPatchLevel, (short) 0, (short) length); + } + + public void setChallenge(byte[] buf, short start, short length) { + if (challenge == null) { + challenge = new byte[16]; + } + if (length != 16) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + Util.arrayCopy(buf, start, challenge, (short) 0, (short) length); + } + + public short getChallenge(byte[] buffer, short start) { + if (challenge == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + Util.arrayCopyNonAtomic(challenge, (short) 0, buffer, start, (short) challenge.length); + return (short) challenge.length; + } + + public boolean isProvisionLocked() { + if (0 != (provisionStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) { + return true; + } + return false; + } + + public short getProvisionStatus() { + return provisionStatus; + } + + public void setProvisionStatus(short pStatus) { + JCSystem.beginTransaction(); + provisionStatus |= pStatus; + JCSystem.commitTransaction(); + } + + public void unlockProvision() { + JCSystem.beginTransaction(); + provisionStatus &= ~KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED; + JCSystem.commitTransaction(); + } + + public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) { + if (inLength != 65) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if (oemRootPublicKey == null) { + oemRootPublicKey = new byte[65]; + } + Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength); + } + + public byte[] getOEMRootPublicKey() { + if (oemRootPublicKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } + return oemRootPublicKey; + } + + @Override + public void onSave(Element element) { + // Prmitives + element.write(provisionStatus); + element.write(secureBootMode); + element.write(ignoreSecondImei); + // Objects + element.write(attIdBrand); + element.write(attIdDevice); + element.write(attIdProduct); + element.write(attIdSerial); + element.write(attIdImei); + element.write(attIdSecondImei); + element.write(attIdMeId); + element.write(attIdManufacturer); + element.write(attIdModel); + element.write(udsCertChain); + element.write(diceCertChain); + element.write(oemRootPublicKey); + + // Key Objects + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey); + seProvider.onSave( + element, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR, deviceUniqueKeyPair); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY, rkpMacKey); + } + + @Override + public void onRestore(Element element, short oldVersion, short currentVersion) { + if (oldVersion <= KM_APPLET_PACKAGE_VERSION_1) { + // 1.0 to 4.0 Upgrade happens here. + handlePreviousVersionUpgrade(element); + return; + } else if (oldVersion == KM_APPLET_PACKAGE_VERSION_2) { + handleUpgrade(element, oldVersion); + JCSystem.beginTransaction(); + // While upgrading Secure Boot Mode flag from 2.0 to 4.0, implementations + // have to update the secureBootMode with the correct input. + secureBootMode = 0; + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE; + // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain. + // From package version 3, the additionalCertificateChain is in X.509 format. + // So Unreference the old address and allocate new persistent memory. + udsCertChain = new byte[UDS_CERT_CHAIN_MAX_SIZE]; + JCSystem.commitTransaction(); + // Request for ObjectDeletion for unreferenced address of additionalCertChain. + JCSystem.requestObjectDeletion(); + return; + } + handleUpgrade(element, oldVersion); + } + + private void handlePreviousVersionUpgrade(Element element) { + // set ignore Imei flag to true. + ignoreSecondImei = true; + // Read Primitives + // restore old data table index + short oldDataIndex = element.readShort(); + element.readBoolean(); // pop deviceBootLocked + element.readShort(); // pop bootState + + // Read Objects + // restore old data table + byte[] oldDataTable = (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(); + element.readObject(); // pop verifiedHash + element.readObject(); // pop bootKey + element.readObject(); // pop bootPatchLevel + udsCertChain = (byte[]) element.readObject(); + diceCertChain = (byte[]) element.readObject(); + + // Read Key Objects + masterKey = (KMKey) seProvider.onRestore(element); + seProvider.onRestore(element); // pop computedHmacKey + preSharedKey = (KMKey) seProvider.onRestore(element); + deviceUniqueKeyPair = (KMKey) seProvider.onRestore(element); + rkpMacKey = (KMKey) seProvider.onRestore(element); + handleProvisionStatusUpgrade(oldDataTable, oldDataIndex); + } + + private void handleUpgrade(Element element, short oldVersion) { + // Read Primitives + provisionStatus = element.readShort(); + if (oldVersion >= KM_APPLET_PACKAGE_VERSION_3) { + secureBootMode = element.readByte(); + } + /* check if KeyMint is upgrading from older HAL version to KM300 + * and set the ignore second Imei flag + */ + if (oldVersion < KM_APPLET_PACKAGE_VERSION_4) { + ignoreSecondImei = true; + } else { + ignoreSecondImei = element.readBoolean(); + } + // Read Objects + attIdBrand = (byte[]) element.readObject(); + attIdDevice = (byte[]) element.readObject(); + attIdProduct = (byte[]) element.readObject(); + attIdSerial = (byte[]) element.readObject(); + attIdImei = (byte[]) element.readObject(); + if (oldVersion >= KM_APPLET_PACKAGE_VERSION_4) { + attIdSecondImei = (byte[]) element.readObject(); + } + attIdMeId = (byte[]) element.readObject(); + attIdManufacturer = (byte[]) element.readObject(); + attIdModel = (byte[]) element.readObject(); + udsCertChain = (byte[]) element.readObject(); + diceCertChain = (byte[]) element.readObject(); + oemRootPublicKey = (byte[]) element.readObject(); + // Read Key Objects + masterKey = (KMKey) seProvider.onRestore(element); + preSharedKey = (KMKey) seProvider.onRestore(element); + deviceUniqueKeyPair = (KMKey) seProvider.onRestore(element); + rkpMacKey = (KMKey) seProvider.onRestore(element); + } + + public void getProvisionStatus(byte[] dataTable, byte[] scratchpad, short offset) { + Util.setShort(scratchpad, offset, (short) 0); + readDataEntry(dataTable, OLD_PROVISIONED_STATUS_OFFSET, scratchpad, offset); + } + + void handleProvisionStatusUpgrade(byte[] dataTable, short dataTableIndex) { + short dInex = repository.allocReclaimableMemory((short) 2); + byte data[] = repository.getHeap(); + getProvisionStatus(dataTable, data, dInex); + short pStatus = (short) (data[dInex] & 0x00ff); + if (KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED + == (pStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) { + pStatus |= + KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED + | KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE; + } + JCSystem.beginTransaction(); + // While upgrading Secure Boot Mode flag from 1.0 to 4.0, implementations + // have to update the secureBootMode with the correct input. + secureBootMode = 0; + provisionStatus = pStatus; + // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain. + // From package version 3, the additionalCertificateChain is in X.509 format. + // So Unreference the old address and allocate new persistent memory. + udsCertChain = new byte[UDS_CERT_CHAIN_MAX_SIZE]; + JCSystem.commitTransaction(); + repository.reclaimMemory((short) 2); + // Request object deletion for unreferenced address for additionalCertChain + JCSystem.requestObjectDeletion(); + } + + @Override + public short getBackupPrimitiveByteCount() { + // provisionStatus - 2 bytes + // secureBootMode - 1 byte + // Flag for ignore second Imei- 1 byte + return (short) + (4 + + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + + seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + + seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR) + + seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + } + + @Override + public short getBackupObjectCount() { + // AttestationIds - 9 + // UdsCertificateChain - 1 + // diceCertificateChain - 1 + // oemRootPublicKey - 1 + return (short) + (12 + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + + seProvider.getBackupObjectCount( + KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + } +} |