Skip to content
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
13 changes: 13 additions & 0 deletions Applet/AndroidSEProvider/AndroidSEProvider.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="gpapi-upgrade" level="project" />
<orderEntry type="library" name="lib" level="project" />
</component>
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.globalplatform.upgrade.OnUpgradeListener;
import org.globalplatform.upgrade.UpgradeManager;

import javacard.framework.Util;

public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener {

KMAndroidSEApplet() {
Expand Down Expand Up @@ -49,8 +51,15 @@ public void onRestore(Element element) {
element.initRead();
provisionStatus = element.readByte();
keymasterState = element.readByte();
repository.onRestore(element);
seProvider.onRestore(element);
// TODO write a comment
if (dataBaseVersion <= CURRENT_DATABASE_VERSION) {
dataBaseVersion = element.readShort();
}
repository.onRestore(element, dataBaseVersion, CURRENT_DATABASE_VERSION);
seProvider.onRestore(element, dataBaseVersion, CURRENT_DATABASE_VERSION);
if (dataBaseVersion == INVALID_DATA_VERSION) {
handleDataUpgradeToVersion1();
}
}

@Override
Expand All @@ -70,18 +79,73 @@ public Element onSave() {
primitiveCount, objectCount);
element.write(provisionStatus);
element.write(keymasterState);
element.write(dataBaseVersion);
repository.onSave(element);
seProvider.onSave(element);
return element;
}

private short computePrimitveDataSize() {
// provisionStatus + keymasterState
return (short) 2;
return (short) 4;
}

private short computeObjectCount() {
return (short) 0;
}

public void handleDataUpgradeToVersion1() {
dataBaseVersion = CURRENT_DATABASE_VERSION;
// 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()
);
}
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);
repository.reclaimMemory((short) (certExpiryLen + issuerLen + certChainLen));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -119,13 +121,14 @@ public class KMAndroidSEProvider implements KMSEProvider {
private static final short MAX_OPERATIONS = 4;
private static final short HMAC_MAX_OPERATIONS = 8;
private static final short COMPUTED_HMAC_KEY_SIZE = 32;
public static final short INVALID_DATA_VERSION = 0x7FFF;

private static final short CERT_CHAIN_OFFSET = 0;
private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE;
private static final short CERT_EXPIRY_OFFSET =
(short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE);

final byte[] CIPHER_ALGS = {
private static final byte[] CIPHER_ALGS = {
Cipher.ALG_AES_BLOCK_128_CBC_NOPAD,
Cipher.ALG_AES_BLOCK_128_ECB_NOPAD,
Cipher.ALG_DES_CBC_NOPAD,
Expand All @@ -136,7 +139,7 @@ public class KMAndroidSEProvider implements KMSEProvider {
Cipher.ALG_RSA_NOPAD,
AEADCipher.ALG_AES_GCM};

final byte[] SIG_ALGS = {
private static final byte[] SIG_ALGS = {
Signature.ALG_RSA_SHA_256_PKCS1,
Signature.ALG_RSA_SHA_256_PKCS1_PSS,
Signature.ALG_ECDSA_SHA_256,
Expand All @@ -145,6 +148,14 @@ public class KMAndroidSEProvider implements KMSEProvider {
KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST,
KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST};

// [L] 256 bits - hardcoded 32 bits as per
// reference impl in keymaster.
private static final byte[] CMAC_KDF_CONSTANT_L = {
0, 0, 1, 0
};
private static final byte[] CMAC_KDF_CONSTANT_ZERO = {
0
};
// AESKey
private AESKey aesKeys[];
// DES3Key
Expand Down Expand Up @@ -715,15 +726,7 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta
// This is hardcoded to requirement - 32 byte output with two concatenated
// 16 bytes K1 and K2.
final byte n = 2; // hardcoded
// [L] 256 bits - hardcoded 32 bits as per
// reference impl in keymaster.
final byte[] L = {
0, 0, 1, 0
};
// byte
final byte[] zero = {
0
};

// [i] counter - 32 bits
short iBufLen = 4;
short keyOutLen = n * 16;
Expand All @@ -746,10 +749,10 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta
// 4 bytes of iBuf with counter in it
kdf.update(tmpArray, (short) 0, (short) iBufLen);
kdf.update(label, labelStart, (short) labelLen); // label
kdf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00
kdf.update(CMAC_KDF_CONSTANT_ZERO, (short) 0, (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00
kdf.update(context, contextStart, contextLength); // context
// 4 bytes of L - signature of 16 bytes
pos = kdf.sign(L, (short) 0, (short) 4, tmpArray,
pos = kdf.sign(CMAC_KDF_CONSTANT_L, (short) 0, (short) CMAC_KDF_CONSTANT_L.length, tmpArray,
(short) (iBufLen + pos));
i++;
}
Expand Down Expand Up @@ -1119,6 +1122,11 @@ public KMAttestationCert getAttestationCert(boolean rsaCert) {
return KMAttestationCertImpl.instance(rsaCert);
}

@Override
public KMPKCS8Decoder getPKCS8DecoderInstance() {
return KMPKCS8DecoderImpl.instance();
}

@Override
public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label,
short labelStart, short labelLen, byte[] context, short contextStart,
Expand Down Expand Up @@ -1147,8 +1155,7 @@ private void persistProvisionData(byte[] buf, short off, short len, short maxSiz
KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
}
JCSystem.beginTransaction();
Util.setShort(provisionData, copyToOff, len);
Util.arrayCopyNonAtomic(buf, off, provisionData, (short) (copyToOff + 2), len);
Util.arrayCopyNonAtomic(buf, off, provisionData, Util.setShort(provisionData, copyToOff, len), len);
JCSystem.commitTransaction();
}

Expand Down Expand Up @@ -1228,12 +1235,20 @@ public void onSave(Element element) {
}

@Override
public void onRestore(Element element) {
public void onRestore(Element element, short oldVersion, short currentVersion) {
provisionData = (byte[]) element.readObject();
masterKey = KMAESKey.onRestore(element);
attestationKey = KMECPrivateKey.onRestore(element);
preSharedKey = KMHmacKey.onRestore(element);
computedHmacKey = KMHmacKey.onRestore(element);
if (oldVersion == INVALID_DATA_VERSION) {
handleDataUpgradeToVersion1();
} else if (oldVersion <= currentVersion) {
computedHmacKey = KMHmacKey.onRestore(element);
} else {
// Invalid case
// TODO test exception in on Restore.
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
}

@Override
Expand Down Expand Up @@ -1350,4 +1365,38 @@ public void releaseAllOperations() {
public KMComputedHmacKey getComputedHmacKey() {
return computedHmacKey;
}

private void handleDataUpgradeToVersion1() {
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();

}

@Override
public short messageDigest256(byte[] inBuff, short inOffset,
short inLength, byte[] outBuff, short outOffset) {
MessageDigest.OneShot mDigest = null;
short len = 0;
try {
mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256);
len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
} finally {
if (mDigest != null) {
mDigest.close();
mDigest = null;
}
}
return len;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,31 @@ public class KMAttestationCertImpl implements KMAttestationCert {
0x03,
0x02
};

// Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension.
private static final short[] swTagIds = {
KMType.ATTESTATION_APPLICATION_ID,
KMType.CREATION_DATETIME,
KMType.USAGE_EXPIRE_DATETIME,
KMType.ORIGINATION_EXPIRE_DATETIME,
KMType.ACTIVE_DATETIME,
KMType.UNLOCKED_DEVICE_REQUIRED
};

// Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension.
private static final short[] hwTagIds = {
KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL,
KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER,
KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI,
KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT,
KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND,
KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST,
KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE,
KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID,
KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH,
KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE,
KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE};

// Validity is not fixed field
// Subject is a fixed field with only CN= Android Keystore Key - same for all the keys
private static final byte[] X509Subject = {
Expand Down Expand Up @@ -451,44 +476,27 @@ private static void pushKeyDescription() {

private static void pushSWParams() {
short last = stackPtr;
// Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension.
short[] tagIds = {
KMType.ATTESTATION_APPLICATION_ID, KMType.CREATION_DATETIME,
KMType.USAGE_EXPIRE_DATETIME, KMType.ORIGINATION_EXPIRE_DATETIME,
KMType.ACTIVE_DATETIME, KMType.UNLOCKED_DEVICE_REQUIRED};
byte index = 0;
short length = (short) swTagIds.length;
do {
pushParams(swParams, swParamsIndex, tagIds[index]);
} while (++index < tagIds.length);
pushParams(swParams, swParamsIndex, swTagIds[index]);
} while (++index < length);
pushSequenceHeader((short) (last - stackPtr));
}

private static void pushHWParams() {
short last = stackPtr;
// Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension.
short[] tagIds = {
KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL,
KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER,
KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI,
KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT,
KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND,
KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST,
KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE,
KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID,
KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH,
KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE,
KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE};

byte index = 0;
short length = (short) hwTagIds.length;
do {
if (tagIds[index] == KMType.ROOT_OF_TRUST) {
if (hwTagIds[index] == KMType.ROOT_OF_TRUST) {
pushRoT();
continue;
}
if (pushParams(hwParams, hwParamsIndex, tagIds[index])) {
if (pushParams(hwParams, hwParamsIndex, hwTagIds[index])) {
continue;
}
} while (++index < tagIds.length);
} while (++index < length);
pushSequenceHeader((short) (last - stackPtr));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class KMOperationImpl implements KMOperation {
private static final short OPER_MODE_OFFSET = 0x02;
private static final short BLOCK_MODE_OFFSET = 0x03;
private static final short MAC_LENGTH_OFFSET = 0x04;
private static final byte[] EMPTY = {};
//This will hold the length of the buffer stored inside the
//Java Card after the GCM update operation.
private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05;
Expand Down Expand Up @@ -239,13 +240,12 @@ public void abort() {
if (operationInst[0] != null) {
if ((parameters[OPER_MODE_OFFSET] == KMType.SIGN || parameters[OPER_MODE_OFFSET] == KMType.VERIFY) &&
(((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) {
byte[] empty = {};
Signature signer = (Signature) operationInst[0];
try {
if (parameters[OPER_MODE_OFFSET] == KMType.SIGN) {
signer.verify(empty, (short) 0, (short) 0, empty, (short) 0, (short) 0);
signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0);
} else {
signer.sign(empty, (short) 0, (short) 0, empty, (short) 0);
signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0);
}
} catch(Exception e) {
// Ignore.
Expand Down
Loading