From d31849071c2a36b561adff714e32c109b5d379af Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sun, 28 Nov 2021 18:37:41 +0000 Subject: [PATCH 1/2] Added applet upgrade versioning support --- .../AndroidSEProvider/AndroidSEProvider.iml | 13 + .../javacard/keymaster/KMAndroidSEApplet.java | 70 +++- .../keymaster/KMAndroidSEProvider.java | 62 +++- .../keymaster/KMAttestationCertImpl.java | 56 ++-- .../javacard/keymaster/KMOperationImpl.java | 6 +- .../android/javacard/keymaster/KMUtils.java | 8 +- .../android/javacard/keymaster/KMDecoder.java | 13 +- .../android/javacard/keymaster/KMEnum.java | 2 +- .../android/javacard/keymaster/KMEnumTag.java | 2 +- .../android/javacard/keymaster/KMInteger.java | 4 +- .../javacard/keymaster/KMKeyParameters.java | 118 ++++--- .../javacard/keymaster/KMKeymasterApplet.java | 31 +- .../javacard/keymaster/KMOperationState.java | 6 +- .../javacard/keymaster/KMRepository.java | 317 ++++++++++-------- .../javacard/keymaster/KMUpgradable.java | 2 +- 15 files changed, 428 insertions(+), 282 deletions(-) create mode 100644 Applet/AndroidSEProvider/AndroidSEProvider.iml diff --git a/Applet/AndroidSEProvider/AndroidSEProvider.iml b/Applet/AndroidSEProvider/AndroidSEProvider.iml new file mode 100644 index 00000000..1e48a48d --- /dev/null +++ b/Applet/AndroidSEProvider/AndroidSEProvider.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 7c6f31fe..d888dcc0 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -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() { @@ -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 @@ -70,6 +79,7 @@ public Element onSave() { primitiveCount, objectCount); element.write(provisionStatus); element.write(keymasterState); + element.write(dataBaseVersion); repository.onSave(element); seProvider.onSave(element); return element; @@ -77,11 +87,65 @@ public Element onSave() { 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)); + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 985d87fb..f3cad8ef 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -18,6 +18,8 @@ import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; @@ -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, @@ -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, @@ -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 @@ -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; @@ -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++; } @@ -1015,6 +1018,8 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; KMHmacKey key = (KMHmacKey) computedHmacKey; + byte[] data = new byte[32]; + key.setKey(data, (short) 0, (short) 32); Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); opr = getHmacSignOperationInstanceFromPool(); opr.setMode(KMType.VERIFY); @@ -1147,8 +1152,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(); } @@ -1228,12 +1232,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 @@ -1350,4 +1362,20 @@ 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(); + + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index e35853ca..7e5eb5cc 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -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 = { @@ -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)); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 81641ee7..de304d8f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -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; @@ -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. diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index 88b7b4d1..e41663ec 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -52,6 +52,8 @@ public class KMUtils { 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 public static final short year2051 = 2051; public static final short year2020 = 2020; + // Convert to milliseconds constants + public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3}; // -------------------------------------- public static short convertToDate(short time, byte[] scratchPad, @@ -422,11 +424,11 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, short scratchPadOff) { - byte[] shiftPos = {9, 8, 7, 6, 5, 3}; short index = 0; - while (index < (short) (shiftPos.length)) { + short length = (short) SEC_TO_MILLIS_SHIFT_POS.length; + while (index < length) { Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); - shiftLeft(buf, scratchPadOff, shiftPos[index]); + shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]); Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8); add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff)); Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8); diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index e305913c..7bd0e6ec 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -436,14 +436,14 @@ private void incrementStartOff(short inc) { } } -//Reads the offset and length values of the ByteBlobs from a CBOR array buffer. + // Reads the offset and length values of the ByteBlobs from a CBOR array buffer. public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOffset, short bufLen, byte[] out, short outOff) { - // Read Array length bufferRef[0] = buf; scratchBuf[START_OFFSET] = bufOffset; scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); short byteBlobLength = 0; + // Read Array length short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); if (expectedArrLen != payloadLength) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -459,5 +459,14 @@ public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOff index++; } } + + public short getCborBytesStartOffset(byte[] buf, short bufOffset, short bufLen) { + bufferRef[0] = buf; + scratchBuf[START_OFFSET] = bufOffset; + scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); + + readMajorTypeWithPayloadLength(BYTES_TYPE); + return scratchBuf[START_OFFSET]; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/src/com/android/javacard/keymaster/KMEnum.java index 2b55a6ce..a55c243d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -30,7 +30,7 @@ public class KMEnum extends KMType { private static KMEnum prototype; // The allowed enum types. - private static short[] types = { + private static final short[] types = { HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 0e45414e..f69aaf51 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -31,7 +31,7 @@ public class KMEnumTag extends KMTag { // The allowed tag keys of type enum tag. - private static short[] tags = { + private static final short[] tags = { ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE }; diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index fd080198..2ae32ac1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -101,14 +101,14 @@ public static short uint_16(short num) { // create integer and copy integer value public static short uint_32(byte[] num, short offset) { short ptr = instance(UINT_32); - Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); + Util.arrayCopyNonAtomic(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); return ptr; } // create integer and copy integer value public static short uint_64(byte[] num, short offset) { short ptr = instance(UINT_64); - Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); + Util.arrayCopyNonAtomic(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); return ptr; } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 3592e32b..f57fffbe 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -26,9 +26,61 @@ * arrayPtr is a pointer to array with any KMTag subtype instances. */ public class KMKeyParameters extends KMType { - - - private static short[] customTags; + + private static final short[] tagArr = { + // Unsupported tags. + KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS + }; + + private static final short[] hwEnforcedTagArr = { + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMType.ENUM_TAG, KMType.ALGORITHM, + KMType.UINT_TAG, KMType.KEYSIZE, + KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, + KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMType.ENUM_ARRAY_TAG, KMType.PADDING, + KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.BOOL_TAG, KMType.CALLER_NONCE, + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMType.ENUM_TAG, KMType.ECCURVE, + KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, + KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, + KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, + KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED + }; + + private static final short[] swEnforcedTagsArr = { + KMType.DATE_TAG, KMType.ACTIVE_DATETIME, + KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, + KMType.UINT_TAG, KMType.USERID, + KMType.DATE_TAG, KMType.CREATION_DATETIME, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY + }; + + private static final short[] invalidTagsArr = { + KMType.BYTES_TAG, KMType.NONCE, + KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, + KMType.BYTES_TAG, KMType.UNIQUE_ID, + KMType.UINT_TAG, KMType.MAC_LENGTH, + }; + + private static final short[] customTags = { + KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + }; private static KMKeyParameters prototype; @@ -111,12 +163,6 @@ public short findTag(short tagType, short tagKey) { } public static boolean hasUnsupportedTags(short keyParamsPtr) { - final short[] tagArr = { - // Unsupported tags. - KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, - KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS - }; byte index = 0; short tagInd; short tagPtr; @@ -145,33 +191,6 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { public static short makeHwEnforced(short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, short bootPatchObjPtr, byte[] scratchPad) { - final short[] hwEnforcedTagArr = { - // HW Enforced - KMType.ENUM_TAG, KMType.ORIGIN, - KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, - KMType.ENUM_TAG, KMType.ALGORITHM, - KMType.UINT_TAG, KMType.KEYSIZE, - KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, - KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, - KMType.ENUM_ARRAY_TAG, KMType.DIGEST, - KMType.ENUM_ARRAY_TAG, KMType.PADDING, - KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, - KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, - KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, - KMType.UINT_TAG, KMType.AUTH_TIMEOUT, - KMType.BOOL_TAG, KMType.CALLER_NONCE, - KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, - KMType.ENUM_TAG, KMType.ECCURVE, - KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, - KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, - KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, - KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, - KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, - KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, - KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED - }; byte index = 0; short tagInd; short arrInd = 0; @@ -224,14 +243,6 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, // ALL_USERS, EXPORTABLE missing from types.hal public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { - final short[] swEnforcedTagsArr = { - KMType.DATE_TAG, KMType.ACTIVE_DATETIME, - KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, - KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, - KMType.UINT_TAG, KMType.USERID, - KMType.DATE_TAG, KMType.CREATION_DATETIME, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY - }; byte index = 0; short tagInd; short arrInd = 0; @@ -296,12 +307,6 @@ public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfT } public static boolean isValidTag(short tagType, short tagKey) { - short[] invalidTagsArr = { - KMType.BYTES_TAG, KMType.NONCE, - KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, - KMType.BYTES_TAG, KMType.UNIQUE_ID, - KMType.UINT_TAG, KMType.MAC_LENGTH, - }; short index = 0; if (tagKey == KMType.INVALID_TAG) { return false; @@ -326,18 +331,8 @@ public static short createKeyParameters(byte[] ptrArr, short len) { } return KMKeyParameters.instance(arrPtr); } - - private static short[] getCustomTags() { - if (customTags == null) { - customTags = new short[] { - KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, - }; - } - return customTags; - } - + public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { - short[] customTags = getCustomTags(); short index = 0; short tagPtr; short len = (short) customTags.length; @@ -364,7 +359,6 @@ public static short addCustomTags(short keyParams, byte[] scratchPad, short offs public void deleteCustomTags() { short arrPtr = getVals(); - short[] customTags = getCustomTags(); short index = (short) (customTags.length - 1); short obj; while (index >= 0) { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b2df2690..9122126e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -43,6 +43,11 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + // DATABASE version + public static final short DATABASE_VERSION_1 = 1; + public static final short CURRENT_DATABASE_VERSION = DATABASE_VERSION_1; + public static final short INVALID_DATA_VERSION = 0x7FFF; + protected short dataBaseVersion; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -68,6 +73,14 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x6B, 0x65, 0x6E }; + + // getHardwareInfo constants. + private static final byte[] JAVACARD_KEYMASTER_DEVICE = { + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + private static final byte[] GOOGLE = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + // Possible states of the applet. private static final byte KM_BEGIN_STATE = 0x00; @@ -199,9 +212,11 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { boolean isUpgrading = seImpl.isUpgrading(); repository = new KMRepository(isUpgrading); initializeTransientArrays(); + dataBaseVersion = INVALID_DATA_VERSION; if (!isUpgrading) { keymasterState = KMKeymasterApplet.INIT_STATE; seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); + dataBaseVersion = CURRENT_DATABASE_VERSION; } KMType.initialize(); encoder = new KMEncoder(); @@ -615,12 +630,6 @@ public static void receiveIncoming(APDU apdu) { private void processGetHwInfoCmd(APDU apdu) { // No arguments expected - final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - // Make the response short respPtr = KMArray.instance((short) 3); KMArray resp = KMArray.cast(respPtr); @@ -628,8 +637,8 @@ private void processGetHwInfoCmd(APDU apdu) { resp.add( (short) 1, KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); - resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + JAVACARD_KEYMASTER_DEVICE, (short) 0, (short) JAVACARD_KEYMASTER_DEVICE.length)); + resp.add((short) 2, KMByteBlob.instance(GOOGLE, (short) 0, (short) GOOGLE.length)); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); // Encode the response - actual bufferProp[BUF_LEN_OFFSET] is 86 @@ -2025,7 +2034,7 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scr // empty Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); short len = (short) authVerification.length; // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); @@ -2063,7 +2072,7 @@ private boolean verifyVerificationTokenMacInLittleEndian(short verToken, byte[] // empty Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); short len = (short) authVerification.length; // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); @@ -2307,7 +2316,7 @@ private void processBeginOperationCmd(APDU apdu) { // While sending the iv back for DES/CBC mode of opeation only send // 8 bytes back. tmpVariables[1] = KMByteBlob.instance((short) 8); - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), KMByteBlob.cast(tmpVariables[1]).getBuffer(), diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 7d2a7f4d..bfd67ceb 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -88,7 +88,7 @@ public static KMOperationState instance(short opHandle) { public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr, Object hmacSignerOpr) { KMOperationState opState = proto(); opState.reset(); - Util.arrayCopy(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); + Util.arrayCopyNonAtomic(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); prototype.objRefs[OPERATION] = opr; prototype.objRefs[HMAC_SIGNER_OPERATION] = hmacSignerOpr; Util.setShort(prototype.data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); @@ -176,7 +176,7 @@ public short getAuthTime() { } public void setAuthTime(byte[] timeBuf, short start) { - Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); + Util.arrayCopyNonAtomic(timeBuf, start, data, (short) AUTH_TIME, (short) 8); dataUpdated(); } @@ -220,7 +220,7 @@ public void setUserSecureId(short integerArrayPtr) { offset += 2; while (index < length) { obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index); - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMInteger.cast(obj).getBuffer(), KMInteger.cast(obj).getStartOff(), data, diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index d8646130..9755d2e0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -29,25 +29,10 @@ */ public class KMRepository implements KMUpgradable { - public static final byte DEFAULT_TABLE_TABLE = 0; - public static final byte ATTEST_IDS_DATA_TABLE = 1; + // Data table configuration + public static final short DATA_INDEX_SIZE = 33; public static final short DATA_INDEX_ENTRY_SIZE = 4; - // Data table configuration for attestation ids - public static final short ATTEST_IDS_DATA_INDEX_SIZE = 8; - public static final short ATTEST_IDS_DATA_TABLE_SIZE = - (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE) - + KMConfigurations.TOTAL_ATTEST_IDS_SIZE; - public static final byte ATT_ID_BRAND = 0; - public static final byte ATT_ID_DEVICE = 1; - public static final byte ATT_ID_PRODUCT = 2; - public static final byte ATT_ID_SERIAL = 3; - public static final byte ATT_ID_IMEI = 4; - public static final byte ATT_ID_MEID = 5; - public static final byte ATT_ID_MANUFACTURER = 6; - public static final byte ATT_ID_MODEL = 7; - - // Data table configuration other non provisioned parameters. - public static final short DATA_INDEX_SIZE = 22; + public static final short DATA_MEM_SIZE = 2048; public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -60,24 +45,33 @@ public class KMRepository implements KMUpgradable { private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; // Data table offsets - public static final byte BEGIN_OFFSET = 0; - public static final byte HMAC_NONCE = BEGIN_OFFSET + 0; - public static final byte BOOT_OS_VERSION = BEGIN_OFFSET + 1; - public static final byte BOOT_OS_PATCH_LEVEL = BEGIN_OFFSET + 2; - public static final byte VENDOR_PATCH_LEVEL = BEGIN_OFFSET + 3; - public static final byte BOOT_PATCH_LEVEL = BEGIN_OFFSET + 4; - public static final byte BOOT_VERIFIED_BOOT_KEY = BEGIN_OFFSET + 5; - public static final byte BOOT_VERIFIED_BOOT_HASH = BEGIN_OFFSET + 6; - public static final byte BOOT_VERIFIED_BOOT_STATE = BEGIN_OFFSET + 7; - public static final byte BOOT_DEVICE_LOCKED_STATUS = BEGIN_OFFSET + 8; - public static final byte DEVICE_LOCKED_TIME = BEGIN_OFFSET + 9; - public static final byte DEVICE_LOCKED = BEGIN_OFFSET + 10; - public static final byte DEVICE_LOCKED_PASSWORD_ONLY = BEGIN_OFFSET + 11; - // Total 8 Auth Tags start offset 12 and end offset 19. - public static final byte AUTH_TAG_1 = BEGIN_OFFSET + 12; - public static final byte BOOT_ENDED_STATUS = BEGIN_OFFSET + 20; - public static final byte EARLY_BOOT_ENDED_STATUS = BEGIN_OFFSET + 21; - public static final byte END_OFFSET = 22; + public static final byte ATT_ID_BRAND = 0; + public static final byte ATT_ID_DEVICE = 1; + public static final byte ATT_ID_PRODUCT = 2; + public static final byte ATT_ID_SERIAL = 3; + public static final byte ATT_ID_IMEI = 4; + public static final byte ATT_ID_MEID = 5; + public static final byte ATT_ID_MANUFACTURER = 6; + public static final byte ATT_ID_MODEL = 7; + public static final byte COMPUTED_HMAC_KEY = 8; + public static final byte HMAC_NONCE = 9; + public static final byte CERT_ISSUER = 10; + public static final byte CERT_EXPIRY_TIME = 11; + public static final byte BOOT_OS_VERSION = 12; + public static final byte BOOT_OS_PATCH_LEVEL = 13; + public static final byte VENDOR_PATCH_LEVEL = 14; + public static final byte BOOT_PATCH_LEVEL = 15; + public static final byte BOOT_VERIFIED_BOOT_KEY = 16; + public static final byte BOOT_VERIFIED_BOOT_HASH = 17; + public static final byte BOOT_VERIFIED_BOOT_STATE = 18; + public static final byte BOOT_DEVICE_LOCKED_STATUS = 19; + public static final byte DEVICE_LOCKED_TIME = 20; + public static final byte DEVICE_LOCKED = 21; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 22; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + public static final byte AUTH_TAG_1 = 23; + public static final byte BOOT_ENDED_STATUS = 31; + public static final byte EARLY_BOOT_ENDED_STATUS = 32; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -94,8 +88,8 @@ public class KMRepository implements KMUpgradable { public static final short DEVICE_LOCKED_PASSWORD_ONLY_SIZE = 1; public static final short BOOT_STATE_SIZE = 1; public static final short MAX_OPS = 4; - public static final byte BOOT_KEY_MAX_SIZE = 32; - public static final byte BOOT_HASH_MAX_SIZE = 32; + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; public static final short MAX_BLOB_STORAGE = 8; public static final short AUTH_TAG_LENGTH = 16; public static final short AUTH_TAG_COUNTER_SIZE = 4; @@ -103,23 +97,10 @@ public class KMRepository implements KMUpgradable { public static final short BOOT_ENDED_FLAG_SIZE = 1; public static final short EARLY_BOOT_ENDED_FLAG_SIZE = 1; private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; - - private static final short DATA_MEM_SIZE = (DATA_INDEX_ENTRY_SIZE * DATA_INDEX_SIZE) - + HMAC_SEED_NONCE_SIZE - + OS_VERSION_SIZE - + OS_PATCH_SIZE - + VENDOR_PATCH_SIZE - + BOOT_PATCH_SIZE - + BOOT_KEY_MAX_SIZE - + BOOT_HASH_MAX_SIZE - + BOOT_STATE_SIZE - + BOOT_DEVICE_LOCK_FLAG_SIZE - + DEVICE_LOCK_TS_SIZE - + DEVICE_LOCKED_FLAG_SIZE - + DEVICE_LOCKED_PASSWORD_ONLY_SIZE - + (8 * AUTH_TAG_ENTRY_SIZE) - + BOOT_ENDED_FLAG_SIZE - + EARLY_BOOT_ENDED_FLAG_SIZE; + + // Buffer type + public static final byte DEFAULT_BUF_TYPE = 0; + public static final byte ATTEST_IDS_BUF_TYPE = 1; // Class Attributes private Object[] operationStateTable; @@ -127,9 +108,8 @@ public class KMRepository implements KMUpgradable { private short[] heapIndex; private byte[] dataTable; private short dataIndex; - private byte[] attestIdsTable; - private short attestIdsIdsIndex; private short[] reclaimIndex; + private short attestIdsIndex; // This variable is used to monitor the power reset status as the Applet does not get // any power reset event. Initially the value of this variable is set to POWER_RESET_STATUS_FLAG. // If the power reset happens then this value becomes 0. @@ -276,7 +256,7 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOper KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), KMByteBlob.cast(buf).length()))) { - Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + Util.arrayCopyNonAtomic(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), KMOperationState.MAX_DATA); operations[index] = op; hmacSignerOprs[index] = hmacSignerOp; @@ -291,13 +271,13 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOper offset = (short) (index * OPER_DATA_LEN); if (0 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) { oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] = 1;/*reserved */ - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), OPERATION_HANDLE_SIZE); - Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + Util.arrayCopyNonAtomic(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), KMOperationState.MAX_DATA); operations[index] = op; hmacSignerOprs[index] = hmacSignerOp; @@ -433,54 +413,32 @@ public short alloc(short length) { return (short) (heapIndex[0] - length); } - private short dataAlloc(byte dataTableType, short length) { - byte[] dataTable = getDataTable(dataTableType); - short dataIndex = getDataTableIndex(dataTableType); + private short dataAlloc(byte bufType, short length) { + short maxSize = getMaxLimitSize(bufType); + short dataIndex = getDataTableIndex(bufType); if (length < 0) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - if (((short) (dataIndex + length)) > dataTable.length) { + if (((short) (dataIndex + length)) > maxSize) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } dataIndex += length; - setDataTableIndex(dataTableType, dataIndex); + setDataTableIndex(bufType, dataIndex); return (short) (dataIndex - length); } - - private void newDataTable(boolean isUpgrading) { - if (!isUpgrading) { - if (dataTable == null) { - dataTable = new byte[DATA_MEM_SIZE]; - dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - } - if (attestIdsTable == null) { - attestIdsTable = new byte[ATTEST_IDS_DATA_TABLE_SIZE]; - attestIdsIdsIndex = (short) (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - } - } - } - - public byte[] getDataTable(byte dataTableType) { - if (dataTableType == ATTEST_IDS_DATA_TABLE) { - return this.attestIdsTable; - } else { - return this.dataTable; - } - } - - private short getDataTableIndex(byte dataTableType) { - if (dataTableType == ATTEST_IDS_DATA_TABLE) { - return this.attestIdsIdsIndex; + private short getDataTableIndex(byte bufType) { + if (bufType == ATTEST_IDS_BUF_TYPE) { + return this.attestIdsIndex; } else { return this.dataIndex; } } - - private void setDataTableIndex(byte dataTableType, short index) { - if (dataTableType == ATTEST_IDS_DATA_TABLE) { + + private void setDataTableIndex(byte bufType, short index) { + if (bufType == ATTEST_IDS_BUF_TYPE) { JCSystem.beginTransaction(); - this.attestIdsIdsIndex = index; + this.attestIdsIndex = index; JCSystem.commitTransaction(); } else { JCSystem.beginTransaction(); @@ -488,9 +446,30 @@ private void setDataTableIndex(byte dataTableType, short index) { JCSystem.commitTransaction(); } } + + private short getMaxLimitSize(byte bufType) { + if (bufType == ATTEST_IDS_BUF_TYPE) { + return (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + } else { // Default buf type. + return (short) dataTable.length; + } + } + + private void newDataTable(boolean isUpgrading) { + if (!isUpgrading) { + if (dataTable == null) { + dataTable = new byte[DATA_MEM_SIZE]; + attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + } + } + } + + public byte[] getDataTable() { + return dataTable; + } - private void clearDataEntry(byte dataTableType, short id) { - byte[] dataTable = getDataTable(dataTableType); + 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) { @@ -501,13 +480,30 @@ private void clearDataEntry(byte dataTableType, short id) { } } - private void writeDataEntry(byte dataTableType, short id, byte[] buf, short offset, short len) { + private void writeDataEntry(short id, byte[] buf, short offset, short len) { + writeDataEntry(DEFAULT_BUF_TYPE, id, buf, offset, len); + } + + 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 void writeDataEntry(byte bufType, short id, byte[] buf, short offset, short len) { short dataPtr; - byte[] dataTable = getDataTable(dataTableType); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen == 0) { - dataPtr = dataAlloc(dataTableType, len); + dataPtr = dataAlloc(bufType, len); // Begin Transaction JCSystem.beginTransaction(); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); @@ -528,43 +524,11 @@ private void writeDataEntry(byte dataTableType, short id, byte[] buf, short offs } } - private short readDataEntry(byte dataTableType, short id, byte[] buf, short offset) { - byte[] dataTable = getDataTable(dataTableType); - 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 void clearDataEntry(short id) { - clearDataEntry(DEFAULT_TABLE_TABLE, id); - } - - private void writeDataEntry(short id, byte[] buf, short offset, short len) { - writeDataEntry(DEFAULT_TABLE_TABLE, id, buf, offset, len); - } - - private short readDataEntry(short id, byte[] buf, short offset) { - return readDataEntry(DEFAULT_TABLE_TABLE, id, buf, offset); - } - - private short dataLength(byte dataTableType, short id) { - byte[] dataTable = getDataTable(dataTableType); + private short dataLength(short id) { id = (short) (id * DATA_INDEX_ENTRY_SIZE); return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); } - private short dataLength(short id) { - return dataLength(DEFAULT_TABLE_TABLE, id); - } - public byte[] getHeap() { return heap; } @@ -573,35 +537,58 @@ public short getHmacNonce() { return readData(HMAC_NONCE); } + public short getComputedHmacKey() { + return readData(COMPUTED_HMAC_KEY); + } public void persistAttId(byte id, byte[] buf, short start, short len) { - writeDataEntry(ATTEST_IDS_DATA_TABLE, id, buf, start, len); + writeDataEntry(ATTEST_IDS_BUF_TYPE, id, buf, start, len); } public short getAttId(byte id) { - return readData(ATTEST_IDS_DATA_TABLE, id); + return readData(id); } public void deleteAttIds() { JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(attestIdsTable, (short) 0, (short) attestIdsTable.length, (byte) 0); - attestIdsIdsIndex = (short) (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + attestIdsIndex = (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + Util.arrayFillNonAtomic(dataTable, attestIdsIndex, KMConfigurations.TOTAL_ATTEST_IDS_SIZE, (byte) 0); JCSystem.commitTransaction(); } - public short readData(byte dataTableType, short id) { - short len = dataLength(dataTableType, id); + public short getIssuer() { + return readData(CERT_ISSUER); + } + + public short readData(short id) { + short len = dataLength(id); if (len != 0) { short blob = KMByteBlob.instance(len); - readDataEntry(dataTableType, id, KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff()); + readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); return blob; } return KMType.INVALID_VALUE; } - public short readData(short id) { - return readData(DEFAULT_TABLE_TABLE, id); + public short readData(byte[] dataTable, short id, byte[] buf, short startOff, short bufLen) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (len > bufLen) { + return KMType.INVALID_VALUE; + } + if (len != 0) { + Util.arrayCopyNonAtomic( + dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), + buf, + startOff, + len); + } + return len; + } + + public short getCertExpiryTime() { + return readData(CERT_EXPIRY_TIME); } public short getOsVersion() { @@ -943,17 +930,24 @@ public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short l @Override public void onSave(Element ele) { ele.write(dataIndex); - ele.write(attestIdsIdsIndex); ele.write(dataTable); - ele.write(attestIdsTable); + ele.write(attestIdsIndex); } @Override - public void onRestore(Element ele) { + public void onRestore(Element ele, short oldVersion, short currentVersion) { dataIndex = ele.readShort(); - attestIdsIdsIndex = ele.readShort(); dataTable = (byte[]) ele.readObject(); - attestIdsTable = (byte[]) ele.readObject(); + if (oldVersion == KMKeymasterApplet.INVALID_DATA_VERSION) { + // Previous revisions does not contain version information. + handleDataUpgradeToVersion1(); + } else if (oldVersion <= currentVersion) { + // No change in the data base version. + attestIdsIndex = ele.readShort(); + } else { + // Invalid case + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } } @Override @@ -965,9 +959,9 @@ public short getBackupPrimitiveByteCount() { @Override public short getBackupObjectCount() { // dataTable - return (short) 2; + return (short) 1; } - + public boolean getBootEndedStatus() { short blob = readData(BOOT_ENDED_STATUS); if (blob == KMType.INVALID_VALUE) { @@ -1004,4 +998,29 @@ public void setEarlyBootEndedStatus(boolean flag) { writeDataEntry(EARLY_BOOT_ENDED_STATUS, getHeap(), start, EARLY_BOOT_ENDED_FLAG_SIZE); } + public void handleDataUpgradeToVersion1() { + byte[] oldDataTable = dataTable; + dataTable = new byte[2048]; + attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + // temp buffer. + short startOffset = alloc((short) 256); + + short index = ATT_ID_BRAND; + short len = 0; + while (index <= DEVICE_LOCKED) { + len = readData(oldDataTable, index, heap, startOffset, (short) 256); + writeDataEntry(index, heap, startOffset, len); + index++; + } + // set default values for the new IDS. + setDeviceLockPasswordOnly(false); + setBootEndedStatus(false); + setEarlyBootEndedStatus(false); + + // Request object deletion + oldDataTable = null; + JCSystem.requestObjectDeletion(); + } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java index 0a241652..87204a06 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java @@ -21,7 +21,7 @@ public interface KMUpgradable { void onSave(Element ele); - void onRestore(Element ele); + void onRestore(Element ele, short oldVersion, short currentVersion); short getBackupPrimitiveByteCount(); From 9c05e26e49d3471b10ffbb170b206473e9c1bc79 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Mon, 29 Nov 2021 08:48:47 +0000 Subject: [PATCH 2/2] optimized the AUTH_DATA creation for Keyblob --- .../keymaster/KMAndroidSEProvider.java | 25 +- .../keymaster/KMPKCS8DecoderImpl.java | 185 ++++++++++++++ .../javacard/keymaster/KMJCardSimulator.java | 24 +- .../keymaster/KMPKCS8DecoderImpl.java | 185 ++++++++++++++ .../javacard/keymaster/KMKeymasterApplet.java | 120 ++++----- .../javacard/keymaster/KMPKCS8Decoder.java | 231 +++--------------- .../javacard/keymaster/KMSEProvider.java | 19 ++ 7 files changed, 514 insertions(+), 275 deletions(-) create mode 100644 Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java create mode 100644 Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index f3cad8ef..9c40ffab 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -1018,8 +1018,6 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; KMHmacKey key = (KMHmacKey) computedHmacKey; - byte[] data = new byte[32]; - key.setKey(data, (short) 0, (short) 32); Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); opr = getHmacSignOperationInstanceFromPool(); opr.setMode(KMType.VERIFY); @@ -1124,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, @@ -1378,4 +1381,22 @@ private void handleDataUpgradeToVersion1() { 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; + } + } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java new file mode 100644 index 00000000..8585587f --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -0,0 +1,185 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { + public static final byte ASN1_OCTET_STRING= 0x04; + public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_INTEGER= 0x02; + public static final byte ASN1_A0_TAG = (byte) 0xA0; + public static final byte ASN1_A1_TAG = (byte) 0xA1; + public static final byte ASN1_BIT_STRING = 0x03; + public static final byte[] EC_CURVE = { + 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, + 0x01,0x07 + }; + public static final byte[] RSA_ALGORITHM = { + 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, + (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + }; + public static final byte[] EC_ALGORITHM = { + 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, + 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, + (byte)0xce,0x3d,0x03,0x01,0x07 + }; + private byte[] data; + private short start; + private short length; + private short cur; + private static KMPKCS8DecoderImpl inst; + private KMPKCS8DecoderImpl(){ + start = 0; + length = 0; + cur = 0; + } + + @Override + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + @Override + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + //Seq[Int,Int,Int,Int,] + public short decodeRsaPrivateKey(short version){ + short resp = KMArray.instance((short)3); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len =header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_INTEGER); + short modulus = getModulus(len); + len = header(ASN1_INTEGER); + short pubKey = KMByteBlob.instance(len); + getBytes(pubKey); + len = header(ASN1_INTEGER); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + KMArray.cast(resp).add((short)0, modulus); + KMArray.cast(resp).add((short)1, pubKey); + KMArray.cast(resp).add((short)2, privKey); + return resp; + } + + // Seq [Int, Blob] + public void decodeCommon(short version, byte[] alg){ + short len = header(ASN1_SEQUENCE); + len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_SEQUENCE); + short blob = KMByteBlob.instance(len); + getBytes(blob); + if(Util.arrayCompare( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + alg, + (short)0,KMByteBlob.cast(blob).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + //Seq[Int,blob,blob] + public short decodeEcPrivateKey(short version){ + short resp = KMArray.instance((short)2); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_OCTET_STRING); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + validateTag0IfPresent(); + header(ASN1_A1_TAG); + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + KMArray.cast(resp).add((short)0, pubKey); + KMArray.cast(resp).add((short)1, privKey); + return resp; + } + private void validateTag0IfPresent(){ + if(data[cur] != ASN1_A0_TAG) return;; + short len = header(ASN1_A0_TAG); + if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + incrementCursor(len); + } + private short header(short tag){ + short t = getByte(); + if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + return getLength(); + } + + private byte getByte(){ + byte d = data[cur]; + incrementCursor((short)1); + return d; + } + + private short getShort(){ + short d = Util.getShort(data, cur); + incrementCursor((short)2); + return d; + } + + private short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + private void getBytes(short blob){ + short len = KMByteBlob.cast(blob).length(); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), len); + incrementCursor(len); + } + + private short getLength(){ + byte len = getByte(); + if(len >= 0) return len; + len = (byte)(len & 0x7F); + if(len == 1) return (short)(getByte() & 0xFF); + else if(len == 2) return getShort(); + else KMException.throwIt(KMError.UNKNOWN_ERROR); + return KMType.INVALID_VALUE; //should not come here + } + public static KMPKCS8DecoderImpl instance() { + if (inst == null) { + inst = new KMPKCS8DecoderImpl(); + } + return inst; + } + + public void init(short blob) { + data = KMByteBlob.cast(blob).getBuffer(); + start = KMByteBlob.cast(blob).getStartOff(); + length = KMByteBlob.cast(blob).length(); + cur = start; + } + + public void incrementCursor(short n){ + cur += n; + if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + } +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index e0d2b546..2086620f 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -40,6 +40,7 @@ import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; import javacard.security.RandomData; @@ -1178,6 +1179,12 @@ public void addRngEntropy(byte[] num, short offset, short length) { public KMAttestationCert getAttestationCert(boolean rsaCert) { return KMAttestationCertImpl.instance(rsaCert); } + + @Override + public KMPKCS8Decoder getPKCS8DecoderInstance() { + return KMPKCS8DecoderImpl.instance(); + } + private short getProvisionDataBufferOffset(byte dataType) { switch(dataType) { case CERTIFICATE_CHAIN: @@ -1286,7 +1293,7 @@ public void onSave(Element ele) { } @Override - public void onRestore(Element ele) { + public void onRestore(Element ele, short oldVersion, short currentVersion) { } @Override @@ -1383,4 +1390,19 @@ public KMComputedHmacKey getComputedHmacKey() { public void releaseAllOperations() { //Do nothing. } + + @Override + public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.getInitializedMessageDigestInstance(MessageDigest.ALG_SHA_256, false); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } catch (Exception e) { + + } + return len; + } + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java new file mode 100644 index 00000000..8585587f --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -0,0 +1,185 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { + public static final byte ASN1_OCTET_STRING= 0x04; + public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_INTEGER= 0x02; + public static final byte ASN1_A0_TAG = (byte) 0xA0; + public static final byte ASN1_A1_TAG = (byte) 0xA1; + public static final byte ASN1_BIT_STRING = 0x03; + public static final byte[] EC_CURVE = { + 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, + 0x01,0x07 + }; + public static final byte[] RSA_ALGORITHM = { + 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, + (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + }; + public static final byte[] EC_ALGORITHM = { + 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, + 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, + (byte)0xce,0x3d,0x03,0x01,0x07 + }; + private byte[] data; + private short start; + private short length; + private short cur; + private static KMPKCS8DecoderImpl inst; + private KMPKCS8DecoderImpl(){ + start = 0; + length = 0; + cur = 0; + } + + @Override + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + @Override + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + //Seq[Int,Int,Int,Int,] + public short decodeRsaPrivateKey(short version){ + short resp = KMArray.instance((short)3); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len =header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_INTEGER); + short modulus = getModulus(len); + len = header(ASN1_INTEGER); + short pubKey = KMByteBlob.instance(len); + getBytes(pubKey); + len = header(ASN1_INTEGER); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + KMArray.cast(resp).add((short)0, modulus); + KMArray.cast(resp).add((short)1, pubKey); + KMArray.cast(resp).add((short)2, privKey); + return resp; + } + + // Seq [Int, Blob] + public void decodeCommon(short version, byte[] alg){ + short len = header(ASN1_SEQUENCE); + len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_SEQUENCE); + short blob = KMByteBlob.instance(len); + getBytes(blob); + if(Util.arrayCompare( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + alg, + (short)0,KMByteBlob.cast(blob).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + //Seq[Int,blob,blob] + public short decodeEcPrivateKey(short version){ + short resp = KMArray.instance((short)2); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_OCTET_STRING); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + validateTag0IfPresent(); + header(ASN1_A1_TAG); + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + KMArray.cast(resp).add((short)0, pubKey); + KMArray.cast(resp).add((short)1, privKey); + return resp; + } + private void validateTag0IfPresent(){ + if(data[cur] != ASN1_A0_TAG) return;; + short len = header(ASN1_A0_TAG); + if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + incrementCursor(len); + } + private short header(short tag){ + short t = getByte(); + if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + return getLength(); + } + + private byte getByte(){ + byte d = data[cur]; + incrementCursor((short)1); + return d; + } + + private short getShort(){ + short d = Util.getShort(data, cur); + incrementCursor((short)2); + return d; + } + + private short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + private void getBytes(short blob){ + short len = KMByteBlob.cast(blob).length(); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), len); + incrementCursor(len); + } + + private short getLength(){ + byte len = getByte(); + if(len >= 0) return len; + len = (byte)(len & 0x7F); + if(len == 1) return (short)(getByte() & 0xFF); + else if(len == 2) return getShort(); + else KMException.throwIt(KMError.UNKNOWN_ERROR); + return KMType.INVALID_VALUE; //should not come here + } + public static KMPKCS8DecoderImpl instance() { + if (inst == null) { + inst = new KMPKCS8DecoderImpl(); + } + return inst; + } + + public void init(short blob) { + data = KMByteBlob.cast(blob).getBuffer(); + start = KMByteBlob.cast(blob).getStartOff(); + length = KMByteBlob.cast(blob).length(); + cur = start; + } + + public void incrementCursor(short n){ + cur += n; + if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 9122126e..77bfdf6a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -40,7 +40,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final short MAX_LENGTH = (short) 0x2000; private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; - private static final short MAX_AUTH_DATA_SIZE = (short) 512; + private static final short MAX_AUTH_DATA_SIZE = (short) 256; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; // DATABASE version @@ -3090,8 +3090,7 @@ private void decodeRawECKey() { private void decodePKCS8ECKeys() { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); - short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); + short keyBlob = seProvider.getPKCS8DecoderInstance().decodeEc(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); data[SECRET] = KMArray.cast(keyBlob).get((short) 1); } @@ -3242,6 +3241,7 @@ private void importTDESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // Read Minimum Mac length - it must not be present + //Added this error check based on default reference implementation. tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { @@ -3296,8 +3296,7 @@ private void importAESKey(byte[] scratchPad) { private void importRSAKey(byte[] scratchPad) { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); - short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]); + short keyblob = seProvider.getPKCS8DecoderInstance().decodeRsa(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0); short pubKeyExp = KMArray.cast(keyblob).get((short)1); data[SECRET] = KMArray.cast(keyblob).get((short) 2); @@ -3998,87 +3997,64 @@ private static void encryptSecret(byte[] scratchPad) { } private static void makeAuthData(byte[] scratchPad) { - tmpVariables[0] = - addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[SW_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - tmpVariables[1] = KMArray.instance((short) (tmpVariables[0] + 1)); - } else { - tmpVariables[1] = KMArray.instance(tmpVariables[0]); - } - // convert scratch pad to KMArray - short index = 0; - short objPtr; - while (index < tmpVariables[0]) { - objPtr = Util.getShort(scratchPad, (short) (index * 2)); - KMArray.cast(tmpVariables[1]).add(index, objPtr); - index++; - } - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - KMArray.cast(tmpVariables[1]).add(index, data[PUB_KEY]); - } - - data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); - short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); - data[AUTH_DATA_LENGTH] = len; - } - - private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { - short index = (short) (offset * 2); - short tagInd = 0; - short tagPtr; - short arrLen = KMArray.cast(dataArrPtr).length(); - while (tagInd < arrLen) { - tagPtr = KMArray.cast(dataArrPtr).get(tagInd); - Util.setShort(aadBuf, index, tagPtr); - index += 2; - tagInd++; - } - return tagInd; + + short arrayLen = 3; + if (KMArray.cast(data[KEY_BLOB]).length() == 5) { + arrayLen = 4; + } + short params = KMArray.instance((short) arrayLen); + KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (4 == arrayLen) { + KMArray.cast(params).add((short) 3, data[PUB_KEY]); + } + + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); + short index = 0; + short len = 0; + short paramsLen = KMArray.cast(params).length(); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + while (index < paramsLen) { + short tag = KMArray.cast(params).get(index); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + (short) (authIndex + len + 32), (short) 32); + len = seProvider.messageDigest256(repository.getHeap(), + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + if (len != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + index++; + } + + data[AUTH_DATA] = authIndex; + data[AUTH_DATA_LENGTH] = len; } private static short deriveKey(byte[] scratchPad) { - tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); - tmpVariables[1] = repository.alloc(DERIVE_KEY_INPUT_SIZE); - // generate derivation material from hidden parameters - tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); - if (DERIVE_KEY_INPUT_SIZE > tmpVariables[2]) { - // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE - Util.arrayCopyNonAtomic(repository.getHeap(), (short) (data[AUTH_DATA]), - repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[2]), - (short) (DERIVE_KEY_INPUT_SIZE - tmpVariables[2])); - } + // KeyDerivation: - // 1. Do HMAC Sign, with below input parameters. - // Key - 128 bit master key - // Input data - HIDDEN_PARAMETERS + KeyCharacateristics - // - Truncate beyond 256 bytes. + // 1. Do HMAC Sign, Auth data. // 2. HMAC Sign generates an output of 32 bytes length. - // Consume only first 16 bytes as derived key. + // Consume only first 16 bytes as derived key. // Hmac sign. - tmpVariables[3] = seProvider.hmacKDF( + short len = seProvider.hmacKDF( seProvider.getMasterKey(), repository.getHeap(), - tmpVariables[1], - DERIVE_KEY_INPUT_SIZE, + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], scratchPad, (short) 0); - if (tmpVariables[3] < 16) { + if (len < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - tmpVariables[3] = 16; + len = 16; + data[DERIVED_KEY] = repository.alloc(len); // store the derived secret in data dictionary - data[DERIVED_KEY] = tmpVariables[1]; Util.arrayCopyNonAtomic( - scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); - return tmpVariables[3]; + scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], len); + return len; } // This function masks the error code with POWER_RESET_MASK_FLAG diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index 5e67eb83..d8d472e2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -1,205 +1,36 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.javacard.keymaster; -import javacard.framework.Util; +public interface KMPKCS8Decoder { -public class KMPKCS8Decoder { - public static final byte ASN1_OCTET_STRING= 0x04; - public static final byte ASN1_SEQUENCE= 0x30; - public static final byte ASN1_INTEGER= 0x02; - public static final byte ASN1_A0_TAG = (byte) 0xA0; - public static final byte ASN1_A1_TAG = (byte) 0xA1; - public static final byte ASN1_BIT_STRING = 0x03; - public static final byte[] EC_CURVE = { - 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, - 0x01,0x07 - }; - public static final byte[] RSA_ALGORITHM = { - 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, - (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 - }; - public static final byte[] EC_ALGORITHM = { - 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, - 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, - (byte)0xce,0x3d,0x03,0x01,0x07 - }; - private byte[] data; - private short start; - private short length; - private short cur; - private static KMPKCS8Decoder inst; - private KMPKCS8Decoder(){ - start = 0; - length = 0; - cur = 0; - } - - public short decodeRsa(short blob){ - init(blob); - decodeCommon((short)0, RSA_ALGORITHM); - return decodeRsaPrivateKey((short)0); - } - - public short decodeEc(short blob){ - init(blob); - decodeCommon((short)0, EC_ALGORITHM); - return decodeEcPrivateKey((short)1); - } - - public short decodeEcSubjectPublicKeyInfo(short blob) { - init(blob); - header(ASN1_SEQUENCE); - short len = header(ASN1_SEQUENCE); - short ecPublicInfo = KMByteBlob.instance(len); - getBytes(ecPublicInfo); - if(Util.arrayCompare( - KMByteBlob.cast(ecPublicInfo).getBuffer(), - KMByteBlob.cast(ecPublicInfo).getStartOff(), - EC_ALGORITHM, - (short)0,KMByteBlob.cast(ecPublicInfo).length()) !=0){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - len = header(ASN1_BIT_STRING); - if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - byte unusedBits = getByte(); - if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); - short pubKey = KMByteBlob.instance((short)(len -1)); - getBytes(pubKey); - return pubKey; - } - - //Seq[Int,Int,Int,Int,] - public short decodeRsaPrivateKey(short version){ - short resp = KMArray.instance((short)3); - header(ASN1_OCTET_STRING); - header(ASN1_SEQUENCE); - short len =header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); - len = header(ASN1_INTEGER); - short modulus = getModulus(len); - len = header(ASN1_INTEGER); - short pubKey = KMByteBlob.instance(len); - getBytes(pubKey); - len = header(ASN1_INTEGER); - short privKey = KMByteBlob.instance(len); - getBytes(privKey); - KMArray.cast(resp).add((short)0, modulus); - KMArray.cast(resp).add((short)1, pubKey); - KMArray.cast(resp).add((short)2, privKey); - return resp; - } - - // Seq [Int, Blob] - public void decodeCommon(short version, byte[] alg){ - short len = header(ASN1_SEQUENCE); - len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - short ver = getByte(); - if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); - len = header(ASN1_SEQUENCE); - short blob = KMByteBlob.instance(len); - getBytes(blob); - if(Util.arrayCompare( - KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), - alg, - (short)0,KMByteBlob.cast(blob).length()) !=0){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } - - //Seq[Int,blob,blob] - public short decodeEcPrivateKey(short version){ - short resp = KMArray.instance((short)2); - header(ASN1_OCTET_STRING); - header(ASN1_SEQUENCE); - short len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); - len = header(ASN1_OCTET_STRING); - short privKey = KMByteBlob.instance(len); - getBytes(privKey); - validateTag0IfPresent(); - header(ASN1_A1_TAG); - len = header(ASN1_BIT_STRING); - if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - byte unusedBits = getByte(); - if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); - short pubKey = KMByteBlob.instance((short)(len -1)); - getBytes(pubKey); - KMArray.cast(resp).add((short)0, pubKey); - KMArray.cast(resp).add((short)1, privKey); - return resp; - } - private void validateTag0IfPresent(){ - if(data[cur] != ASN1_A0_TAG) return;; - short len = header(ASN1_A0_TAG); - if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); - if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); - incrementCursor(len); - } - private short header(short tag){ - short t = getByte(); - if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); - return getLength(); - } - - private byte getByte(){ - byte d = data[cur]; - incrementCursor((short)1); - return d; - } - - private short getShort(){ - short d = Util.getShort(data, cur); - incrementCursor((short)2); - return d; - } + /** + * Decodes the PKCS8 encoded RSA Key and extracts the private and public key + * + * @param Instance of the PKCS8 encoded data + * @return Instance of KMArray holding RSA public key, RSA private key and modulus. + */ + short decodeRsa(short blob); - private short getModulus(short modulusLen) { - if(0 == data[cur] && modulusLen == 257) { - incrementCursor((short) 1); - modulusLen--; - } - short blob = KMByteBlob.instance(modulusLen); - getBytes(blob); - return blob; - } - - private void getBytes(short blob){ - short len = KMByteBlob.cast(blob).length(); - Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), len); - incrementCursor(len); - } - - private short getLength(){ - byte len = getByte(); - if(len >= 0) return len; - len = (byte)(len & 0x7F); - if(len == 1) return (short)(getByte() & 0xFF); - else if(len == 2) return getShort(); - else KMException.throwIt(KMError.UNKNOWN_ERROR); - return KMType.INVALID_VALUE; //should not come here - } - public static KMPKCS8Decoder instance() { - if (inst == null) { - inst = new KMPKCS8Decoder(); - } - return inst; - } - - public void init(short blob) { - data = KMByteBlob.cast(blob).getBuffer(); - start = KMByteBlob.cast(blob).getStartOff(); - length = KMByteBlob.cast(blob).length(); - cur = start; - } - - public void incrementCursor(short n){ - cur += n; - if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); - } + /** + * Decodes the PKCS8 encoded EC Key and extracts the private and public key + * + * @param Instance of the PKCS8 encoded data. + * @return Instance of KMArray holding EC public key and EC private key. + */ + short decodeEc(short blob); + } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index da57e3ee..b7cb5609 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -453,6 +453,13 @@ KMOperation initAsymmetricOperation( */ KMAttestationCert getAttestationCert(boolean rsaCert); + /** + * Returns the implementation of the PKCS8 decoder. + * + * @return Instance of PKCS8 decoder. + */ + KMPKCS8Decoder getPKCS8DecoderInstance(); + /** * This operation persists the provision data in the persistent memory. * @@ -591,4 +598,16 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, */ void releaseAllOperations(); + /** + * This is a one-shot operation the does digest of the input mesage. + * + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. + */ + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset); + }