blob: efd7ae5f2afc57e32c098ef8d804cc53c3a4602b [file] [log] [blame]
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 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 ADDITIONAL_CERT_CHAIN_MAX_SIZE = 2500; // First 2 bytes for length.
private static final short BCC_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[] 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;
private short dataIndex;
private byte[] dataTable;
private KMSEProvider seProvider;
private KMRepository repository;
private byte[] additionalCertChain;
private byte[] bcc;
private KMKey masterKey;
private KMKey testDeviceUniqueKeyPair;
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) {
additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
bcc = new byte[BCC_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 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;
}
// 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 createRkpTestDeviceUniqueKeyPair(
byte[] pubKey,
short pubKeyOff,
short pubKeyLen,
byte[] privKey,
short privKeyOff,
short privKeyLen) {
if (testDeviceUniqueKeyPair == null) {
testDeviceUniqueKeyPair =
seProvider.createRkpDeviceUniqueKeyPair(
testDeviceUniqueKeyPair,
pubKey,
pubKeyOff,
pubKeyLen,
privKey,
privKeyOff,
privKeyLen);
} else {
seProvider.createRkpDeviceUniqueKeyPair(
testDeviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
}
return testDeviceUniqueKeyPair;
}
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(boolean testMode) {
return ((KMKey) (testMode ? testDeviceUniqueKeyPair : 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 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) {
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 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;
// 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 != 32) {
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 != 32) {
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);
// Objects
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(additionalCertChain);
element.write(bcc);
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 3.1 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 3.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.
additionalCertChain = new byte[ADDITIONAL_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) {
// 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
additionalCertChain = (byte[]) element.readObject();
bcc = (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();
}
// Read Objects
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();
additionalCertChain = (byte[]) element.readObject();
bcc = (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 3.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.
additionalCertChain = new byte[ADDITIONAL_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
return (short)
(3
+ 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 - 8
// AdditionalCertificateChain - 1
// BCC - 1
// oemRootPublicKey - 1
return (short)
(11
+ 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));
}
}