Skip to content
This repository was archived by the owner on May 24, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,21 +52,22 @@ public void onConsolidate() {
public void onRestore(Element element) {
element.initRead();
byte firstByte = element.readByte();
short packageVersion_ = 0;
byte provisionStatus_ = firstByte;
short oldPackageVersion = 0;
if (firstByte == KMKeymasterApplet.KM_MAGIC_NUMBER) {
packageVersion_ = element.readShort();
provisionStatus_ = element.readByte();
oldPackageVersion = element.readShort();
provisionStatus = element.readByte();
} else {
// MAGIC_NUMBER is introduced in version 2.0. Upgrade is
// not allowed for Applets having version less than 2.0
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
if (0 != packageVersion_ && !isUpgradeAllowed(packageVersion_)) {
if (!isUpgradeAllowed(oldPackageVersion)) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
packageVersion = packageVersion_;
provisionStatus = provisionStatus_;
keymasterState = element.readByte();
repository.onRestore(element, packageVersion, KM_PERSISTENT_DATA_STORAGE_VERSION);
seProvider.onRestore(element, packageVersion, KM_PERSISTENT_DATA_STORAGE_VERSION);
handleDataUpgradeToVersion2_0();
repository.onRestore(element, oldPackageVersion, KM_APPLET_PACKAGE_VERSION);
seProvider.onRestore(element, oldPackageVersion, KM_APPLET_PACKAGE_VERSION);
handleDataUpgrade();
}

@Override
Expand DownExpand Up@@ -102,12 +103,12 @@ private short computeObjectCount() {
return (short) 0;
}

public boolean isUpgradeAllowed(short version) {
public boolean isUpgradeAllowed(short oldVersion) {
boolean upgradeAllowed = false;
short oldMajorVersion = (short) ((version >> 8) & 0x00FF);
short oldMinorVersion = (short) (version & 0x00FF);
short currentMajorVersion = (short) (KM_PERSISTENT_DATA_STORAGE_VERSION >> 8 & 0x00FF);
short currentMinorVersion = (short) (KM_PERSISTENT_DATA_STORAGE_VERSION & 0x00FF);
short oldMajorVersion = (short) ((oldVersion >> 8) & 0x00FF);
short oldMinorVersion = (short) (oldVersion & 0x00FF);
short currentMajorVersion = (short) (KM_APPLET_PACKAGE_VERSION >> 8 & 0x00FF);
short currentMinorVersion = (short) (KM_APPLET_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) {
Expand All@@ -121,85 +122,21 @@ public boolean isUpgradeAllowed(short version) {
}
return upgradeAllowed;
}

public void handleDataUpgradeToVersion2_0() {

if (packageVersion != 0) {
// No Data upgrade required.
return;
}
byte status = provisionStatus;
// In the current version of the applet set boot parameters is removed from
// provision status so readjust the provision locked flag.
// 0x40 is provision locked flag in the older applet.
// Unset the 5th bit. setboot parameters flag.
status = (byte) (status & 0xDF);
// Readjust the lock provisioned status flag.
if ((status & 0x40) == 0x40) {
// 0x40 to 0x20
// Unset 6th bit
status = (byte) (status & 0xBF);
// set the 5th bit
status = (byte) (status | 0x20);
}
provisionStatus = status;
packageVersion = KM_PERSISTENT_DATA_STORAGE_VERSION;

short certExpiryLen = 0;
short issuerLen = 0;
short certExpiry = repository.getCertExpiryTime();
if (certExpiry != KMType.INVALID_VALUE) {
certExpiryLen = KMByteBlob.cast(certExpiry).length();
}
short issuer = repository.getIssuer();
if (issuer != KMType.INVALID_VALUE) {
issuerLen = KMByteBlob.cast(issuer).length();
}
short certChainLen = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN);
short offset = repository.allocReclaimableMemory((short) (certExpiryLen + issuerLen + certChainLen));
// Get the start offset of the certificate chain.
short certChaionOff =
decoder.getCborBytesStartOffset(
repository.getHeap(),
offset,
seProvider.readProvisionedData(KMSEProvider.CERTIFICATE_CHAIN, repository.getHeap(), offset));
certChainLen -= (short) (certChaionOff - offset);
Util.arrayCopyNonAtomic(
KMByteBlob.cast(issuer).getBuffer(),
KMByteBlob.cast(issuer).getStartOff(),
repository.getHeap(),
(short) (certChaionOff + certChainLen),
issuerLen);
Util.arrayCopyNonAtomic(
KMByteBlob.cast(certExpiry).getBuffer(),
KMByteBlob.cast(certExpiry).getStartOff(),
repository.getHeap(),
(short) (certChaionOff + certChainLen + issuerLen),
certExpiryLen);

seProvider.persistProvisionData(
repository.getHeap(),
certChaionOff, // cert chain offset
certChainLen,
(short) (certChaionOff + certChainLen), // issuer offset
issuerLen,
(short) (certChaionOff + certChainLen + issuerLen), // cert expiry offset
certExpiryLen);

// Update computed HMAC key.
short blob = repository.getComputedHmacKey();
if (blob != KMType.INVALID_VALUE) {
seProvider.createComputedHmacKey(
KMByteBlob.cast(blob).getBuffer(),
KMByteBlob.cast(blob).getStartOff(),
KMByteBlob.cast(blob).length()
);
} else {
// Initialize the Key object.
Util.arrayFillNonAtomic(repository.getHeap(), offset, (short) 32, (byte) 0);
seProvider.createComputedHmacKey(repository.getHeap(), offset,(short) 32);

public void handleDataUpgrade() {
// In version 3.0, two new provisionStatus states are introduced
// 1. PROVISION_STATUS_SE_LOCKED - bit 6 of provisionStatus
// 2. PROVISION_STATUS_OEM_PUBLIC_KEY - bit 7 of provisionStatus
// In the process of upgrade from 2.0 to 3.0 OEM PUBLIC Key is provisioned
// in SEProvider.so update the state of the provision status by making
// 7th bit HIGH.
provisionStatus |= PROVISION_STATUS_OEM_ROOT_PUBLIC_KEY;
// Check if the provisioning is already locked. If so update
// the state of the provisionStatus by making 6th bit HIGH.
// Lock the SE Factory provisioning as well.
if ( 0 != (provisionStatus & PROVISION_STATUS_OEM_PROVISIONING_LOCKED)) {
provisionStatus |= PROVISION_STATUS_SE_FACTORY_PROVISIONING_LOCKED;
}
repository.reclaimMemory((short) (certExpiryLen + issuerLen + certChainLen));
}
}

Original file line numberDiff line numberDiff line change
Expand Up@@ -133,6 +133,7 @@ public class KMAndroidSEProvider implements KMSEProvider {
//Resource type constants
public static final byte RESOURCE_TYPE_CRYPTO = 0x00;
public static final byte RESOURCE_TYPE_KEY = 0x01;
public static final byte EC_PUB_KEY_SIZE = 65;

final byte[] KEY_ALGS = {
AES_128,
Expand DownExpand Up@@ -210,6 +211,7 @@ public class KMAndroidSEProvider implements KMSEProvider {
private KMECPrivateKey attestationKey;
private KMHmacKey preSharedKey;
private KMHmacKey computedHmacKey;
private byte[] oemRootPublicKey;

private static KMAndroidSEProvider androidSEProvider = null;

Expand DownExpand Up@@ -267,6 +269,7 @@ public KMAndroidSEProvider() {
short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE +
KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE);
provisionData = new byte[totalLen];
oemRootPublicKey = new byte[EC_PUB_KEY_SIZE];

// Initialize attestationKey and preShared key with zeros.
Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
Expand DownExpand Up@@ -1315,6 +1318,7 @@ public void onSave(Element element) {
KMECPrivateKey.onSave(element, attestationKey);
KMHmacKey.onSave(element, preSharedKey);
KMHmacKey.onSave(element, computedHmacKey);
element.write(oemRootPublicKey);
}

@Override
Expand All@@ -1323,11 +1327,11 @@ public void onRestore(Element element, short oldVersion, short currentVersion) {
masterKey = KMAESKey.onRestore(element);
attestationKey = KMECPrivateKey.onRestore(element);
preSharedKey = KMHmacKey.onRestore(element);
if (oldVersion == 0) {
// Previous versions does not contain version information.
handleDataUpgradeToVersion2_0();
computedHmacKey = KMHmacKey.onRestore(element);
if (oldVersion == 0x200) {
createOemRootPublicKey();
} else {
computedHmacKey = KMHmacKey.onRestore(element);
oemRootPublicKey = (byte[]) element.readObject();
}
}

Expand All@@ -1344,7 +1348,7 @@ public short getBackupPrimitiveByteCount() {
@Override
public short getBackupObjectCount() {
short count =
(short) (1 + /* provisionData buffer */
(short) (2 + /* provisionData buffer + oemRootPublicKey */
KMAESKey.getBackupObjectCount() +
KMECPrivateKey.getBackupObjectCount() +
KMHmacKey.getBackupObjectCount() +
Expand DownExpand Up@@ -1446,20 +1450,21 @@ public KMComputedHmacKey getComputedHmacKey() {
return computedHmacKey;
}

private void handleDataUpgradeToVersion2_0() {
short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE +
KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE);
byte[] oldBuffer = provisionData;
provisionData = new byte[totalLen];
persistCertificateChain(
oldBuffer,
(short) 2,
Util.getShort(oldBuffer, (short) 0));

// Request object deletion
oldBuffer = null;
JCSystem.requestObjectDeletion();

private void createOemRootPublicKey() {
// Please note that this is a dummy EC P256 Public Key. Replace below key with a real OEM Root
// EC P256 public key while upgrading the Applet from data version 2.0 to 3.0. This change
// is not required if the Applet is installed first time with version 3.0.
oemRootPublicKey = new byte[]{
(byte) 0x04, (byte) 0xa7, (byte) 0xf7, (byte) 0x4e, (byte) 0xf2, (byte) 0x21, (byte) 0xdd,
(byte) 0x1f, (byte) 0xdb, (byte) 0x19, (byte) 0x87, (byte) 0xbf, (byte) 0x38, (byte) 0x05,
(byte) 0xed, (byte) 0x4e, (byte) 0x82, (byte) 0x84, (byte) 0xaf, (byte) 0x92, (byte) 0x99,
(byte) 0x36, (byte) 0x7e, (byte) 0xb8, (byte) 0xba, (byte) 0xda, (byte) 0x59, (byte) 0xfe,
(byte) 0xd6, (byte) 0x38, (byte) 0x70, (byte) 0x60, (byte) 0xda, (byte) 0xd5, (byte) 0x05,
(byte) 0xf2, (byte) 0x83, (byte) 0xf6, (byte) 0x0b, (byte) 0xd2, (byte) 0x82, (byte) 0xcb,
(byte) 0x8e, (byte) 0x21, (byte) 0xf5, (byte) 0xf7, (byte) 0x52, (byte) 0xff, (byte) 0x82,
(byte) 0x55, (byte) 0xca, (byte) 0xf2, (byte) 0x57, (byte) 0x07, (byte) 0x8e, (byte) 0xea,
(byte) 0x7a, (byte) 0xb0, (byte) 0x82, (byte) 0x59, (byte) 0x84, (byte) 0xe7, (byte) 0x75,
(byte) 0xfb, (byte) 0xb2};
}

@Override
Expand DownExpand Up@@ -1513,5 +1518,40 @@ private KMKeyObject createKeyObjectInstance(byte alg) {
return ptr;
}

@Override
public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) {
if (inLength != 65) {
KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
}
Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength);
}

@Override
public short readOEMRootPublicKey(byte[] buf, short off) {
Util.arrayCopyNonAtomic(oemRootPublicKey, (short) 0, buf, off, (short) oemRootPublicKey.length);
return (short) oemRootPublicKey.length;
}

@Override
public boolean ecVerify256(byte[] keyBuf, short keyBufStart, short keyBufLen,
byte[] inputDataBuf, short inputDataStart, short inputDataLength,
byte[] signature, short signatureOff, short signatureLen) {
ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic();
ecPublicKey.setW(keyBuf, keyBufStart, keyBufLen);
Signature.OneShot signer = null;
try {

signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256,
Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
signer.init(ecPublicKey, Signature.MODE_VERIFY);
return signer.verify(inputDataBuf, inputDataStart, inputDataLength,
signature, signatureOff, signatureLen);
} finally {
if (signer != null) {
signer.close();
}
}
}


}
Original file line numberDiff line numberDiff line change
Expand Up@@ -94,6 +94,7 @@ public class KMJCardSimulator implements KMSEProvider {
private KMECPrivateKey attestationKey;
private KMHmacKey preSharedKey;
private KMHmacKey computedHmacKey;
private byte[] oemRootPublicKey;

private static KMJCardSimulator jCardSimulator = null;

Expand DownExpand Up@@ -123,6 +124,7 @@ public KMJCardSimulator() {
short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE +
KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE);
provisionData = new byte[totalLen];
oemRootPublicKey = new byte[65];
jCardSimulator = this;
}

Expand DownExpand Up@@ -1405,4 +1407,32 @@ public short messageDigest256(byte[] inBuff, short inOffset,
return len;
}

@Override
public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) {
if (inLength != 65) {
KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
}
Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength);
}

@Override
public short readOEMRootPublicKey(byte[] buf, short off) {
Util.arrayCopyNonAtomic(oemRootPublicKey, (short) 0, buf, off, (short) oemRootPublicKey.length);
return (short) oemRootPublicKey.length;
}

@Override
public boolean ecVerify256(byte[] keyBuf, short keyBufStart, short keyBufLen, byte[] inputDataBuf,
short inputDataStart, short inputDataLength, byte[] signatureDataBuf,
short signatureDataStart, short signatureDataLen) {
KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic();
ecPublicKey.setW(keyBuf, keyBufStart, keyBufLen);
Signature signer = Signature
.getInstance(Signature.ALG_ECDSA_SHA_256, false);
signer.init(ecPublicKey, Signature.MODE_VERIFY);
return signer.verify(inputDataBuf, inputDataStart, inputDataLength,
signatureDataBuf, signatureDataStart, signatureDataLen);
}

}
Loading