diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 642775f1..39559f70 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -27,7 +27,9 @@ import javacard.framework.APDU; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; +import javacard.security.CryptoException; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { @@ -47,6 +49,8 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis INS_KEYMINT_PROVIDER_APDU_START + 6; private static final byte INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; + private static final byte INS_SET_BOOT_ENDED_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 8; private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; public static final byte BOOT_KEY_MAX_SIZE = 32; @@ -59,8 +63,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; private static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; - private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; - private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -101,6 +104,13 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); break; + + case INS_SET_BOOT_ENDED_CMD: + //set the flag to mark boot ended + repository.setBootEndedStatus(true); + sendError(apdu, KMError.OK); + break; + default: super.process(apdu); break; @@ -135,7 +145,6 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - provisionStatus |= PROVISION_STATUS_BOOT_PARAM; break; case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: @@ -150,6 +159,14 @@ public void process(APDU apdu) { super.process(apdu); break; } + } catch (KMException exception) { + sendError(apdu, KMException.reason()); + } catch (ISOException exp) { + sendError(apdu, mapISOErrorToKMError(exp.getReason())); + } catch (CryptoException e) { + sendError(apdu, mapCryptoErrorToKMError(e.getReason())); + } catch (Exception e) { + sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); } finally { repository.clean(); } @@ -310,7 +327,8 @@ private void processGetProvisionStatusCmd(APDU apdu) { private void processSetBootParamsCmd(APDU apdu) { short argsProto = KMArray.instance((short) 5); - + + byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments // Argument 0 Boot Patch level KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); @@ -355,6 +373,11 @@ private void processSetBootParamsCmd(APDU apdu) { enumVal = KMEnum.cast(bootParam).getVal(); ((KMAndroidSEProvider) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + // Clear the Computed SharedHmac and Hmac nonce from persistent memory. + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE, (byte) 0); + seProvider.createComputedHmacKey(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE); + super.reboot(); sendError(apdu, KMError.OK); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index c1cdb91d..f9fca3d0 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -89,6 +89,32 @@ public class KMAttestationCertImpl implements KMAttestationCert { 0x05, 0x00 }; + + + // 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 = { @@ -241,9 +267,9 @@ public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratch public KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) { if(!derEncoded) { if (usageExpiryTimeObj != KMType.INVALID_VALUE) { - // compare if the expiry time is greater then 2051 then use generalized + // compare if the expiry time is greater then 2050 then use generalized // time format else use utc time format. - short tmpVar = KMInteger.uint_64(KMUtils.firstJan2051, (short) 0); + short tmpVar = KMInteger.uint_64(KMUtils.firstJan2050, (short) 0); if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) { usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, false); @@ -505,44 +531,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)); } @@ -1046,11 +1055,4 @@ public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte return this; } - //Check - /* - * private void print(byte[] buf, short start, short length){ StringBuilder sb = - * new StringBuilder(length * 2); for(short i = start; i < (start+length); i - * ++){ sb.append(String.format("%02x", buf[i])); } System.out.println( - * sb.toString()); } - */ } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java new file mode 100644 index 00000000..3eb4a2e4 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -0,0 +1,23 @@ +/* + * 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" BASIS, + * 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; + +public class KMConfigurations { + // Machine types + public static final byte LITTLE_ENDIAN = 0x00; + public static final byte BIG_ENDIAN = 0x01; + public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index 82093450..54c2d032 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -34,6 +34,8 @@ public class KMUtils { 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec public static final byte[] leapYearMsec = { 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; + public static byte[] thirtyDaysMsec = { + 0, 0, 0, 0, (byte) 0x9A, (byte) 0x7E, (byte) 0xC8, 0}; //2592000000 msec public static final byte[] yearMsec = { 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 //Leap year(366) + 3 * 365 @@ -41,8 +43,8 @@ public class KMUtils { 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 public static final byte[] firstJan2020 = { 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00}; // 1577836800000 msec - public static final byte[] firstJan2051 = { - 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00}; // 2556144000000 + public static final byte[] firstJan2050 = { + 0, 0, 0x02, 0x4b, (byte) 0xCE, 0x5C, (byte)0xF0, 0x00}; //2524608000000 // msec public static final byte[] febMonthLeapMSec = { 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00}; //2505600000 @@ -54,6 +56,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, @@ -78,12 +82,12 @@ public static short convertToDate(short time, byte[] scratchPad, KMException.throwIt(KMError.INVALID_ARGUMENT); } if (utcFlag - && KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, + && KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8) >= 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); @@ -92,7 +96,7 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8); } else { from2020 = false; - Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, + Util.arrayCopyNonAtomic(firstJan2050, (short) 0, scratchPad, (short) 8, (short) 8); subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, @@ -103,10 +107,8 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient - // is - // multiple - // of 4 + // quotient is multiple of 4 + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); yrsCount = (short) (yrsCount * 4); // number of yrs. // copy reminder as new dividend Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, @@ -305,6 +307,14 @@ public static byte compare(byte[] buf, short lhs, short rhs) { return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); } + public static void shiftLeft(byte[] buf, short start, short count) { + short index = 0; + while (index < count) { + shiftLeft(buf, start); + index++; + } + } + public static void shiftLeft(byte[] buf, short start) { byte index = 7; byte carry = 0; @@ -419,4 +429,20 @@ public static void computeOnesCompliment(byte[] buf, short offset, short len) { index++; } } + + // 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) { + short index = 0; + short length = (short) SEC_TO_MILLIS_SHIFT_POS.length; + while (index < length) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + 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); + Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0); + index++; + } + } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index aaf93834..522f4edf 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -65,6 +65,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short SHARED_SECRET_KEY_SIZE = 32; public static final byte POWER_RESET_FALSE = (byte) 0xAA; public static final byte POWER_RESET_TRUE = (byte) 0x00; + private static final short COMPUTED_HMAC_KEY_SIZE = 32; private static KeyAgreement keyAgreement; @@ -117,6 +118,7 @@ public class KMAndroidSEProvider implements KMSEProvider { private KMECDeviceUniqueKey testKey; private KMECDeviceUniqueKey deviceUniqueKey; private KMHmacKey preSharedKey; + private KMHmacKey computedHmacKey; private byte[] additionalCertChain; private byte[] bcc; private boolean isProvisionLocked; @@ -132,12 +134,12 @@ public KMAndroidSEProvider() { // Re-usable AES,DES and HMAC keys in persisted memory. aesKeys = new AESKey[2]; aesKeys[KEYSIZE_128_OFFSET] = (AESKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false); aesKeys[KEYSIZE_256_OFFSET] = (AESKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false); + triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false); - hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 512, + hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false); rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); @@ -167,6 +169,8 @@ public KMAndroidSEProvider() { createAttestationKey(tmpArray, (short) 0, (short) 32); // Pre-shared secret key length is 32 bytes. createPresharedKey(tmpArray, (short) 0, (short) SHARED_SECRET_KEY_SIZE); + // Initialize the Computed Hmac Key object. + createComputedHmacKey(tmpArray, (short)0, (short) 32); } androidSEProvider = this; resetFlag = JCSystem.makeTransientByteArray((short) 1, @@ -593,13 +597,6 @@ public short hmacSign(HMACKey key, byte[] data, short dataStart, return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); } - public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, - short dataLength, byte[] mac, short macStart, short macLength) { - hmacSignature.init(key, Signature.MODE_VERIFY); - return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, - macLength); - } - @Override public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { @@ -623,12 +620,12 @@ public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, } @Override - public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, - byte[] data, short dataStart, short dataLength, byte[] mac, - short macStart, short macLength) { - HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacVerify(key, data, dataStart, dataLength, mac, macStart, - macLength); + public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, + macLength); } @Override @@ -739,7 +736,7 @@ public KMOperation createSymmetricCipher(short alg, short purpose, short macLeng } short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0); KMOperation operation = - poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength); + poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength, false); ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength); return operation; } @@ -751,11 +748,23 @@ public KMOperation createHmacSignerVerifier(short purpose, short digest, } KMOperation operation = poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, - KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); HMACKey key = createHMACKey(secret, secretStart, secretLength); ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); return operation; } + + private KMOperation createHmacSignerVerifier(short purpose, short digest, HMACKey key, boolean isTrustedConf) { + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + KMOperation operation = + poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, isTrustedConf); + + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } @Override public KMOperation initSymmetricOperation(byte purpose, byte alg, @@ -781,12 +790,18 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, return opr; } + @Override + public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { + KMHmacKey key = (KMHmacKey) computedHmacKey; + return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey(), true); + } + public KMOperation createRsaSigner(short digest, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); KMOperation operation = poolMgr.getOperationImpl(KMType.SIGN, alg, KMType.RSA, padding, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); @@ -799,7 +814,7 @@ public KMOperation createRsaDecipher(short padding, short mgfDigest, byte[] secr short modLength) { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest); KMOperation operation = poolMgr.getOperationImpl(KMType.DECRYPT, cipherAlg, KMType.RSA, padding, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); @@ -814,7 +829,7 @@ public KMOperation createEcSigner(short digest, byte[] secret, key.setS(secret, secretStart, secretLength); KMOperation operation = poolMgr .getOperationImpl(KMType.SIGN, alg, KMType.EC, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); return operation; } @@ -825,7 +840,7 @@ public KMOperation createKeyAgreement(byte[] secret, short secretStart, key.setS(secret, secretStart, secretLength); KMOperation operation = poolMgr .getOperationImpl(KMType.AGREE_KEY, KeyAgreement.ALG_EC_SVDP_DH_PLAIN, - KMType.EC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.EC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); return operation; } @@ -921,6 +936,8 @@ public short getCertificateChainLength() { @Override public void onSave(Element element) { element.write(certificateChain); + element.write(additionalCertChain); + element.write(bcc); KMAESKey.onSave(element, masterKey); KMECPrivateKey.onSave(element, attestationKey); KMHmacKey.onSave(element, preSharedKey); @@ -929,6 +946,8 @@ public void onSave(Element element) { @Override public void onRestore(Element element) { certificateChain = (byte[]) element.readObject(); + additionalCertChain = (byte[]) element.readObject(); + bcc = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); @@ -1001,6 +1020,20 @@ public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short len return (KMPreSharedKey) preSharedKey; } + @Override + public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (computedHmacKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), + false); + computedHmacKey = new KMHmacKey(key); + } + computedHmacKey.setKey(keyData, offset, length); + return (KMComputedHmacKey) computedHmacKey; + } + @Override public KMMasterKey getMasterKey() { return (KMMasterKey) masterKey; @@ -1286,7 +1319,7 @@ public void setBootPatchLevel(byte[] buffer, short start, short length) { if (length > 4 || length < 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) 4); + Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) length); } @Override @@ -1439,7 +1472,7 @@ public void persistAdditionalCertChain(byte[] buf, short offset, short len) { // // self-signed cert, leaf contains DK_pu b // ] // Certificate = COSE_Sign1 of a public key - if ((short) (len + 2) >= ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); @@ -1486,4 +1519,40 @@ public void setProvisionLocked(boolean locked) { public boolean isProvisionLocked() { return isProvisionLocked; } + + @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; + } + + @Override + public KMComputedHmacKey getComputedHmacKey() { + return computedHmacKey; + } + + private byte mapPurpose(short purpose) { + switch (purpose) { + case KMType.ENCRYPT: + return Cipher.MODE_ENCRYPT; + case KMType.DECRYPT: + return Cipher.MODE_DECRYPT; + case KMType.SIGN: + return Signature.MODE_SIGN; + case KMType.VERIFY: + return Signature.MODE_VERIFY; + } + return -1; + } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java new file mode 100644 index 00000000..3b0aa405 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java @@ -0,0 +1,5 @@ +package com.android.javacard.seprovider; + + +public interface KMComputedHmacKey { +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index 2eaeeeb9..a863bf88 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -19,7 +19,7 @@ import javacard.security.HMACKey; -public class KMHmacKey implements KMPreSharedKey { +public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { private HMACKey hmacKey; @@ -34,7 +34,11 @@ public void setKey(byte[] keyData, short kOff, short length) { public byte getKey(byte[] keyData, short kOff) { return hmacKey.getKey(keyData, kOff); } - + + public HMACKey getKey() { + return hmacKey; + } + public short getKeySizeBits() { return hmacKey.getSize(); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java index 2c447fd4..baa8a0f9 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -35,6 +35,7 @@ public class KMOperationImpl implements KMOperation { private static final short PURPOSE_OFFSET = 0x02; private static final short BLOCK_MODE_OFFSET = 0x03; private static final short MAC_LENGTH_OFFSET = 0x04; + private 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; @@ -340,6 +341,24 @@ public boolean verify(byte[] inputDataBuf, short inputDataStart, @Override public void abort() { + // Few simulators does not reset the Hmac signer instance on init so as + // a workaround to reset the hmac signer instance in case of abort/failure of the operation + // the corresponding sign / verify function is called. + if (operationInst[0] != null) { + if ((parameters[PURPOSE_OFFSET] == KMType.SIGN || parameters[PURPOSE_OFFSET] == KMType.VERIFY) && + (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { + Signature signer = (Signature) operationInst[0]; + try { + if (parameters[PURPOSE_OFFSET] == KMType.SIGN) { + signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); + } else { + signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0); + } + } catch(Exception e) { + // Ignore. + } + } + } reset(); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java index 9938cd59..e3baf610 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -14,6 +14,7 @@ * limitations under the License. */ package com.android.javacard.seprovider; +import javacard.framework.JCSystem; import javacard.security.KeyAgreement; import javacard.security.Signature; import javacardx.crypto.AEADCipher; @@ -25,6 +26,7 @@ public class KMPoolManager { public static final short MAX_OPERATION_INSTANCES = 4; + private static final short HMAC_MAX_OPERATION_INSTANCES = 8; // Cipher pool private Object[] cipherPool; // Signature pool @@ -33,6 +35,8 @@ public class KMPoolManager { private Object[] keyAgreementPool; // KMOperationImpl pool private Object[] operationPool; + // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. + private Object[] hmacSignOperationPool; final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, @@ -69,11 +73,14 @@ public static KMPoolManager getInstance() { private KMPoolManager() { cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; - signerPool = new Object[(short) (SIG_ALGS.length * 4)]; + // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. + signerPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * 4)]; operationPool = new Object[4]; + hmacSignOperationPool = new Object[4]; /* Initialize pools */ initializeOperationPool(); + initializeHmacSignOperationPool(); initializeSignerPool(); initializeCipherPool(); initializeKeyAgreementPool(); @@ -87,6 +94,14 @@ private void initializeOperationPool() { } } + private void initializeHmacSignOperationPool() { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + hmacSignOperationPool[index] = new KMOperationImpl(); + index++; + } + } + // Create a signature instance of each algorithm once. private void initializeSignerPool() { short index = 0; @@ -180,11 +195,17 @@ private Cipher getCipherInstance(byte alg) { * * @return instance of the available resource or null if no resource is available. */ - public KMOperation getResourceFromOperationPool() { + public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) { short index = 0; KMOperationImpl impl; - while (index < operationPool.length) { - impl = (KMOperationImpl) operationPool[index]; + Object[] oprPool; + if(isTrustedConfOpr) { + oprPool = hmacSignOperationPool; + } else { + oprPool = operationPool; + } + while (index < oprPool.length) { + impl = (KMOperationImpl) oprPool[index]; // Mode is always set. so compare using mode value. if (impl.getPurpose() == KMType.INVALID_VALUE) { return impl; @@ -216,7 +237,8 @@ private byte getAlgorithm(short purpose, Object object) { private boolean isResourceBusy(Object obj) { short index = 0; while (index < MAX_OPERATION_INSTANCES) { - if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj)) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj) + || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj)) { return true; } index++; @@ -255,19 +277,23 @@ private void reserveOperation(KMOperation operation, short purpose, short strong public KMOperation getOperationImpl(short purpose, short alg, short strongboxAlgType, short padding, - short blockMode, short macLength) { + short blockMode, short macLength, boolean isTrustedConfOpr) { KMOperation operation; // Throw exception if no resource from operation pool is available. - if (null == (operation = getResourceFromOperationPool())) { + if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); } // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose. Object[] pool = getCryptoPoolInstance(purpose); short index = 0; short usageCount = 0; + short maxOperations = MAX_OPERATION_INSTANCES; + if (Signature.ALG_HMAC_SHA_256 == alg) { + maxOperations = HMAC_MAX_OPERATION_INSTANCES; + } while (index < pool.length) { - if (usageCount >= MAX_OPERATION_INSTANCES) { + if (usageCount >= maxOperations) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); } if (pool[index] == null) { @@ -297,6 +323,7 @@ public void powerReset() { short index = 0; while (index < operationPool.length) { ((KMOperationImpl) operationPool[index]).abort(); + ((KMOperationImpl) hmacSignOperationPool[index]).abort(); index++; } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 0c3051d1..18932ee5 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -63,6 +63,14 @@ void createAsymmetricKey( short pubModMaxLength, short[] lengths); + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then * it should throw a CryptoException. @@ -301,9 +309,7 @@ short hmacKDF( * @return true if the signature matches. */ boolean hmacVerify( - byte[] keyBuf, - short keyStart, - short keyLength, + KMComputedHmacKey hmacKey, byte[] data, short dataStart, short dataLength, @@ -576,6 +582,16 @@ KMOperation initAsymmetricOperation( */ KMMasterKey createMasterKey(short keySizeBits); + /** + * This function creates an HMACKey and initializes the key with the provided input key data. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMComputedHmacKey. + */ + KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length); + /** * Returns the master key. * @@ -697,6 +713,26 @@ KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, */ byte[] getBootCertificateChain(); + /** + * Returns the computed Hmac key. + * + * @return Instance of the computed hmac key. + */ + KMComputedHmacKey getComputedHmacKey(); + + /** + * 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); + public boolean isProvisionLocked(); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java new file mode 100644 index 00000000..3eb4a2e4 --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -0,0 +1,23 @@ +/* + * 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" BASIS, + * 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; + +public class KMConfigurations { + // Machine types + public static final byte LITTLE_ENDIAN = 0x00; + public static final byte BIG_ENDIAN = 0x01; + public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 63c00331..a2c81488 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -45,9 +45,8 @@ public class KMJCardSimApplet extends KMKeymasterApplet { private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; private static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; - private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; - private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; - private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x60; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; + private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x40; private static final byte PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = (byte) 0x80; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -58,9 +57,6 @@ public class KMJCardSimApplet extends KMKeymasterApplet { KMJCardSimApplet() { super(new KMJCardSimulator()); - // setDummyBootParams(); - // setDummyPresharedKey(); - // setDummyAttestationIds(); } /** @@ -120,7 +116,6 @@ public void process(APDU apdu) { break; case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - provisionStatus |= PROVISION_STATUS_BOOT_PARAM; break; case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: processProvisionDeviceUniqueKey(apdu); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index 06c77ff6..3e9002e2 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -53,6 +53,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, @@ -102,10 +104,8 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient - // is - // multiple - // of 4 + // quotient is multiple of 4. + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); yrsCount = (short) (yrsCount * 4); // number of yrs. // copy reminder as new dividend Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, @@ -304,6 +304,14 @@ public static byte compare(byte[] buf, short lhs, short rhs) { return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); } + public static void shiftLeft(byte[] buf, short start, short count) { + short index = 0; + while (index < count) { + shiftLeft(buf, start); + index++; + } + } + public static void shiftLeft(byte[] buf, short start) { byte index = 7; byte carry = 0; @@ -418,4 +426,20 @@ public static void computeOnesCompliment(byte[] buf, short offset, short len) { index++; } } + + // 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) { + short index = 0; + short length = (short) SEC_TO_MILLIS_SHIFT_POS.length; + while (index < length) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + 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); + Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0); + index++; + } + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java new file mode 100644 index 00000000..3b0aa405 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java @@ -0,0 +1,5 @@ +package com.android.javacard.seprovider; + + +public interface KMComputedHmacKey { +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index 8473f1bf..735789b6 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -36,4 +36,8 @@ public byte getKey(byte[] keyData, short kOff) { public short getKeySizeBits() { return hmacKey.getSize(); } + + public HMACKey getKey() { + return hmacKey; + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index 1d0c426e..ed41afe1 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -39,6 +39,7 @@ import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; import javacard.security.KeyAgreement; import javacard.security.RandomData; @@ -75,6 +76,7 @@ public class KMJCardSimulator implements KMSEProvider { public static final byte POWER_RESET_FALSE = (byte)0xAA; public static final byte POWER_RESET_TRUE = (byte)0x00; public static final byte AES_BLOCK_SIZE = 16; + private static final short COMPUTED_HMAC_KEY_SIZE = 32; public static byte[] resetFlag; public static boolean jcardSim = false; @@ -95,6 +97,7 @@ public class KMJCardSimulator implements KMSEProvider { private KMECDeviceUniqueKey testKey; private KMECDeviceUniqueKey deviceUniqueKey; private KMHmacKey preSharedKey; + private KMHmacKey computedHmacKey; private boolean deviceReboot; // Data - originally was in repository @@ -571,12 +574,6 @@ public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLengt return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); } - public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, - byte[] mac, short macStart, short macLength) { - hmacSignature.init(key, Signature.MODE_VERIFY); - return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); - } - @Override public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, short dataLength, byte[] signature, short signatureStart) { @@ -651,10 +648,11 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] dat } @Override - public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, - short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacVerify(key, data, dataStart, dataLength, mac, macStart, macLength); + public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); } @Override @@ -697,6 +695,14 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, b return null; } + @Override + public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { + KMOperationImpl opr = null; + KMHmacKey key = (KMHmacKey) computedHmacKey; + Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); + return new KMOperationImpl(signerVerifier); + } + @Override public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, @@ -1120,6 +1126,15 @@ public Signature createHmacSignerVerifier(short purpose, short digest, byte[] se return hmacSignerVerifier; } + private Signature createHmacSignerVerifier(short purpose, short digest, HMACKey key) { + byte alg = Signature.ALG_HMAC_SHA_256; + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Signature hmacSignerVerifier = Signature.getInstance((byte) alg, false); + hmacSignerVerifier.init(key, (byte) purpose); + return hmacSignerVerifier; + } public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, short secretStart, short secretLength, @@ -1466,6 +1481,20 @@ public boolean isUpgrading() { return false; } + @Override + public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (computedHmacKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), + false); + computedHmacKey = new KMHmacKey(key); + } + computedHmacKey.setKey(keyData, offset, length); + return (KMComputedHmacKey) computedHmacKey; + } + @Override public KMMasterKey createMasterKey(short keySizeBits) { if (masterKey == null) { @@ -1760,5 +1789,24 @@ public void setProvisionLocked(boolean locked) { public boolean isProvisionLocked() { return isProvisionLocked; } + + @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; + } + + @Override + public KMComputedHmacKey getComputedHmacKey() { + return (KMComputedHmacKey) computedHmacKey; + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index d23d7f15..e493f3aa 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -63,6 +63,14 @@ void createAsymmetricKey( short pubModMaxLength, short[] lengths); + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then * it should throw a CryptoException. @@ -289,9 +297,7 @@ short hmacKDF( /** * This is a oneshot operation that verifies the signature using hmac algorithm. * - * @param keyBuf is the buffer with hmac key. - * @param keyStart is the start of the buffer. - * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param hmacKey is the computed hmac key. * @param data is the buffer containing data. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -301,9 +307,7 @@ short hmacKDF( * @return true if the signature matches. */ boolean hmacVerify( - byte[] keyBuf, - short keyStart, - short keyLength, + KMComputedHmacKey hmacKey, byte[] data, short dataStart, short dataLength, @@ -555,6 +559,16 @@ KMOperation initAsymmetricOperation( */ boolean isUpgrading(); + /** + * This function creates an HMACKey and initializes the key with the provided input key data. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMComputedHmacKey. + */ + KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length); + /** * This function generates an AES Key of keySizeBits, which is used as an master key. This * generated key is maintained by the SEProvider. This function should be called only once at the @@ -685,6 +699,26 @@ KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, * @return boot certificate chain. */ byte[] getBootCertificateChain(); + + /** + * Returns the computed Hmac key. + * + * @return Instance of the computed hmac key. + */ + KMComputedHmacKey getComputedHmacKey(); + + /** + * 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); public boolean isProvisionLocked(); diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index c1540fa7..6fa4098e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -137,8 +137,18 @@ public short length() { return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2)); } + public short setLength(short len) { + return Util.setShort(heap, + (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), len); + } + public byte[] getBuffer() { return heap; } + public void deleteLastEntry() { + short len = length(); + Util.setShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), + (short) (len - 1)); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index e68ecaa7..4cacce96 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -345,6 +345,7 @@ private short decodeKeyParam(short exp) { short tagClass; short allowedType; short obj; + short arrPos = 0; // For each tag in payload ... while (index < payloadLength) { tagFound = false; @@ -360,13 +361,14 @@ private short decodeKeyParam(short exp) { try { tagFound = true; obj = decode(tagClass); - KMArray.cast(vals).add(index, obj); + KMArray.cast(vals).add(arrPos++, obj); break; }catch(KMException e){ if(KMException.reason() == KMError.INVALID_TAG && !ignoreInvalidTags){ KMException.throwIt(KMError.INVALID_TAG); } + break; } } tagInd++; @@ -377,6 +379,7 @@ private short decodeKeyParam(short exp) { index++; } } + KMArray.cast(vals).setLength(arrPos); return KMKeyParameters.instance(vals); } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 447b1b06..75029326 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -103,10 +103,9 @@ public static void create() { enums = new Object[]{ new byte[]{RSA, DES, EC, AES, HMAC}, - new byte[]{P_224, P_256, P_384, P_521}, + new byte[]{P_224, P_256, P_384, P_521, CURVE_25519}, new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte) (PASSWORD & FINGERPRINT), - ANY}, + new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY}, new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 0c5db5b5..52398824 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -59,6 +59,7 @@ public class KMError { public static final short INVALID_NONCE = 52; public static final short MISSING_MAC_LENGTH = 53; public static final short CALLER_NONCE_PROHIBITED = 55; + public static final short KEY_MAX_OPS_EXCEEDED = 56; public static final short INVALID_MAC_LENGTH = 57; public static final short MISSING_MIN_MAC_LENGTH = 58; public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; @@ -70,6 +71,7 @@ public class KMError { public static final short CANNOT_ATTEST_IDS = 66; public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + public static final short NO_USER_CONFIRMATION = 71; public static final short DEVICE_LOCKED = 72; public static final short EARLY_BOOT_ENDED = 73; public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index 8ae8c8c9..24905f01 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -148,6 +148,15 @@ public short value(byte[] dest, short destOff) { return length(); } + public short toLittleEndian(byte[] dest, short destOff) { + short index = (short) (length() - 1); + while (index >= 0) { + dest[destOff++] = heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + index)]; + index--; + } + return length(); + } + public short getShort() { return Util.getShort(heap, (short) (getStartOff() + 2)); } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 6f7cc2ff..e62ce9fe 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -46,7 +46,7 @@ public static short exp(short tagType) { if (!validateTagType(tagType)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - short arrPtr = KMArray.exp(KMType.INTEGER_TYPE); + short arrPtr = KMArray.exp(KMInteger.exp()); short ptr = instance(TAG_TYPE, (short) 6); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG); @@ -150,4 +150,14 @@ public static boolean contains(short tagId, short tagValue, short params) { return false; } + public boolean contains(short tagValue) { + short index = 0; + while (index < length()) { + if (KMInteger.compare(tagValue, get(index)) == 0) { + return true; + } + index++; + } + return false; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 2e22f183..d370793e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -53,6 +53,8 @@ public class KMIntegerTag extends KMTag { CERTIFICATE_NOT_BEFORE, CERTIFICATE_NOT_AFTER, USAGE_COUNT_LIMIT, + // custom tag + AUTH_TIMEOUT_MILLIS, }; private KMIntegerTag() { @@ -193,7 +195,7 @@ public boolean isValidKeySize(byte alg) { } break; case KMType.DES: - if (val == 192 || val == 168) { + if (val == 168) { return true; } break; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 21dd7f5c..d4cbc323 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -30,6 +30,10 @@ public class KMKeyParameters extends KMType { private static KMKeyParameters prototype; + private static final short[] customTags = { + KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + }; + private KMKeyParameters() { } @@ -130,11 +134,8 @@ public short findTag(short tagType, short tagKey) { public static boolean hasUnsupportedTags(short keyParamsPtr) { final short[] tagArr = { // Unsupported tags. - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, - KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS, - KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS }; byte index = 0; short tagInd; @@ -185,6 +186,9 @@ public static short makeSbEnforced(short keyParamsPtr, byte origin, KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, }; byte index = 0; short tagInd; @@ -259,6 +263,8 @@ public static short makeSbEnforced(short keyParamsPtr, byte[] scratchPad) { KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, }; byte index = 0; short tagInd; @@ -324,7 +330,8 @@ public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, KMType.UINT_TAG, KMType.USERID, KMType.DATE_TAG, KMType.CREATION_DATETIME, - KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT + KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY }; byte index = 0; short tagInd; @@ -389,6 +396,9 @@ public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) { } index++; } + // Add custom tags at the end of the array. So it becomes easy to + // delete them when sending key characteristics back to HAL. + arrInd = addCustomTags(keyParamsPtr, scratchPad, arrInd); return createKeyParameters(scratchPad, (short) (arrInd / 2)); } @@ -431,7 +441,6 @@ public static boolean isValidTag(short tagType, short tagKey) { KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, KMType.BYTES_TAG, KMType.UNIQUE_ID, KMType.UINT_TAG, KMType.MAC_LENGTH, - KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY }; short index = 0; if (tagKey == KMType.INVALID_TAG) { @@ -457,4 +466,56 @@ public static short createKeyParameters(byte[] ptrArr, short len) { } return KMKeyParameters.instance(arrPtr); } + + public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { + short index = 0; + short tagPtr; + short len = (short) customTags.length; + short tagType; + while (index < len) { + tagType = customTags[(short) (index + 1)]; + switch(tagType) { + case KMType.AUTH_TIMEOUT_MILLIS: + short authTimeOutTag = + KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT); + if (authTimeOutTag != KMType.INVALID_VALUE) { + tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset); + Util.setShort(scratchPad, offset, tagPtr); + offset += 2; + } + break; + default: + break; + } + index += 2; + } + return offset; + } + + public void deleteCustomTags() { + short arrPtr = getVals(); + short index = (short) (customTags.length - 1); + short obj; + while (index >= 0) { + obj = findTag(customTags[(short) (index - 1)], customTags[index]); + if (obj != KMType.INVALID_VALUE) { + KMArray.cast(arrPtr).deleteLastEntry(); + } + index -= 2; + } + } + + public static short createAuthTimeOutMillisTag(short authTimeOutTag, byte[] scratchPad, short offset) { + short authTime = KMIntegerTag.cast(authTimeOutTag).getValue(); + Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0); + Util.arrayCopyNonAtomic( + KMInteger.cast(authTime).getBuffer(), + KMInteger.cast(authTime).getStartOff(), + scratchPad, + (short) (offset + 8 - KMInteger.cast(authTime).length()), + KMInteger.cast(authTime).length()); + KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16)); + return KMIntegerTag.instance(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + KMInteger.uint_64(scratchPad, (short) (offset + 8))); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b8483275..295d6e96 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -19,6 +19,7 @@ import com.android.javacard.seprovider.KMAttestationCert; import com.android.javacard.seprovider.KMDeviceUniqueKey; import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMHmacKey; import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.APDU; import javacard.framework.Applet; @@ -28,6 +29,8 @@ import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; +import javacard.security.HMACKey; +import javacard.security.KeyBuilder; import javacardx.apdu.ExtendedLength; /** @@ -53,6 +56,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static final short KM_HAL_VERSION = (short) 0x5000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; + public static final byte TRUSTED_ENVIRONMENT = 1; // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys private static final byte[] defaultSubject = { @@ -97,6 +101,12 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x6E }; + // "confirmation token" + public static final byte[] confirmationToken = { + 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, + 0x6B, + 0x65, 0x6E + }; public static final short MAX_COSE_BUF_SIZE = (short) 1024; // Top 32 commands are reserved for provisioning. @@ -181,6 +191,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte PLAIN_SECRET = 33; public static final byte TEE_PARAMETERS = 34; public static final byte SB_PARAMETERS = 35; + public static final byte CONFIRMATION_TOKEN = 36; // Constant // AddRngEntropy @@ -259,6 +270,7 @@ private KMOperationState reserveOperation(short algorithm, short opHandle){ short index = 0; while(index < MAX_OPERATIONS_COUNT) { if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) { + opTable[index].reset(); opTable[index].setAlgorithm(algorithm); opTable[index].setHandle(KMInteger.cast(opHandle).getBuffer(), KMInteger.cast(opHandle).getStartOff(), @@ -319,7 +331,7 @@ public void uninstall() { repository.onUninstall(); } - private short mapISOErrorToKMError(short reason) { + protected short mapISOErrorToKMError(short reason) { switch (reason) { case ISO7816.SW_CLA_NOT_SUPPORTED: return KMError.UNSUPPORTED_CLA; @@ -341,7 +353,7 @@ private short mapISOErrorToKMError(short reason) { } } - private short mapCryptoErrorToKMError(short reason) { + protected short mapCryptoErrorToKMError(short reason) { switch (reason) { case CryptoException.ILLEGAL_USE: return KMError.CRYPTO_ILLEGAL_USE; @@ -386,6 +398,7 @@ protected short validateApduHeader(APDU apdu) { @Override public void process(APDU apdu) { try { + resetData(); repository.onProcess(); // If this is select applet apdu which is selecting this applet then return if (apdu.isISOInterindustryCLA()) { @@ -493,12 +506,10 @@ public void process(APDU apdu) { resetWrappingKey(); sendError(apdu, mapCryptoErrorToKMError(e.getReason())); } catch (Exception e) { - sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); freeOperations(); resetWrappingKey(); sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); } finally { - resetData(); repository.clean(); } } @@ -519,15 +530,16 @@ private void freeOperations() { } private void processEarlyBootEndedCmd(APDU apdu) { - KMException.throwIt(KMError.UNIMPLEMENTED); + repository.setEarlyBootEndedStatus(true); } private short deviceLockedCmd(APDU apdu){ short cmd = KMArray.instance((short) 2); + short ptr = KMVerificationToken.exp(); // passwordOnly KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // verification token - KMArray.cast(cmd).add((short) 1, KMVerificationToken.exp()); + KMArray.cast(cmd).add((short) 1, ptr); return receiveIncoming(apdu, cmd); } @@ -699,6 +711,11 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { parseEncryptedKeyBlob(data[KEY_BLOB],data[APP_ID], data[APP_DATA], scratchPad); // Check Version and Patch Level checkVersionAndPatchLevel(scratchPad); + // Remove custom tags from key characteristics + short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); + if(teeParams != KMType.INVALID_VALUE) { + KMKeyParameters.cast(teeParams).deleteCustomTags(); + } // make response. short resp = KMArray.instance((short) 2); KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -865,9 +882,9 @@ private void processComputeSharedHmacCmd(APDU apdu) { bufferIndex, scratchPad, (short) 0); - // persist the computed hmac key. - repository.initComputedHmac(scratchPad, (short) 0, keyLen); + // persist the computed hmac key. + seProvider.createComputedHmacKey(scratchPad, (short) 0, keyLen); // Generate sharingKey verification signature and store that in scratch pad. //tmpVariables[5] short signLen = @@ -898,6 +915,31 @@ private short upgradeKeyCmd(APDU apdu){ return receiveIncoming(apdu, cmd); } + private boolean isKeyUpgradeRequired(short tag, short systemParam) { + // validate the tag and check if key needs upgrade. + short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); + tagValue = KMIntegerTag.cast(tagValue).getValue(); + short zero = KMInteger.uint_8((byte) 0); + if (tagValue != KMType.INVALID_VALUE) { + // OS version in key characteristics must be less the OS version stored in Javacard or the + // stored version must be zero. Then only upgrade is allowed else it is invalid argument. + if ((tag == KMType.OS_VERSION + && KMInteger.compare(tagValue, systemParam) == 1 + && KMInteger.compare(systemParam, zero) == 0)) { + // Key needs upgrade. + return true; + } else if ((KMInteger.compare(tagValue, systemParam) == -1)) { + // Each os version or patch level associated with the key must be less than it's + // corresponding value stored in Javacard, then only upgrade is allowed otherwise it + // is invalid argument. + return true; + } else if (KMInteger.compare(tagValue, systemParam) == 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + return false; + } + private void processUpgradeKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. short cmd = upgradeKeyCmd(apdu); @@ -906,7 +948,7 @@ private void processUpgradeKeyCmd(APDU apdu) { data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); //tmpVariables[0] - short appId = + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); if (appId != KMTag.INVALID_VALUE) { data[APP_ID] = KMByteTag.cast(appId).getValue(); @@ -918,64 +960,18 @@ private void processUpgradeKeyCmd(APDU apdu) { } // parse existing key blob parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); - // validate characteristics to be upgraded. - short osVersion = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - short osPatchLvl = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - osPatchLvl = KMIntegerTag.cast(osPatchLvl).getValue(); - short provOsVersion = repository.getOsVersion(); - short provOsPatch = repository.getOsPatch(); - short zero = KMInteger.uint_8((byte) 0); - short error = KMError.OK; - if (osVersion != KMType.INVALID_VALUE) { - // os version in key characteristics must be less the os version stored in javacard or the - // stored version must be zero. Then only upgrade is allowed else it is invalid argument. - if (KMInteger.compare(osVersion, provOsVersion) != -1 - && KMInteger.compare(provOsVersion, zero) != 0) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - if (osPatchLvl != KMType.INVALID_VALUE) { - // The key characteristics should have had os patch level < os patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(osPatchLvl, provOsPatch) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - - //Compare vendor patch levels - short vendorPatchLvl = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, data[HW_PARAMETERS]); - vendorPatchLvl = KMIntegerTag.cast(vendorPatchLvl).getValue(); - short provVendorPatch = repository.getVendorPatchLevel(); - if (vendorPatchLvl != KMType.INVALID_VALUE) { - // The key characteristics should have had vendor patch level < vendor patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(vendorPatchLvl, provVendorPatch) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - - //Compare boot patch levels - short bootPatch = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, data[HW_PARAMETERS]); - bootPatch = KMIntegerTag.cast(bootPatch).getValue(); - short provBootPatch = getBootPatchLevel(scratchPad); - if (bootPatch != KMType.INVALID_VALUE) { - // The key characteristics should have had boot patch level < boot patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(bootPatch, provBootPatch) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - - if (error != KMError.INVALID_ARGUMENT) { + boolean isKeyUpgradeRequired = false; + // Check if key requires upgrade. + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); + isKeyUpgradeRequired |= + isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); + // Get boot patch level. + seProvider.getBootPatchLevel(scratchPad, (short) 0); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, + KMInteger.uint_32(scratchPad, (short) 0)); + + if (isKeyUpgradeRequired) { // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); makeKeyCharacteristics(scratchPad); @@ -1558,7 +1554,7 @@ private KMAttestationCert makeSelfSignedCert(short attPrivKey, short attPubKey, protected short getBootKey(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_KEY_SIZE, (byte)0); - short len = seProvider.getVerifiedBootHash(scratchPad,(short)0); + short len = seProvider.getBootKey(scratchPad, (short) 0); if(len != VERIFIED_BOOT_KEY_SIZE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -1567,7 +1563,7 @@ protected short getBootKey(byte[] scratchPad){ protected short getVerifiedBootHash(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_HASH_SIZE, (byte)0); - short len = seProvider.getVerifiedBootHash(scratchPad,(short)0); + short len = seProvider.getVerifiedBootHash(scratchPad, (short) 0); if(len != VERIFIED_BOOT_HASH_SIZE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -1691,7 +1687,7 @@ private void processAbortOperationCmd(APDU apdu) { } private short finishOperationCmd(APDU apdu){ - short cmd = KMArray.instance((short) 5); + short cmd = KMArray.instance((short) 6); KMArray.cast(cmd).add((short) 0, KMInteger.exp());//op handle KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());// input data KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // signature @@ -1699,6 +1695,7 @@ private short finishOperationCmd(APDU apdu){ KMArray.cast(cmd).add((short) 3, authToken); // auth token short verToken = KMVerificationToken.exp(); KMArray.cast(cmd).add((short) 4, verToken); // time stamp token + KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); //confirmation token return receiveIncoming(apdu, cmd); } @@ -1710,6 +1707,7 @@ private void processFinishOperationCmd(APDU apdu) { data[SIGNATURE] = KMArray.cast(cmd).get((short) 2); data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3); data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 4); + data[CONFIRMATION_TOKEN] = KMArray.cast(cmd).get((short) 5); // Check Operation Handle KMOperationState op = findOperation(data[OP_HANDLE]); if (op == null) { @@ -1719,6 +1717,7 @@ private void processFinishOperationCmd(APDU apdu) { authorizeUpdateFinishOperation(op, scratchPad); switch (op.getPurpose()) { case KMType.SIGN: + finishTrustedConfirmationOperation(op); case KMType.VERIFY: finishSigningVerifyingOperation(op, scratchPad); break; @@ -1770,10 +1769,6 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { break; case KMType.AES: case KMType.DES: - if(op.getBlockMode() == KMType.GCM && (len < (short) (op.getMacLength() / 8))){ - // Check if there is at least MAC Length bytes of input data - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } finishAesDesOperation(op); break; } @@ -1786,13 +1781,6 @@ private void finishAesDesOperation(KMOperationState op){ blockSize= DES_BLOCK_SIZE; } - // If no padding then data length must be block aligned - if(op.getPadding() == KMType.PADDING_NONE && op.getPurpose() == KMType.ENCRYPT - && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) - && ((short) (len % blockSize) != 0)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - if(op.getPurpose() == KMType.DECRYPT && len > 0 && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) && ((short) (len % blockSize) != 0)){ @@ -1800,26 +1788,32 @@ private void finishAesDesOperation(KMOperationState op){ } if (op.getBlockMode() == KMType.GCM) { + if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } if(op.isAesGcmUpdateAllowed()){ op.setAesGcmUpdateComplete(); } - //Get output size as it can be more then input size in case of GCM + // Get the output size len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); } // If padding i.e. pkcs7 then add padding to right // Output data can at most one block size more the input data in case of pkcs7 encryption // In case of gcm we will allocate extra memory of the size equal to blocksize. - short outData = KMByteBlob.instance((short) (len + blockSize)); - len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(outData).getBuffer(), - KMByteBlob.cast(outData).getStartOff()); - KMByteBlob.cast(outData).setLength(len); - data[OUTPUT_DATA] = outData; + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); + try { + len = op.getOperation().finish( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + } catch (CryptoException e) { + if (e.getReason() == CryptoException.ILLEGAL_USE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len); } private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) { @@ -1938,26 +1932,77 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratch private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) { // If one time user Authentication is required if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { - validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); - short authTime = op.getAuthTime(); - short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - if (verTime == KMType.INVALID_VALUE) { + // Validate Verification Token. + validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); + // validate operation handle. + short ptr = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getChallenge(); + if (KMInteger.compare(ptr, op.getHandle()) != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + tmpVariables[0] = op.getAuthTime(); + tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + if (tmpVariables[2] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.VERIFICATION_FAILED); } - if (KMInteger.compare(authTime, verTime) < 0) { + if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } op.setAuthTimeoutValidated(true); } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required - short challenge = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); - if (KMInteger.compare(data[OP_HANDLE], challenge) != 0) { + tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); + if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authenticateUser(); } } - private void authorizeDeviceUnlock(short hwToken) { + private void authorizeKeyUsageForCount(byte[] scratchPad) { + short scratchPadOff = 0; + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 12, (byte) 0); + + short usageLimitBufLen = KMIntegerTag.getValue(scratchPad, scratchPadOff, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); + + if (usageLimitBufLen == KMType.INVALID_VALUE) { + return; + } + + if (usageLimitBufLen > 4) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + if (repository.isAuthTagPersisted(data[AUTH_TAG])) { + // Get current counter, update and increment it. + short len = repository + .getRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4)); + if (len != 4) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (0 >= KMInteger.unsignedByteArrayCompare(scratchPad, scratchPadOff, scratchPad, + (short) (scratchPadOff + 4), (short) 4)) { + KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); + } + // Increment the counter. + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0); + Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1); + KMUtils.add(scratchPad, scratchPadOff, (short) (scratchPadOff + len), + (short) (scratchPadOff + len * 2)); + + repository + .setRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), + len); + } else { + // Persist auth tag. + if (!repository.persistAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + } + } + + private void authorizeDeviceUnlock(byte[] scratchPad) { // If device is locked and key characteristics requires unlocked device then check whether // HW auth token has correct timestamp. short ptr = @@ -1965,11 +2010,11 @@ private void authorizeDeviceUnlock(short hwToken) { KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]); if (ptr != KMType.INVALID_VALUE && repository.getDeviceLock()) { - if (hwToken == KMType.INVALID_VALUE) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { KMException.throwIt(KMError.DEVICE_LOCKED); } - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - // Check if the current auth time stamp is greater then device locked time stamp + ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(); + // Check if the current auth time stamp is greater than device locked time stamp short ts = repository.getDeviceTimeStamp(); if (KMInteger.compare(ptr, ts) <= 0) { KMException.throwIt(KMError.DEVICE_LOCKED); @@ -1977,7 +2022,7 @@ private void authorizeDeviceUnlock(short hwToken) { // Now check if the device unlock requires password only authentication and whether // auth token is generated through password authentication or not. if (repository.getDeviceLockPasswordOnly()) { - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); ptr = KMEnum.cast(ptr).getVal(); if (((byte) ptr & KMType.PASSWORD) == 0) { KMException.throwIt(KMError.DEVICE_LOCKED); @@ -1990,37 +2035,16 @@ private void authorizeDeviceUnlock(short hwToken) { } } - private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { - // CBOR Encoding is always big endian and Java is big endian - short ptr = KMVerificationToken.cast(verToken).getMac(); - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return; - } - validateVerificationToken(verToken, scratchPad); - // validate operation handle. - ptr = KMVerificationToken.cast(verToken).getChallenge(); - if (op.getHandle() != KMInteger.cast(ptr).getShort()) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - } - - private void validateVerificationToken(short verToken, byte[] scratchPad) { - short ptr = KMVerificationToken.cast(verToken).getMac(); - short len; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return; - } - // concatenation length will be 37 + length of verified parameters list - which is typically - // empty + private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) { + // concatenation length will be 37 + length of verified parameters list - which + // is typically 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); - len = (short) authVerification.length; + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, + (short) authVerification.length); + short len = (short) authVerification.length; // concatenate challenge - 8 bytes - ptr = KMVerificationToken.cast(verToken).getChallenge(); + short ptr = KMVerificationToken.cast(verToken).getChallenge(); KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; @@ -2029,22 +2053,30 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; + // concatenate security level - 4 bytes + scratchPad[(short) (len + 3)] = TRUSTED_ENVIRONMENT; + len += 4; // hmac the data ptr = KMVerificationToken.cast(verToken).getMac(); - short key = repository.getComputedHmacKey(); - boolean verified = - seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - if (!verified) { + return seProvider.hmacVerify( + seProvider.getComputedHmacKey(), + scratchPad, + (short) 0, + len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + } + + private void validateVerificationToken(short verToken, byte[] scratchPad) { + short ptr = KMVerificationToken.cast(verToken).getMac(); + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (!verifyVerificationTokenMacInBigEndian(verToken, scratchPad)) { + // Throw Exception if none of the combination works. KMException.throwIt(KMError.VERIFICATION_FAILED); } } @@ -2084,13 +2116,15 @@ private void processUpdateOperationCmd(APDU apdu) { authorizeUpdateFinishOperation(op, scratchPad); if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); // update the data. op.getOperation() .update( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length()); + // update trusted confirmation operation + updateTrustedConfirmationOperation(op); + data[OUTPUT_DATA] = KMType.INVALID_VALUE; } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { // Update for encrypt/decrypt using RSA will not be supported because to do this op state @@ -2099,28 +2133,21 @@ private void processUpdateOperationCmd(APDU apdu) { KMException.throwIt(KMError.OPERATION_CANCELLED); } short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - short additionalExpOutLen = 0; + short blockSize = DES_BLOCK_SIZE; if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.GCM || op.getBlockMode() == KMType.CTR) { - if(op.isAesGcmUpdateAllowed()){ - op.setAesGcmUpdateComplete(); - } - additionalExpOutLen = 16; - } else { - // input data must be block aligned. - // 128 bit block size - HAL must send block aligned data - if (len % AES_BLOCK_SIZE != 0 || len <= 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + blockSize = AES_BLOCK_SIZE; + if (op.getBlockMode() == KMType.GCM) { + // if input data present + if (len > 0) { + // no more future updateAAD allowed if input data present. + if (op.isAesGcmUpdateAllowed()) { + op.setAesGcmUpdateComplete(); + } + } } - } - } else if (op.getAlgorithm() == KMType.DES) { - // 64 bit block size - HAL must send block aligned data - if (len % DES_BLOCK_SIZE != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } } // Allocate output buffer as input data is already block aligned - data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + additionalExpOutLen)); + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); // Otherwise just update the data. // HAL consumes all the input and maintains a buffered data inside it. So the // applet sends the inputConsumed length as same as the input length. @@ -2139,16 +2166,7 @@ private void processUpdateOperationCmd(APDU apdu) { // Adjust the Output data if it is not equal to input data. // This happens in case of JCardSim provider. - if (len != KMByteBlob.cast(data[OUTPUT_DATA]).length()) { - data[INPUT_DATA] = data[OUTPUT_DATA]; - data[OUTPUT_DATA] = KMByteBlob.instance(len); - Util.arrayCopy( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), - len); - } + KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len); } if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { @@ -2255,7 +2273,6 @@ private void processBeginOperationCmd(APDU apdu) { } // Parse the encrypted blob and decrypt it. parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); - KMTag.assertAbsence(data[SB_PARAMETERS],KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); KMTag.assertPresence(data[SB_PARAMETERS],KMType.ENUM_TAG,KMType.ALGORITHM,KMError.UNSUPPORTED_ALGORITHM); short algorithm = KMEnumTag.getValue(KMType.ALGORITHM,data[SB_PARAMETERS]); // If Blob usage tag is present in key characteristics then it should be standalone. @@ -2286,6 +2303,7 @@ private void processBeginOperationCmd(APDU apdu) { authorizeAndBeginOperation(op, scratchPad); switch (op.getPurpose()) { case KMType.SIGN: + beginTrustedConfirmationOperation(op); case KMType.VERIFY: beginSignVerifyOperation(op); break; @@ -2428,7 +2446,7 @@ private void authorizePadding(KMOperationState op) { KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); if (param != KMType.INVALID_VALUE) { if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); } param = KMEnumArrayTag.cast(param).get((short) 0); if (!KMEnumArrayTag.cast(paddings).contains(param)) { @@ -2525,9 +2543,6 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { default: KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); } - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } if (param == KMType.GCM) { if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) { KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); @@ -2577,10 +2592,8 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { < KMIntegerTag.getShortValue( KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } else if (macLen - > KMIntegerTag.getShortValue( - KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } else if (macLen % 8 != 0 || macLen > 256) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); } op.setMacLength(macLen); } @@ -2596,11 +2609,23 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizeDigest(op); authorizePadding(op); authorizeBlockModeAndMacLength(op); - if (!validateHwToken(data[HW_TOKEN], scratchPad)) { - data[HW_TOKEN] = KMType.INVALID_VALUE; - } - authorizeUserSecureIdAuthTimeout(op); - authorizeDeviceUnlock(data[HW_TOKEN]); + authorizeUserSecureIdAuthTimeout(op, scratchPad); + authorizeDeviceUnlock(scratchPad); + authorizeKeyUsageForCount(scratchPad); + + //Validate early boot + //VTS expects error code EARLY_BOOT_ONLY during begin operation if eary boot ended tag is present + if (repository.getEarlyBootEndedStatus()) { + KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMError.EARLY_BOOT_ENDED); + } + + //Validate bootloader only + if (repository.getBootEndedStatus()) { + KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMError.INVALID_KEY_BLOB); + } + // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in // key params then fail if it is not a Decrypt operation data[IV] = KMType.INVALID_VALUE; @@ -2736,6 +2761,22 @@ private void beginCipherOperation(KMOperationState op) { } } + private void beginTrustedConfirmationOperation(KMOperationState op) { + // Check for trusted confirmation - if required then set the signer in op state. + if (KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + data[HW_PARAMETERS]) != KMType.INVALID_VALUE) { + + op.setTrustedConfirmationSigner( + seProvider.initTrustedConfirmationSymmetricOperation(seProvider.getComputedHmacKey())); + + op.getTrustedConfirmationSigner().update( + confirmationToken, + (short) 0, + (short) confirmationToken.length); + } + + } + private void beginSignVerifyOperation(KMOperationState op) { switch (op.getAlgorithm()) { case KMType.RSA: @@ -2822,70 +2863,104 @@ private void beginSignVerifyOperation(KMOperationState op) { } } - private void authorizeUserSecureIdAuthTimeout(KMOperationState op) { + private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, + short secureUserIdsObj) { + short secureUserId = KMHardwareAuthToken.cast(hwAuthToken).getUserId(); + if (!KMInteger.cast(secureUserId).isZero()) { + if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(secureUserId)) + return true; + } + + short authenticatorId = KMHardwareAuthToken.cast(hwAuthToken).getAuthenticatorId(); + if (!KMInteger.cast(authenticatorId).isZero()) { + if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(authenticatorId)) + return true; + } + return false; + } + + private boolean authTokenMatches(short userSecureIdsPtr, short authType, + byte[] scratchPad) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { + return false; + } + if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { + return false; + } + // check auth type + tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); + tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); + if (((byte) tmpVariables[2] & (byte) authType) == 0) { + return false; + } + return true; + } + + private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { short authTime; + short authType; // Authorize User Secure Id and Auth timeout - if (KMTag.isPresent(data[HW_PARAMETERS], KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID)) { - authTime = + short userSecureIdPtr = + KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); + if (userSecureIdPtr != KMType.INVALID_VALUE) { + // Authentication required. + if (KMType.INVALID_VALUE != + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, data[HW_PARAMETERS])) { + // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // authenticator type must be provided. + if(KMType.INVALID_VALUE == + (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) { + // Authentication required, but no auth type found. + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + + short authTimeoutTagPtr = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); - if (authTime != KMType.INVALID_VALUE) { - // check if hw token is empty - mac should not be empty. - if (data[HW_TOKEN] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - authTime = KMIntegerTag.cast(authTime).getValue(); + if (authTimeoutTagPtr != KMType.INVALID_VALUE) { // authenticate user - authenticateUser(); + if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + + authTimeoutTagPtr = + KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[HW_PARAMETERS]); + if (authTimeoutTagPtr == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + authTime = KMIntegerTag.cast(authTimeoutTagPtr).getValue(); // set the one time auth op.setOneTimeAuthReqd(true); // set the authentication time stamp in operation state - authTime = addIntegers(authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp()); + authTime = + addIntegers(authTime, + KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad); op.setAuthTime( KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff()); // auth time validation will happen in update or finish op.setAuthTimeoutValidated(false); } else { // auth per operation required + // store user secure id and authType in OperationState. + op.setUserSecureId(userSecureIdPtr); + op.setAuthType((byte) authType); + // set flags op.setOneTimeAuthReqd(false); op.setAuthPerOperationReqd(true); } } } - private void authenticateUser() { - short id = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); - if (KMInteger.cast(id).isZero()) { - id = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); - if (KMInteger.cast(id).isZero()) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - // check user secure id - if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, id, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - // check auth type - short authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); - short authenticatorType = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); - authenticatorType = KMEnum.cast(authenticatorType).getVal(); - if (((byte) authenticatorType & (byte) authType) == 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - - private boolean validateHwToken(short hwToken, byte[] scratchPad) { - // CBOR Encoding is always big endian - short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short len; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return false; - } + private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { + // The challenge, userId and authenticatorId, authenticatorType and timestamp + // are in network order (big-endian). + short len = 0; // add 0 Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); len = 1; // concatenate challenge - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; @@ -2908,13 +2983,52 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; - // hmac the data + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short key = repository.getComputedHmacKey(); + + return seProvider.hmacVerify( + seProvider.getComputedHmacKey(), + scratchPad, + (short) 0, + len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + } + + private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) { + // The challenge, userId and authenticatorId values are in little endian order, + // but authenticatorType and timestamp are in network order (big-endian). + short len = 0; + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + return seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), + seProvider.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -2923,6 +3037,20 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMByteBlob.cast(ptr).length()); } + private boolean validateHwToken(short hwToken, byte[] scratchPad) { + // CBOR Encoding is always big endian + short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return false; + } + if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { + return verifyHwTokenMacInLittleEndian(hwToken, scratchPad); + } else { + return verifyHwTokenMacInBigEndian(hwToken, scratchPad); + } + } + private short importKeyCmd(APDU apdu){ short cmd = KMArray.instance((short) 6); // Arguments @@ -2953,12 +3081,20 @@ private void processImportKeyCmd(APDU apdu) { } private void validateImportKey(short params, short keyFmt){ + short attKeyPurpose = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, params); + // ATTEST_KEY cannot be combined with any other purpose. + if (attKeyPurpose != KMType.INVALID_VALUE + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } // Rollback protection not supported KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - // vts disallows importing EARLY_BOOT keys + // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is + // provided to IKeyMintDevice::importKey KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); - // Importing Bootloader only keys not supported. - KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + // Algorithm must be present KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); @@ -3016,20 +3152,22 @@ private void importECKeys(byte[] scratchPad) { // initialize 256 bit p256 key for given private key and public key. short index = 0; // check whether the key size tag is present in key parameters. + short SecretLen = (short) (KMByteBlob.length(data[SECRET]) * 8); short keySize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keySize != KMType.INVALID_VALUE) { // As per NIST.SP.800-186 page 9, secret for 256 curve should be between // 256-383 - if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ keySize == 256) { + if (((256 <= SecretLen) && (383 >= SecretLen)) ^ keySize == 256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } if (keySize != 256) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } else { + if ((256 > SecretLen) || (383 < SecretLen)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } // add the key size to scratchPad keySize = KMInteger.uint_16((short) 256); keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize); @@ -3041,15 +3179,16 @@ private void importECKeys(byte[] scratchPad) { if (curve != KMType.INVALID_VALUE) { // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between // 256-383 - if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ curve == KMType.P_256) { + if (((256 <= SecretLen) && (383 >= SecretLen)) ^ curve == KMType.P_256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } if (curve != KMType.P_256) { KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); } } else { + if ((256 > SecretLen) || (383 < SecretLen)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } // add the curve to scratchPad curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); Util.setShort(scratchPad, index, curve); @@ -3068,8 +3207,6 @@ private void importECKeys(byte[] scratchPad) { // add scratch pad to key parameters updateKeyParameters(scratchPad, index); - // validate updated key parameters. - validateECKeys(); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3083,12 +3220,19 @@ private void importHmacKey(byte[] scratchPad) { short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keysize != KMType.INVALID_VALUE) { - if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (keysize != (short) (KMByteBlob.length(data[SECRET]) * 8)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { - // add the key size to scratchPad - keysize = KMInteger.uint_16((short) (KMByteBlob.cast(data[SECRET]).length() * 8)); + // add the key size to scratchPad + keysize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + keysize = KMInteger.uint_16(keysize); short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); Util.setShort(scratchPad, index, keySizeTag); index += 2; @@ -3119,13 +3263,23 @@ private void importTDESKey(byte[] scratchPad) { if (keysize != 168) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } + if (192 != (short) (8 * KMByteBlob.length(data[SECRET]))) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } } else { + keysize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (keysize != 192) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } // add the key size to scratchPad keysize = KMInteger.uint_16((short) 168); short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); Util.setShort(scratchPad, index, keysizeTag); index += 2; } + // Read Minimum Mac length - it must not be present + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMError.INVALID_TAG); // Check whether key can be created seProvider.importSymmetricKey( KMType.DES, @@ -3135,8 +3289,6 @@ private void importTDESKey(byte[] scratchPad) { KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list updateKeyParameters(scratchPad, index); - // validate TDES Key parameters - validateTDESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3155,6 +3307,9 @@ private void importAESKey(byte[] scratchPad) { short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keysize != KMType.INVALID_VALUE) { + if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } validateAesKeySize(keysize); } else { // add the key size to scratchPad @@ -3192,7 +3347,7 @@ private void importRSAKey(byte[] scratchPad) { } if(Util.arrayCompare(F4, (short)0, KMByteBlob.cast(pubKeyExp).getBuffer(), KMByteBlob.cast(pubKeyExp).getStartOff(), (short)F4.length) != 0){ - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } short index = 0; // index in scratchPad for update parameters. // validate public exponent if present in key params - it must be 0x010001 @@ -3223,12 +3378,15 @@ private void importRSAKey(byte[] scratchPad) { // check the keysize tag if present in key parameters. short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + short kSize = (short) (KMByteBlob.length(data[SECRET]) * 8); if (keysize != KMType.INVALID_VALUE) { - if (keysize != 2048 - || keysize != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { + if (keysize != 2048 || (keysize != kSize)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { + if (2048 != kSize) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } // add the key size to scratchPad keysize = KMInteger.uint_16((short) 2048); keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); @@ -3311,14 +3469,18 @@ private void processInitStrongBoxCmd(APDU apdu) { setVendorPatchLevel(vendorPatchLevel); } - public void reboot(){ - // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - repository.clearComputedHmac(); - repository.clearHmacNonce(); - //Clear all the operation state. - releaseAllOperations(); - // Hmac is cleared, so generate a new Hmac nonce. - initHmacNonceAndSeed(); + public void reboot() { + repository.clearHmacNonce(); + //flag to maintain the boot state + repository.setBootEndedStatus(false); + //flag to maintain early boot ended state + repository.setEarlyBootEndedStatus(false); + //Clear all the operation state. + releaseAllOperations(); + // Hmac is cleared, so generate a new Hmac nonce. + initHmacNonceAndSeed(); + // Clear all auth tags. + repository.removeAllAuthTags(); } protected void initSystemBootParams(short osVersion, @@ -3376,10 +3538,19 @@ private void processGenerateKey(APDU apdu) { data[CERTIFICATE] = KMArray.instance((short)0); //by default the cert is empty. // ROLLBACK_RESISTANCE not supported. KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - // BOOTLOADER_ONLY keys not supported. - KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + + // As per specification Early boot keys may be created after early boot ended. // Algorithm must be present KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); + + short attKeyPurpose = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); + // ATTEST_KEY cannot be combined with any other purpose. + if (attKeyPurpose != KMType.INVALID_VALUE + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); // Check algorithm and dispatch to appropriate handler. switch (alg) { @@ -3436,7 +3607,8 @@ private short getAttestationMode(short attKeyBlob, short attChallenge){ // Attestation challenge present then it is an error because no factory provisioned attest key if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) { KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); - } else if(KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[KEY_PARAMETERS])) { + } else if(KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[KEY_PARAMETERS]) || + KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[KEY_PARAMETERS])) { mode = KMType.SELF_SIGNED_CERT; }else{ mode = KMType.FAKE_CERT; @@ -3654,11 +3826,11 @@ private static void validateHmacKey() { // check whether digest sizes are greater then or equal to min mac length. // Only SHA256 digest must be supported. - if (KMEnumArrayTag.contains(KMType.DIGEST, KMType.DIGEST_NONE, data[KEY_PARAMETERS])) { + if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } // Read Minimum Mac length - KMTag.assertPresence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.MISSING_MAC_LENGTH); + KMTag.assertPresence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.MISSING_MIN_MAC_LENGTH); short minMacLength = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); @@ -3829,8 +4001,7 @@ public static short readROT(byte[] scratchPad) { short len = seProvider.getBootKey(scratchPad, (short)0); len += seProvider.getVerifiedBootHash(scratchPad, (short)len); short bootState = seProvider.getBootState(); - Util.setShort(scratchPad, (short)0,bootState); - len +=2; + len = Util.setShort(scratchPad, len, bootState); if(seProvider.isDeviceBootLocked()){ scratchPad[len] = (byte)1; }else{ @@ -3907,88 +4078,58 @@ private static void encryptSecret(byte[] scratchPad) { } private static void makeAuthData(byte[] scratchPad) { - short len = - addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); - len += - addPtrToAAD( - KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, len); - short authData; + short arrayLen = 2; if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - authData = KMArray.instance((short) (len + 1)); - } else { - authData = KMArray.instance(len); + arrayLen = 3; } - // convert scratch pad to KMArray + 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) 1, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (3 == arrayLen) { + KMArray.cast(params).add((short) 2, data[PUB_KEY]); + } + + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); short index = 0; - short objPtr; - while (index < len) { - objPtr = Util.getShort(scratchPad, (short) (index * 2)); - KMArray.cast(authData).add(index, objPtr); + 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++; } - //TODO change the code below - implicitly adds the pub key. - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - KMArray.cast(authData).add(index, data[PUB_KEY]); - } - data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); - len = encoder.encode(authData, repository.getHeap(), data[AUTH_DATA]); + data[AUTH_DATA] = authIndex; 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; - } - private static short deriveKey(byte[] scratchPad) { - short hiddenParams = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); - short derivationData = repository.alloc(DERIVE_KEY_INPUT_SIZE); - // generate derivation material from hidden parameters - short len = encoder.encode(hiddenParams, repository.getHeap(), derivationData); - if (DERIVE_KEY_INPUT_SIZE > len) { - short start = (short)(derivationData+len); - len = (short)(DERIVE_KEY_INPUT_SIZE - len); - // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE - // if the data[AUTH_DATA] length is less then DERIVE_KEY_INPUT_SIZE - len - // then add complete key characteristics. - if(data[AUTH_DATA_LENGTH] < len){ - len = data[AUTH_DATA_LENGTH]; - } - Util.arrayCopyNonAtomic(repository.getHeap(), data[AUTH_DATA], - repository.getHeap(), start, len); - } // 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. - short signLen = seProvider.hmacKDF( + short len = seProvider.hmacKDF( seProvider.getMasterKey(), repository.getHeap(), - derivationData, - DERIVE_KEY_INPUT_SIZE, + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], scratchPad, (short) 0); - if (signLen < 16) { + if (len < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - signLen = 16; - // store the derived secret in data dictionary - data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short)0, signLen); - //TODO do we need to return this len? - return signLen; + len = 16; + data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short)0, len); + return len; } public static void sendError(APDU apdu, short err) { @@ -3999,24 +4140,26 @@ public static void sendError(APDU apdu, short err) { sendOutgoing(apdu, resp); } - private short addIntegers(short num1, short num2) { - short buf = repository.alloc((short) 24); - byte[] scratchPad = repository.getHeap(); - Util.arrayFillNonAtomic(scratchPad, buf, (short) 24, (byte) 0); + private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0); Util.arrayCopyNonAtomic( - KMInteger.cast(num1).getBuffer(), - KMInteger.cast(num1).getStartOff(), + KMInteger.cast(authTime).getBuffer(), + KMInteger.cast(authTime).getStartOff(), scratchPad, - (short) (buf + 8 - KMInteger.cast(num1).length()), - KMInteger.cast(num1).length()); + (short) (8 - KMInteger.cast(timeStamp).length()), + KMInteger.cast(timeStamp).length()); + + // Copy timestamp to scratchpad Util.arrayCopyNonAtomic( - KMInteger.cast(num2).getBuffer(), - KMInteger.cast(num2).getStartOff(), + KMInteger.cast(timeStamp).getBuffer(), + KMInteger.cast(timeStamp).getStartOff(), scratchPad, - (short) (buf + 16 - KMInteger.cast(num2).length()), - KMInteger.cast(num2).length()); - add(scratchPad, buf, (short) (buf + 8), (short) (buf + 16)); - return KMInteger.uint_64(scratchPad, (short) (buf + 16)); + (short) (16 - KMInteger.cast(timeStamp).length()), + KMInteger.cast(timeStamp).length()); + + // add authTime in millis to timestamp. + KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); + return KMInteger.uint_64(scratchPad, (short) 16); } private void add(byte[] buf, short op1, short op2, short result) { @@ -4141,12 +4284,18 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); encodedLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - + + short signatureLen = + rkp.encodeES256CoseSignSignature( + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), + KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)), + scratchPad, + (short) (keySize + encodedLen)); + if (!seProvider.ecVerify256(scratchPad, (short) 0, keySize, scratchPad, keySize, encodedLen, - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).length())) { - KMException.throwIt(KMError.STATUS_FAILED); + scratchPad, (short) (keySize + encodedLen), signatureLen)) { + KMException.throwIt(KMError.STATUS_FAILED); } prevCoseKey = ptr2; } @@ -4225,6 +4374,8 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { scratchPad, temp ); + len = KMPKCS8Decoder.instance(). + decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp); coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); // construct cose_sign1 @@ -4237,4 +4388,29 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { KMArray.cast(bcc).add((short) 1, coseSign1); return bcc; } + + private void updateTrustedConfirmationOperation(KMOperationState op) { + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner().update(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length()); + } + } + + private void finishTrustedConfirmationOperation(KMOperationState op) { + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + + boolean verified = op.getTrustedConfirmationSigner().verify(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()); + if (!verified) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + } + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index df2bcbb3..83370b8e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMOperation; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -28,10 +29,6 @@ */ public class KMOperationState { - public static final byte MAX_DATA = 20; - public static final byte MAX_REFS = 1; - private static final byte DATA = 0; - private static final byte REFS = 1; // byte type private static final byte ALG = 0; private static final byte PURPOSE = 1; @@ -40,175 +37,96 @@ public class KMOperationState { private static final byte DIGEST = 4; private static final byte FLAGS = 5; private static final byte KEY_SIZE = 6; - private static final byte MAC_LENGTH = 7 ; + private static final byte MAC_LENGTH = 7; private static final byte MGF_DIGEST = 8; + private static final byte AUTH_TYPE = 9; + // sizes public static final byte OPERATION_HANDLE_SIZE = 8; - public static final byte DATA_SIZE = 9; + public static final byte DATA_SIZE = 10; public static final byte AUTH_TIME_SIZE = 8; + // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs) + // First two bytes are reserved to store number of secure ids. So total 42 bytes. + public static final byte USER_SECURE_IDS_SIZE = 42; - // short type - - //private static final byte KEY_SIZE = 6; - //private static final byte MAC_LENGTH = 8; - // Handle - currently this is short - private static final byte OP_HANDLE = 10; - // Auth time 64 bits - private static final byte AUTH_TIME = 12; - + private static final byte OPERATION = 0; + private static final byte HMAC_SIGNER_OPERATION = 1; // Flag masks private static final short AUTH_PER_OP_REQD = 1; private static final short SECURE_USER_ID_REQD = 2; private static final short AUTH_TIMEOUT_VALIDATED = 4; private static final short AES_GCM_UPDATE_ALLOWED = 8; - private static final byte ACTIVE = 1; - private static final byte INACTIVE = 0; + // Max user secure ids. + private static final byte MAX_SECURE_USER_IDS = 5; // Object References private byte[] opHandle; private byte[] authTime; + private byte[] userSecureIds; private short[] data; - private Object[] operation; + private Object[] operations; - /* - private static final byte OPERATION = 0; - private static KMOperation op; - private static Object[] slot; - private static KMOperationState prototype; - private static boolean dFlag; -*/ public KMOperationState() { - // data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET); authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET); data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); - operation = JCSystem.makeTransientObjectArray((short)1, JCSystem.CLEAR_ON_RESET); - reset(); - } -/* - private static KMOperationState proto() { - if (prototype == null) { - prototype = new KMOperationState(); - } - return prototype; - } - - public static KMOperationState instance(short opHandle, Object[] slot) { - KMOperationState opState = proto(); - opState.reset(); - Util.setShort(data, OP_HANDLE, opHandle); - KMOperationState.slot = slot; - return opState; - } - - public static KMOperationState read(byte[] oprHandle, short off, Object[] slot) { - KMOperationState opState = proto(); - opState.reset(); - Util.arrayCopy((byte[]) slot[DATA], (short) 0, data, (short) 0, (short) data.length); - Object[] ops = ((Object[]) slot[REFS]); - op = (KMOperation) ops[OPERATION]; - Util.setShort(data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); - KMOperationState.slot = slot; - return opState; - } - - public void persist() { - if (!dFlag) { - return; - } - KMRepository.instance().persistOperation(data, Util.getShort(data, OP_HANDLE), op); - dFlag = false; - } - - private void dataUpdated() { - dFlag = true; - } - - public void release() { - Object[] ops = ((Object[]) slot[REFS]); - ((KMOperation) ops[OPERATION]).abort(); - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic( - (byte[]) slot[0], (short) 0, (short) ((byte[]) slot[0]).length, (byte) 0); - ops[OPERATION] = null; - JCSystem.commitTransaction(); + operations = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); + userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET); reset(); } -*/ public void reset() { byte index = 0; - while(index < DATA_SIZE){ + while (index < DATA_SIZE) { data[index] = KMType.INVALID_VALUE; index++; } Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0); Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0); - if(null != operation[0]) - ((KMOperation)operation[0]).abort(); + if(null != operations[OPERATION]) + ((KMOperation)operations[OPERATION]).abort(); + operations[OPERATION] = null; - operation[0] = null; - /* - dFlag = false; - op = null; - slot = null; - - Util.arrayFillNonAtomic( - data, (short) 0, (short) data.length, (byte) 0); + if(null != operations[HMAC_SIGNER_OPERATION]) + ((KMOperation)operations[HMAC_SIGNER_OPERATION]).abort(); + operations[HMAC_SIGNER_OPERATION] = null; + } - */ -} public short compare(byte[] handle, short start, short len){ return Util.arrayCompare(handle, start, opHandle, (short)0, (short)opHandle.length); } + public void setKeySize(short keySize) { - // Util.setShort(data, KEY_SIZE, keySize); data[KEY_SIZE] = keySize; } public short getKeySize() { - // return Util.getShort(data, KEY_SIZE); return data[KEY_SIZE]; } - public short getHandle(){ - return KMInteger.uint_64(opHandle,(short)0); - } - public void setHandle(short handle){ - setHandle(KMInteger.cast(handle).getBuffer(), - KMInteger.cast(handle).getStartOff(), - KMInteger.cast(handle).length()); - } - public short getHandle(byte[] buf, short start) { - //return Util.getShort(KMOperationState.data, OP_HANDLE); - Util.arrayCopyNonAtomic(opHandle,(short)0, buf, start, (short)opHandle.length); - return (short) opHandle.length; + public short getHandle() { + return KMInteger.uint_64(opHandle, (short) 0); } public void setHandle(byte[] buf, short start, short len) { - Util.arrayCopyNonAtomic(buf,start, opHandle, (short)0, (short) opHandle.length); - //return KMByteBlob.instance(opHandle,(short)0, (short)8); + Util.arrayCopyNonAtomic(buf, start, opHandle, (short) 0, (short) opHandle.length); } + public short getPurpose() { return data[PURPOSE]; } public void setPurpose(short purpose) { data[PURPOSE] = purpose; -// dataUpdated(); } public void setOperation(KMOperation op) { - operation[0] = op; - /*(op = operation; - dataUpdated(); - persist(); - */ + operations[OPERATION] = op; } public KMOperation getOperation() { - return (KMOperation) operation[0]; + return (KMOperation) operations[OPERATION]; } public boolean isAuthPerOperationReqd() { @@ -224,18 +142,11 @@ public boolean isSecureUserIdReqd() { } public short getAuthTime() { - return KMInteger.uint_64(authTime,(short) 0); - // return KMInteger.uint_64(data, (short) AUTH_TIME); + return KMInteger.uint_64(authTime, (short) 0); } - public void setAuthTime(short time){ - setAuthTime(KMInteger.cast(time).getBuffer(), KMInteger.cast(time).getStartOff()); - } public void setAuthTime(byte[] timeBuf, short start) { - Util.arrayCopyNonAtomic(timeBuf,start,authTime,(short)0, AUTH_TIME_SIZE); - /*Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); - dataUpdated(); - */ + Util.arrayCopyNonAtomic(timeBuf, start, authTime, (short) 0, AUTH_TIME_SIZE); } public void setOneTimeAuthReqd(boolean flag) { @@ -244,7 +155,6 @@ public void setOneTimeAuthReqd(boolean flag) { } else { data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD)); } - // dataUpdated(); } public void setAuthTimeoutValidated(boolean flag) { @@ -253,7 +163,55 @@ public void setAuthTimeoutValidated(boolean flag) { } else { data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED)); } - //dataUpdated(); + } + + public void setAuthType(byte authType) { + data[AUTH_TYPE] = authType; + } + + public short getAuthType() { + return data[AUTH_TYPE]; + } + + public short getUserSecureId() { + short offset = 0; + short length = Util.getShort(userSecureIds, offset); + offset += 2; + if (length == 0) { + return KMType.INVALID_VALUE; + } + short arrObj = KMArray.instance(length); + short index = 0; + short obj; + while (index < length) { + obj = KMInteger.instance(userSecureIds, (short) (offset + index * 8), (short) 8); + KMArray.cast(arrObj).add(index, obj); + index++; + } + return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj); + } + + public void setUserSecureId(short integerArrayPtr) { + short length = KMIntegerArrayTag.cast(integerArrayPtr).length(); + if (length > MAX_SECURE_USER_IDS) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + Util.arrayFillNonAtomic(userSecureIds, (short) 0, USER_SECURE_IDS_SIZE, (byte) 0); + short index = 0; + short obj; + short offset = 0; + offset = Util.setShort(userSecureIds, offset, length); + while (index < length) { + obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index); + Util.arrayCopyNonAtomic( + KMInteger.cast(obj).getBuffer(), + KMInteger.cast(obj).getStartOff(), + userSecureIds, + (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index), + KMInteger.cast(obj).length() + ); + index++; + } } public void setAuthPerOperationReqd(boolean flag) { @@ -262,7 +220,6 @@ public void setAuthPerOperationReqd(boolean flag) { } else { data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD)); } - //dataUpdated(); } public short getAlgorithm() { @@ -271,7 +228,6 @@ public short getAlgorithm() { public void setAlgorithm(short algorithm) { data[ALG] = algorithm; - //dataUpdated(); } public short getPadding() { @@ -280,7 +236,6 @@ public short getPadding() { public void setPadding(short padding) { data[PADDING] = padding; - //dataUpdated(); } public short getBlockMode() { @@ -289,7 +244,6 @@ public short getBlockMode() { public void setBlockMode(short blockMode) { data[BLOCK_MODE] = blockMode; - // dataUpdated(); } public short getDigest() { @@ -302,7 +256,6 @@ public short getMgfDigest() { public void setDigest(byte digest) { data[DIGEST] = digest; - // dataUpdated(); } public void setMgfDigest(byte mgfDigest) { @@ -315,56 +268,64 @@ public boolean isAesGcmUpdateAllowed() { public void setAesGcmUpdateComplete() { data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED)); - //dataUpdated(); } public void setAesGcmUpdateStart() { data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED); - //dataUpdated(); } public void setMacLength(short length) { data[MAC_LENGTH] = length; - //Util.setShort(data, MAC_LENGTH, length); - //dataUpdated(); } public short getMacLength() { return data[MAC_LENGTH]; - //return Util.getShort(data, MAC_LENGTH); } - public byte getBufferingMode(){ + + public byte getBufferingMode() { short alg = getAlgorithm(); short purpose = getPurpose(); short digest = getDigest(); short padding = getPadding(); short blockMode = getBlockMode(); - if(alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN){ + if (alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { return KMType.BUF_RSA_NO_DIGEST; } - if(alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN){ + if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { return KMType.BUF_EC_NO_DIGEST; } - switch(alg) { - case KMType.AES: - if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { - return KMType.BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN; - } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { - return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; - } else if (blockMode == KMType.CBC || blockMode == KMType.ECB) { - return KMType.BUF_AES_BLOCK_ALIGN; - } - break; - case KMType.DES: - if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { - return KMType.BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN; - } else { - return KMType.BUF_DES_BLOCK_ALIGN; - } + switch (alg) { + case KMType.AES: + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { + return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; + } + break; + case KMType.DES: + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN; + } } return KMType.BUF_NONE; } + + public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) { + operations[HMAC_SIGNER_OPERATION] = hmacSignerOp; + } + + public KMOperation getTrustedConfirmationSigner() { + return (KMOperation)operations[HMAC_SIGNER_OPERATION]; + } + + public boolean isTrustedConfirmationRequired() { + return operations[HMAC_SIGNER_OPERATION] != null; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index eb860e2d..3040d480 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -103,6 +103,34 @@ private void updateModulus(short blob) { KMByteBlob.cast(blob).setLength(--len); } } + + private short readEcdsa256SigIntegerHeader() { + short len = header(ASN1_INTEGER); + if (len == 33) { + if (0 != getByte()) { + KMException.throwIt(KMError.INVALID_DATA); + } + len--; + } else if (len > 33) { + KMException.throwIt(KMError.INVALID_DATA); + } + return len; + } + + // Seq [Int, Int] + public short decodeEcdsa256Signature(short blob, byte[] scratchPad, short scratchPadOff) { + init(blob); + short len = header(ASN1_SEQUENCE); + len = readEcdsa256SigIntegerHeader(); + // concatenate r and s in the buffer (r||s) + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 64, (byte) 0); + // read r + getBytes(scratchPad, (short) (scratchPadOff + 32 - len), len); + len = readEcdsa256SigIntegerHeader(); + // read s + getBytes(scratchPad, (short) (scratchPadOff + 64 - len), len); + return (short) 64; + } // Seq [Int, Blob] public void decodeCommon(short version, byte[] alg){ @@ -180,6 +208,11 @@ private void getBytes(short blob){ incrementCursor(len); } + private void getBytes(byte[] buffer, short offset, short len) { + Util.arrayCopyNonAtomic(data, cur, buffer, offset, len); + incrementCursor(len); + } + private short getLength(){ byte len = getByte(); if(len >= 0) return len; diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 39340daf..03791210 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -34,7 +34,7 @@ public class KMRepository implements KMUpgradable { public static final short HEAP_SIZE = 15000; // Data table configuration - public static final short DATA_INDEX_SIZE = 12; + public static final short DATA_INDEX_SIZE = 17; public static final short DATA_INDEX_ENTRY_SIZE = 4; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -46,11 +46,16 @@ public class KMRepository implements KMUpgradable { public static final byte COMPUTED_HMAC_KEY = 0; public static final byte HMAC_NONCE = 1; public static final byte BOOT_OS_VERSION = 2; - public static final byte BOOT_OS_PATCH = 3; + public static final byte BOOT_OS_PATCH_LEVEL = 3; public static final byte VENDOR_PATCH_LEVEL = 4; public static final byte DEVICE_LOCKED_TIME = 5; public static final byte DEVICE_LOCKED = 6; - + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + public static final byte AUTH_TAG_1 = 8; + public static final byte BOOT_ENDED_FLAG = 15; + public static final byte EARLY_BOOT_ENDED_FLAG = 16; + // Data Item sizes public static final short HMAC_SEED_NONCE_SIZE = 32; public static final short COMPUTED_HMAC_KEY_SIZE = 32; @@ -58,7 +63,10 @@ public class KMRepository implements KMUpgradable { public static final short OS_PATCH_SIZE = 4; public static final short VENDOR_PATCH_SIZE = 4; public static final short DEVICE_LOCK_TS_SIZE = 8; - public static final short DEVICE_LOCK_FLAG_SIZE = 1; + 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; + public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); // Class Attributes private byte[] heap; @@ -88,13 +96,6 @@ public KMRepository(boolean isUpgrading) { repository = this; } - public void initComputedHmac(byte[] key, short start, short len) { - if (len != COMPUTED_HMAC_KEY_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(COMPUTED_HMAC_KEY, key, start, len); - } - public void initHmacNonce(byte[] nonce, short offset, short len) { if (len != HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); @@ -106,10 +107,6 @@ public void clearHmacNonce() { clearDataEntry(HMAC_NONCE); } - public void clearComputedHmac() { - clearDataEntry(COMPUTED_HMAC_KEY); - } - public void onUninstall() { // Javacard Runtime environment cleans up the data. @@ -186,34 +183,36 @@ private void newDataTable(boolean isUpgrading) { } private void clearDataEntry(short id) { - JCSystem.beginTransaction(); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen != 0) { short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0); + JCSystem.commitTransaction(); } - JCSystem.commitTransaction(); } private void writeDataEntry(short id, byte[] buf, short offset, short len) { - JCSystem.beginTransaction(); short dataPtr; id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen == 0) { dataPtr = dataAlloc(len); + JCSystem.beginTransaction(); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len); Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); } else { if (len != dataLen) { KMException.throwIt(KMError.UNKNOWN_ERROR); } dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); } - JCSystem.commitTransaction(); } private short readDataEntry(short id, byte[] buf, short offset) { @@ -243,24 +242,21 @@ public short getHmacNonce() { return readData(HMAC_NONCE); } - public short getComputedHmacKey() { - return readData(COMPUTED_HMAC_KEY); - } - public short readData(short id) { - short blob = KMByteBlob.instance(dataLength(id)); - if (readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()) - == 0) { - return 0; + short len = dataLength(id); + if (len != 0) { + short blob = KMByteBlob.instance(dataLength(id)); + readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + return blob; } - return blob; + return KMType.INVALID_VALUE; } private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; public short getOsVersion() { short blob = readData(BOOT_OS_VERSION); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -270,7 +266,7 @@ public short getOsVersion() { public short getVendorPatchLevel() { short blob = readData(VENDOR_PATCH_LEVEL); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -278,10 +274,9 @@ public short getVendorPatchLevel() { } } - public short getOsPatch() { - short blob = readData(BOOT_OS_PATCH); - if (blob != 0) { + short blob = readData(BOOT_OS_PATCH_LEVEL); + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -289,19 +284,33 @@ public short getOsPatch() { } } + private boolean readBoolean(short id) { + short blob = readData(id); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; + } + public boolean getDeviceLock() { - short blob = readData(DEVICE_LOCKED); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; + return readBoolean(DEVICE_LOCKED); } public boolean getDeviceLockPasswordOnly() { - short blob = readData(DEVICE_LOCKED); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFD) != 0; + return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY); + } + + public boolean getEarlyBootEndedStatus() { + return readBoolean(EARLY_BOOT_ENDED_FLAG); + } + + public boolean getBootEndedStatus() { + return readBoolean(BOOT_ENDED_FLAG); } public short getDeviceTimeStamp() { short blob = readData(DEVICE_LOCKED_TIME); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -323,24 +332,22 @@ public void setVendorPatchLevel(byte[] buf, short start, short len) { writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len); } - public void setDeviceLock(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); + private void writeBoolean(short id, boolean flag) { + short start = alloc((short) 1); if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x01); + (getHeap())[start] = (byte) 0x01; } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFE); + (getHeap())[start] = (byte) 0x00; } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(id, getHeap(), start, (short) 1); + } + + public void setDeviceLock(boolean flag) { + writeBoolean(DEVICE_LOCKED, flag); } public void setDeviceLockPasswordOnly(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); - if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x02); - } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFD); - } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag); } public void setDeviceLockTimestamp(byte[] buf, short start, short len) { @@ -350,6 +357,14 @@ public void setDeviceLockTimestamp(byte[] buf, short start, short len) { writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len); } + public void setEarlyBootEndedStatus(boolean flag) { + writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); + } + + public void setBootEndedStatus(boolean flag) { + writeBoolean(BOOT_ENDED_FLAG, flag); + } + public void clearDeviceLockTimeStamp() { clearDataEntry(DEVICE_LOCKED_TIME); } @@ -358,8 +373,119 @@ public void setOsPatch(byte[] buf, short start, short len) { if (len != OS_PATCH_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - writeDataEntry(BOOT_OS_PATCH, buf, start, len); + writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len); + } + + private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) { + readDataEntry(tagId, buf, offset); + return (0 == buf[offset]); + } + + private void writeAuthTagState(byte[] buf, short offset, byte state) { + buf[offset] = state; + } + + public boolean persistAuthTag(short authTag) { + + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + + short authTagEntry = alloc(AUTH_TAG_ENTRY_SIZE); + short scratchPadOff = alloc(AUTH_TAG_ENTRY_SIZE); + byte[] scratchPad = getHeap(); + writeAuthTagState(getHeap(), authTagEntry, (byte) 1); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + getHeap(), (short) (authTagEntry + 1), AUTH_TAG_LENGTH); + Util.setShort(getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), + (short) 1); + short index = 0; + while (index < MAX_BLOB_STORAGE) { + if ((dataLength((short) (index + AUTH_TAG_1)) == 0) || + isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) { + + writeDataEntry((short) (index + AUTH_TAG_1), getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE); + return true; + } + index++; + } + return false; + } + + public void removeAllAuthTags() { + short index = 0; + while (index < MAX_BLOB_STORAGE) { + clearDataEntry((short) (index + AUTH_TAG_1)); + index++; + } } + + public boolean isAuthTagPersisted(short authTag) { + return (KMType.INVALID_VALUE != findTag(authTag)); + } + + private short findTag(short authTag) { + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + short index = 0; + short found; + short offset = alloc(AUTH_TAG_ENTRY_SIZE); + while (index < MAX_BLOB_STORAGE) { + if (dataLength((short) (index + AUTH_TAG_1)) != 0) { + readDataEntry((short) (index + AUTH_TAG_1), + getHeap(), offset); + found = + Util.arrayCompare( + getHeap(), + (short) (offset + 1), + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AUTH_TAG_LENGTH); + if (found == 0) { + return (short) (index + AUTH_TAG_1); + } + } + index++; + } + return KMType.INVALID_VALUE; + } + + public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) { + short tag = findTag(authTag); + short blob; + if (tag != KMType.INVALID_VALUE) { + blob = readData(tag); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(blob).getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1), + out, + outOff, + AUTH_TAG_COUNTER_SIZE); + return AUTH_TAG_COUNTER_SIZE; + } + return (short) 0; + } + + public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) { + short tag = findTag(authTag); + if (tag != KMType.INVALID_VALUE) { + short dataPtr = readData(tag); + Util.arrayCopyNonAtomic( + buf, + off, + KMByteBlob.cast(dataPtr).getBuffer(), + (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1), + len); + writeDataEntry(tag, + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + KMByteBlob.cast(dataPtr).length()); + } + } + @Override public void onSave(Element ele) { ele.write(dataIndex); diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index 499c7636..7aab6c7d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -92,6 +92,7 @@ public abstract class KMType { public static final byte P_256 = 0x01; public static final byte P_384 = 0x02; public static final byte P_521 = 0x03; + public static final byte CURVE_25519 = 0x04; // KeyBlobUsageRequirements Enum Tag key and values. public static final short BLOB_USAGE_REQ = 0x012D; @@ -209,6 +210,8 @@ public abstract class KMType { public static final short USERID = 0x01F5; // Auth Timeout public static final short AUTH_TIMEOUT = 0x01F9; + // Auth Timeout in Milliseconds + public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF; // OS Version public static final short OS_VERSION = 0x02C1; // OS Patch Level @@ -349,10 +352,10 @@ public abstract class KMType { public static final byte BUF_NONE = 0; public static final byte BUF_RSA_NO_DIGEST = 1; public static final byte BUF_EC_NO_DIGEST = 2; - public static final byte BUF_AES_BLOCK_ALIGN = 3; - public static final byte BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN = 4; - public static final byte BUF_DES_BLOCK_ALIGN = 5; - public static final byte BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN = 6; + public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3; + public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4; + public static final byte BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN = 5; + public static final byte BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN = 6; public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7; protected static KMRepository repository; diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index adc1684a..4a68b718 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -763,33 +763,6 @@ private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLeng ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); } - private static short getAttestKeyParameters() { - short tagIndex = 0; - short arrPtr = KMArray.instance((short) 6); - // Key size - 256 - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); - // Digest - SHA256 - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - // Purpose - Attest - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - - KMArray.cast(arrPtr).add(tagIndex++, purpose); - // Algorithm - EC - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - // Curve - P256 - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); - // No Authentication is required to use this key. - KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); - return KMKeyParameters.instance(arrPtr); - } - private short createSignedMac(KMDeviceUniqueKey deviceUniqueKey, byte[] scratchPad, short deviceMapPtr, short pubKeysToSign) { // Challenge @@ -833,6 +806,8 @@ private short createSignedMac(KMDeviceUniqueKey deviceUniqueKey, byte[] scratchP scratchPad, signStructure ); + len = KMPKCS8Decoder.instance(). + decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, signStructure, len), scratchPad, signStructure); signStructure = KMByteBlob.instance(scratchPad, signStructure, len); /* Construct unprotected headers */ @@ -926,7 +901,9 @@ private short createDeviceInfo(byte[] scratchpad) { updateItem(deviceIds, out, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION)); updateItem(deviceIds, out, SECURITY_LEVEL, KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); - //TODO Add attest_id_state + byte[] attestIdState = seProvider.isProvisionLocked() ? ATTEST_ID_LOCKED : ATTEST_ID_OPEN; + updateItem(deviceIds, out, ATTEST_ID_STATE, + KMTextString.instance(attestIdState, (short) 0, (short) attestIdState.length)); // Create device info map. short map = KMMap.instance(out[1]); short mapIndex = 0; @@ -1043,6 +1020,11 @@ private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKe pubKeyBLen, scratchPad, (short) 0); key = KMByteBlob.instance(scratchPad, (short) 0, key); + // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04. + pubKeyAOff += 1; + pubKeyALen -= 1; + pubKeyBOff += 1; + pubKeyBLen -= 1; short kdfContext = KMCose.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, true); @@ -1329,27 +1311,6 @@ private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { .constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); } - private short constructCoseEncryptHeaders(byte[] scratchPad, short nonce) { - // CoseEncrypt protected headers. - short protectedHeader = KMCose.constructHeaders( - KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); - /* CoseEncrypt unprotected headers */ - short unprotectedHeader = - KMCose.constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, - KMType.INVALID_VALUE); - // Construct partial CoseEncrypt - return KMCose.constructCoseEncrypt(protectedHeader, unprotectedHeader, KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - } - - private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) { // prepare cosekey short coseKey = @@ -1427,4 +1388,71 @@ private short getEcAttestKeyParameters() { KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); return KMKeyParameters.instance(arrPtr); } + + private boolean isSignedByte(byte b) { + return ((b & 0x0080) != 0); + } + + private short writeIntegerHeader(short valueLen, byte[] data, short offset) { + // write length + data[offset] = (byte) valueLen; + // write INTEGER tag + offset--; + data[offset] = 0x02; + return offset; + } + + private short writeSequenceHeader(short valueLen, byte[] data, short offset) { + // write length + data[offset] = (byte) valueLen; + // write INTEGER tag + offset--; + data[offset] = 0x30; + return offset; + } + + private short writeSignatureData(byte[] input, short inputOff, short inputlen, byte[] output, short offset) { + Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen); + if (isSignedByte(input[inputOff])) { + offset--; + output[offset] = (byte) 0; + } + return offset; + } + + public short encodeES256CoseSignSignature(byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) { + // SEQ [ INTEGER(r), INTEGER(s)] + // write from bottom to the top + if (len != 64) { + KMException.throwIt(KMError.INVALID_DATA); + } + short maxTotalLen = 72; + short end = (short) (scratchPadOff + maxTotalLen); + // write s. + short start = (short) (end - 32); + start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start); + // write length and header + short length = (short) (end - start); + start--; + start = writeIntegerHeader(length, scratchPad, start); + // write r + short rEnd = start; + start = (short) (start - 32); + start = writeSignatureData(input, offset, (short) 32, scratchPad, start); + // write length and header + length = (short) (rEnd - start); + start--; + start = writeIntegerHeader(length, scratchPad, start); + // write length and sequence header + length = (short) (end - start); + start--; + start = writeSequenceHeader(length, scratchPad, start); + length = (short) (end - start); + if (start > scratchPadOff) { + // re adjust the buffer + Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length); + } + return length; + } + } diff --git a/HAL/Android.bp b/HAL/Android.bp index bf842f8b..69fead77 100644 --- a/HAL/Android.bp +++ b/HAL/Android.bp @@ -25,14 +25,14 @@ cc_library { "JavacardRemotelyProvisionedComponentDevice.cpp", "JavacardSecureElement.cpp", "JavacardSharedSecret.cpp", + "JavacardKeyMintUtils.cpp", "keymint_utils.cpp", ], cflags:["-O0",], shared_libs: [ - "android.hardware.security.keymint-V1-ndk_platform", - "android.hardware.security.secureclock-V1-ndk_platform", - "android.hardware.security.sharedsecret-V1-ndk_platform", - "lib_android_keymaster_keymint_utils", + "android.hardware.security.keymint-V1-ndk", + "android.hardware.security.secureclock-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", "libbase", "libcppbor_external", "libkeymaster_portable", @@ -52,7 +52,6 @@ cc_library { cc_library { name: "libjc_keymint_transport", - host_supported: true, vendor_available: true, srcs: [ "SocketTransport.cpp", @@ -80,8 +79,8 @@ cc_binary { "-Wextra", ], shared_libs: [ - "android.hardware.security.keymint-V1-ndk_platform", - "android.hardware.security.sharedsecret-V1-ndk_platform", + "android.hardware.security.keymint-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", "libbase", "libbinder_ndk", "libcppbor_external", diff --git a/HAL/CborConverter.cpp b/HAL/CborConverter.cpp index a97d12ad..e34c651c 100644 --- a/HAL/CborConverter.cpp +++ b/HAL/CborConverter.cpp @@ -16,7 +16,7 @@ */ #include "CborConverter.h" -#include +#include #include #include #include @@ -331,8 +331,8 @@ bool CborConverter::addSharedSecretParameters(Array& array, bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) { Array vToken; - vToken.add(token.challenge); - vToken.add(token.timestamp.milliSeconds); + vToken.add(static_cast(token.challenge)); + vToken.add(static_cast(token.timestamp.milliSeconds)); vToken.add((std::vector(token.mac))); array.add(std::move(vToken)); return true; @@ -341,11 +341,11 @@ bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) { Array hwAuthToken; - hwAuthToken.add(authToken.challenge); - hwAuthToken.add(authToken.userId); - hwAuthToken.add(authToken.authenticatorId); + hwAuthToken.add(static_cast(authToken.challenge)); + hwAuthToken.add(static_cast(authToken.userId)); + hwAuthToken.add(static_cast(authToken.authenticatorId)); hwAuthToken.add(static_cast(authToken.authenticatorType)); - hwAuthToken.add(authToken.timestamp.milliSeconds); + hwAuthToken.add(static_cast(authToken.timestamp.milliSeconds)); hwAuthToken.add((std::vector(authToken.mac))); array.add(std::move(hwAuthToken)); return true; @@ -354,27 +354,39 @@ bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& bool CborConverter::getHardwareAuthToken(const unique_ptr& item, const uint32_t pos, HardwareAuthToken& token) { uint64_t authType; + uint64_t challenge; + uint64_t userId; + uint64_t authenticatorId; + uint64_t timestampMillis; // challenge, userId, AuthenticatorId, AuthType, Timestamp, MAC - if (!getUint64(item, pos, token.challenge) || - !getUint64(item, pos + 1, token.userId) || - !getUint64(item, pos + 2, token.authenticatorId) || + if (!getUint64(item, pos, challenge) || + !getUint64(item, pos + 1, userId) || + !getUint64(item, pos + 2, authenticatorId) || !getUint64(item, pos + 3, authType) || - !getUint64(item, pos + 4, token.timestamp.milliSeconds) || + !getUint64(item, pos + 4, timestampMillis) || !getBinaryArray(item, pos + 5, token.mac)) { return false; } + token.challenge = static_cast(challenge); + token.userId = static_cast(userId); + token.authenticatorId = static_cast(authenticatorId); token.authenticatorType = static_cast(authType); + token.timestamp.milliSeconds = static_cast(timestampMillis); return true; } bool CborConverter::getTimeStampToken(const unique_ptr& item, const uint32_t pos, TimeStampToken& token) { // {challenge, timestamp, Mac} - if (!getUint64(item, pos, token.challenge) || - !getUint64(item, pos + 1, token.timestamp.milliSeconds) || - !getBinaryArray(item, pos + 4, token.mac)) { + uint64_t challenge; + uint64_t timestampMillis; + if (!getUint64(item, pos, challenge) || + !getUint64(item, pos + 1, timestampMillis) || + !getBinaryArray(item, pos + 2, token.mac)) { return false; } + token.challenge = static_cast(challenge); + token.timestamp.milliSeconds = static_cast(timestampMillis); return true; } diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp index 402504b3..584c75ed 100644 --- a/HAL/JavacardKeyMintDevice.cpp +++ b/HAL/JavacardKeyMintDevice.cpp @@ -18,7 +18,7 @@ #include "JavacardKeyMintDevice.h" #include "JavacardKeyMintOperation.h" #include "JavacardSharedSecret.h" -#include +#include #include #include #include @@ -116,7 +116,6 @@ ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyPa cppbor::Array request; vector updatedParams(keyParams); - // Add CREATION_DATETIME if required, as secure element is not having clock. if (!findTag(keyParams, Tag::CREATION_DATETIME) && !findTag(keyParams, Tag::ACTIVE_DATETIME)) { updatedParams.push_back(km_utils::kmParam2Aidl( @@ -164,7 +163,6 @@ ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector& wra vector authList; KeyFormat keyFormat; std::vector wrappedKeyDescription; - keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, authList, keyFormat, wrappedKeyDescription); if (errorCode != KM_ERROR_OK) { @@ -288,6 +286,10 @@ ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector cbor_.addKeyparameters(array, params); HardwareAuthToken token = authToken.value_or(HardwareAuthToken()); cbor_.addHardwareAuthToken(array, token); + + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array); if (err != KM_ERROR_OK) { LOG(ERROR) << "Error in sending in begin."; @@ -329,9 +331,20 @@ JavacardKeyMintDevice::deviceLocked(bool passwordOnly, return ScopedAStatus::ok(); } +void JavacardKeyMintDevice::handleSendEarlyBootEndedEvent() { + if (isEarlyBootEventPending) { + LOG(INFO) << "JavacardKeyMintDevice::handleSendEarlyBootEndedEvent send earlyBootEnded Event."; + if (earlyBootEnded().isOk()) { + isEarlyBootEventPending = false; + } + } +} + ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() { auto [item, err] = card_->sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); if (err != KM_ERROR_OK) { + // Incase of failure cache the event and send in the next immediate request to Applet. + isEarlyBootEventPending = true; return km_utils::kmError2ScopedAStatus(err); } return ScopedAStatus::ok(); diff --git a/HAL/JavacardKeyMintDevice.h b/HAL/JavacardKeyMintDevice.h index 51ee0c14..4bbbaa87 100644 --- a/HAL/JavacardKeyMintDevice.h +++ b/HAL/JavacardKeyMintDevice.h @@ -35,7 +35,8 @@ using std::vector; class JavacardKeyMintDevice : public BnKeyMintDevice { public: explicit JavacardKeyMintDevice(shared_ptr card) - : securitylevel_(SecurityLevel::STRONGBOX), card_(card) { + : securitylevel_(SecurityLevel::STRONGBOX), card_(card), + isEarlyBootEventPending(false) { card_->initializeJavacard(); } virtual ~JavacardKeyMintDevice() {} @@ -106,9 +107,12 @@ class JavacardKeyMintDevice : public BnKeyMintDevice { ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info); + void handleSendEarlyBootEndedEvent(); + const SecurityLevel securitylevel_; const shared_ptr card_; CborConverter cbor_; + bool isEarlyBootEventPending; }; } // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp index d4952aa0..38cbb0b4 100644 --- a/HAL/JavacardKeyMintOperation.cpp +++ b/HAL/JavacardKeyMintOperation.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "javacard.strongbox.keymint.operation-impl" #include "JavacardKeyMintOperation.h" -#include +#include #include #include #include @@ -75,9 +75,10 @@ ScopedAStatus JavacardKeyMintOperation::update(const vector& input, ScopedAStatus JavacardKeyMintOperation::finish( const optional>& input, const optional>& signature, const optional& authToken, const optional& timestampToken, - const optional>& /*confirmationToken*/, vector* output) { + const optional>& confirmationToken, vector* output) { HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + const vector confToken = confirmationToken.value_or(vector()); const vector inData = input.value_or(vector()); DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; const vector sign = signature.value_or(vector()); @@ -92,7 +93,7 @@ ScopedAStatus JavacardKeyMintOperation::finish( } } vector remaining = popNextChunk(view, view.length); - return km_utils::kmError2ScopedAStatus(sendFinish(remaining, sign, aToken, tToken, *output)); + return km_utils::kmError2ScopedAStatus(sendFinish(remaining, sign, aToken, tToken, confToken, *output)); } ScopedAStatus JavacardKeyMintOperation::abort() { @@ -130,18 +131,18 @@ uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t bl uint16_t offset = 0; uint16_t remaining = 0; switch(bufferingMode_) { - case BufferingMode::BUF_AES_BLOCK_ALIGNED: - case BufferingMode::BUF_DES_BLOCK_ALIGNED: - offset = ((view.length / blockSize)) * blockSize; - break; - case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: offset = ((view.length / blockSize)) * blockSize; remaining = (view.length % blockSize); if (offset >= blockSize && remaining == 0) { offset -= blockSize; } break; + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + break; case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: if (view.length > macLength_) { offset = (view.length - macLength_); @@ -176,14 +177,14 @@ keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { view.start = 0; view.length = 0; break; - case BufferingMode::BUF_AES_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: blockAlign(view, AES_BLOCK_SIZE); break; case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: blockAlign(view, macLength_); break; - case BufferingMode::BUF_DES_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: blockAlign(view, DES_BLOCK_SIZE); break; @@ -193,7 +194,7 @@ keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { return KM_ERROR_OK; } -// Incermentally send the request using multiple updates. +// Incrementally send the request using multiple updates. keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view, HardwareAuthToken& authToken, TimeStampToken& timestampToken, @@ -262,6 +263,7 @@ keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector& da const vector& sign, const HardwareAuthToken& authToken, const TimeStampToken& timestampToken, + const vector& confToken, vector& output) { cppbor::Array request; request.add(Uint(opHandle_)); @@ -269,6 +271,8 @@ keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector& da request.add(Bstr(sign)); cbor_.addHardwareAuthToken(request, authToken); cbor_.addTimeStampToken(request, timestampToken); + request.add(Bstr(confToken)); + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request); if (err != KM_ERROR_OK) { return err; diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h index 4f5fc046..2ac6930b 100644 --- a/HAL/JavacardKeyMintOperation.h +++ b/HAL/JavacardKeyMintOperation.h @@ -46,9 +46,9 @@ enum class BufferingMode : int32_t { // will further check according to exact key size and crypto provider. EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate // upto exact keysize. - BUF_AES_BLOCK_ALIGNED = 3, // Buffer 15 bytes and reminder to make input data block aligned. + BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes. BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes. - BUF_DES_BLOCK_ALIGNED = 5, // Buffer 7 bytes and reminder to make input data block aligned. + BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 5, // Buffer 8 bytes. BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes. BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes. @@ -98,7 +98,7 @@ class JavacardKeyMintOperation : public BnKeyMintOperation { keymaster_error_t sendFinish(const vector& data, const vector& signature, const HardwareAuthToken& authToken, - const TimeStampToken& timestampToken, vector& output); + const TimeStampToken& timestampToken, const vector& confToken, vector& output); keymaster_error_t sendUpdate(const vector& data, const HardwareAuthToken& authToken, const TimeStampToken& timestampToken, vector& output); diff --git a/HAL/JavacardKeyMintUtils.cpp b/HAL/JavacardKeyMintUtils.cpp new file mode 100644 index 00000000..9860d407 --- /dev/null +++ b/HAL/JavacardKeyMintUtils.cpp @@ -0,0 +1,241 @@ +/* + * 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" BASIS, + * 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. + */ + +#include "JavacardKeyMintUtils.h" +#include +#include + +namespace aidl::android::hardware::security::keymint::km_utils { + +keymaster_key_param_t kInvalidTag{.tag = KM_TAG_INVALID, .integer = 0}; + +KeyParameter kmEnumParam2Aidl(const keymaster_key_param_t& param) { + switch (param.tag) { + case KM_TAG_PURPOSE: + return KeyParameter{Tag::PURPOSE, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_ALGORITHM: + return KeyParameter{Tag::ALGORITHM, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_BLOCK_MODE: + return KeyParameter{Tag::BLOCK_MODE, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_DIGEST: + return KeyParameter{Tag::DIGEST, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_PADDING: + return KeyParameter{Tag::PADDING, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_EC_CURVE: + return KeyParameter{Tag::EC_CURVE, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_USER_AUTH_TYPE: + return KeyParameter{Tag::USER_AUTH_TYPE, + KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_ORIGIN: + return KeyParameter{Tag::ORIGIN, KeyParameterValue::make( + static_cast(param.enumerated))}; + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_KDF: + default: + return KeyParameter{Tag::INVALID, false}; + } +} + + +KeyParameter kmParam2Aidl(const keymaster_key_param_t& param) { + auto tag = legacy_enum_conversion(param.tag); + switch (typeFromTag(param.tag)) { + case KM_ENUM: + case KM_ENUM_REP: + return kmEnumParam2Aidl(param); + break; + + case KM_UINT: + case KM_UINT_REP: + return KeyParameter{tag, + KeyParameterValue::make(param.integer)}; + + case KM_ULONG: + case KM_ULONG_REP: + return KeyParameter{ + tag, KeyParameterValue::make(param.long_integer)}; + break; + + case KM_DATE: + return KeyParameter{tag, + KeyParameterValue::make(param.date_time)}; + break; + + case KM_BOOL: + return KeyParameter{tag, param.boolean}; + break; + + case KM_BIGNUM: + case KM_BYTES: + return {tag, KeyParameterValue::make( + std::vector(param.blob.data, param.blob.data + param.blob.data_length))}; + break; + + case KM_INVALID: + default: + CHECK(false) << "Unknown or unused tag type: Something is broken"; + return KeyParameter{Tag::INVALID, false}; + break; + } +} + +vector kmParamSet2Aidl(const keymaster_key_param_set_t& set) { + vector result; + if (set.length == 0 || set.params == nullptr) return result; + + result.reserve(set.length); + for (size_t i = 0; i < set.length; ++i) { + result.push_back(kmParam2Aidl(set.params[i])); + } + return result; +} + +template +keymaster_key_param_t aidlEnumVal2Km(keymaster_tag_t km_tag, const KeyParameterValue& value) { + return value.getTag() == aidl_tag + ? keymaster_param_enum(km_tag, static_cast(value.get())) + : kInvalidTag; +} + +keymaster_key_param_t aidlEnumParam2Km(const KeyParameter& param) { + auto tag = legacy_enum_conversion(param.tag); + switch (tag) { + case KM_TAG_PURPOSE: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_ALGORITHM: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_BLOCK_MODE: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_DIGEST: + case KM_TAG_RSA_OAEP_MGF_DIGEST: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_PADDING: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_EC_CURVE: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_USER_AUTH_TYPE: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_ORIGIN: + return aidlEnumVal2Km(tag, param.value); + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_KDF: + default: + CHECK(false) << "Unknown or unused enum tag: Something is broken"; + return keymaster_param_enum(tag, false); + } +} + + + +keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, + LegacyHardwareAuthToken* legacyToken) { + legacyToken->challenge = aidlToken.challenge; + legacyToken->user_id = aidlToken.userId; + legacyToken->authenticator_id = aidlToken.authenticatorId; + legacyToken->authenticator_type = + static_cast(aidlToken.authenticatorType); + legacyToken->timestamp = aidlToken.timestamp.milliSeconds; + Vec2KmBlob(aidlToken.mac, &legacyToken->mac); + return KM_ERROR_OK; +} + +keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, + vector* encodedToken) { + cppbor::Array array; + ::keymaster::TimestampToken token; + array.add(static_cast(timestampToken.challenge)); + array.add(static_cast(timestampToken.timestamp.milliSeconds)); + array.add(timestampToken.mac); + *encodedToken = array.encode(); + return KM_ERROR_OK; +} + +keymaster_key_param_set_t aidlKeyParams2Km(const vector& keyParams) { + keymaster_key_param_set_t set; + + set.params = static_cast( + malloc(keyParams.size() * sizeof(keymaster_key_param_t))); + set.length = keyParams.size(); + + for (size_t i = 0; i < keyParams.size(); ++i) { + const auto& param = keyParams[i]; + auto tag = legacy_enum_conversion(param.tag); + switch (typeFromTag(tag)) { + + case KM_ENUM: + case KM_ENUM_REP: + set.params[i] = aidlEnumParam2Km(param); + break; + + case KM_UINT: + case KM_UINT_REP: + set.params[i] = + param.value.getTag() == KeyParameterValue::integer + ? keymaster_param_int(tag, param.value.get()) + : kInvalidTag; + break; + + case KM_ULONG: + case KM_ULONG_REP: + set.params[i] = + param.value.getTag() == KeyParameterValue::longInteger + ? keymaster_param_long(tag, param.value.get()) + : kInvalidTag; + break; + + case KM_DATE: + set.params[i] = + param.value.getTag() == KeyParameterValue::dateTime + ? keymaster_param_date(tag, param.value.get()) + : kInvalidTag; + break; + + case KM_BOOL: + set.params[i] = keymaster_param_bool(tag); + break; + + case KM_BIGNUM: + case KM_BYTES: + if (param.value.getTag() == KeyParameterValue::blob) { + const auto& value = param.value.get(); + uint8_t* copy = static_cast(malloc(value.size())); + std::copy(value.begin(), value.end(), copy); + set.params[i] = keymaster_param_blob(tag, copy, value.size()); + } else { + set.params[i] = kInvalidTag; + } + break; + + case KM_INVALID: + default: + CHECK(false) << "Invalid tag: Something is broken"; + set.params[i].tag = KM_TAG_INVALID; + /* just skip */ + break; + } + } + + return set; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintUtils.h b/HAL/JavacardKeyMintUtils.h new file mode 100644 index 00000000..9b103ff1 --- /dev/null +++ b/HAL/JavacardKeyMintUtils.h @@ -0,0 +1,90 @@ +/* + * 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" BASIS, + * 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. + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint::km_utils { +using namespace ::keymaster; +using secureclock::TimeStampToken; +using ::ndk::ScopedAStatus; +using std::vector; +using LegacyHardwareAuthToken = ::keymaster::HardwareAuthToken; + +inline keymaster_tag_t legacy_enum_conversion(const Tag value) { + return static_cast(value); +} + +inline Tag legacy_enum_conversion(const keymaster_tag_t value) { + return static_cast(value); +} + +inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { + return keymaster_tag_get_type(tag); +} + +inline void Vec2KmBlob(const vector& input, KeymasterBlob* blob) { + blob->Reset(input.size()); + memcpy(blob->writable_data(), input.data(), input.size()); +} + +inline vector kmBlob2vector(const keymaster_key_blob_t& blob) { + vector result(blob.key_material, blob.key_material + blob.key_material_size); + return result; +} + +inline vector kmBlob2vector(const keymaster_blob_t& blob) { + vector result(blob.data, blob.data + blob.data_length); + return result; +} + +keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, + LegacyHardwareAuthToken* legacyToken); + +keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, + vector* encodedToken); + +inline ScopedAStatus kmError2ScopedAStatus(const keymaster_error_t value) { + return (value == KM_ERROR_OK + ? ScopedAStatus::ok() + : ScopedAStatus(AStatus_fromServiceSpecificError(static_cast(value)))); +} + +KeyParameter kmParam2Aidl(const keymaster_key_param_t& param); +vector kmParamSet2Aidl(const keymaster_key_param_set_t& set); +keymaster_key_param_set_t aidlKeyParams2Km(const vector& keyParams); + +class KmParamSet : public keymaster_key_param_set_t { + public: + explicit KmParamSet(const vector& keyParams) + : keymaster_key_param_set_t(aidlKeyParams2Km(keyParams)) {} + + KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} { + other.length = 0; + other.params = nullptr; + } + + KmParamSet(const KmParamSet&) = delete; + ~KmParamSet() { keymaster_free_param_set(this); } +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp index 5657f9e2..9055de94 100644 --- a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl" #include #include -#include +#include #include #include #include diff --git a/HAL/JavacardSharedSecret.cpp b/HAL/JavacardSharedSecret.cpp index cc83eabd..2f4dfe4b 100644 --- a/HAL/JavacardSharedSecret.cpp +++ b/HAL/JavacardSharedSecret.cpp @@ -2,7 +2,7 @@ #include #include "JavacardSharedSecret.h" -#include +#include namespace aidl::android::hardware::security::sharedsecret { using namespace ::keymint::javacard; diff --git a/ProvisioningTool/src/cppcose/cppcose.cpp b/ProvisioningTool/src/cppcose/cppcose.cpp index e0c5aaab..29bb2015 100644 --- a/ProvisioningTool/src/cppcose/cppcose.cpp +++ b/ProvisioningTool/src/cppcose/cppcose.cpp @@ -27,9 +27,30 @@ #include #include #include +#include namespace cppcose { +ErrMsgOr ecdsaDerSignatureToCose(const bytevec& ecdsaSignature) { + const unsigned char* p = ecdsaSignature.data(); + ECDSA_SIG *sig = d2i_ECDSA_SIG(nullptr, &p, ecdsaSignature.size()); + if (sig == nullptr) { + return "Error decoding DER signature"; + } + + bytevec ecdsaCoseSignature(64, 0); + if (BN_bn2binpad(ECDSA_SIG_get0_r(sig), ecdsaCoseSignature.data(), 32) != 32) { + ECDSA_SIG_free(sig); + return "Error encoding r"; + } + if (BN_bn2binpad(ECDSA_SIG_get0_s(sig), ecdsaCoseSignature.data() + 32, 32) != 32) { + ECDSA_SIG_free(sig); + return "Error encoding s"; + } + ECDSA_SIG_free(sig); + return ecdsaCoseSignature; +} + ErrMsgOr ECDSA_sign(const bytevec& key, bytevec& input) { EVP_PKEY_CTX* pkeyCtx = NULL; EVP_MD_CTX_Ptr digestCtx(EVP_MD_CTX_new()); @@ -132,7 +153,7 @@ ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& pr .encode(); auto signature = ECDSA_sign(key, signatureInput); if (!signature) return "Signing failed"; - return signature; + return ecdsaDerSignatureToCose(*signature); } ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index e70e5ce8..fb50dce6 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -40,8 +40,8 @@ enum ProvisionStatus { }; // TODO keymint provision status and lock -std::string provisionStatusApdu = hex2str("80084000000000"); -std::string lockProvisionApdu = hex2str("80074000000000"); +std::string provisionStatusApdu = hex2str("80045000000000"); +std::string lockProvisionApdu = hex2str("80035000000000"); Json::Value root; static std::string inputFileName; diff --git a/aosp_integration_patches/device_google_cuttlefish.patch b/aosp_integration_patches/device_google_cuttlefish.patch index 825676a9..1f5e0765 100644 --- a/aosp_integration_patches/device_google_cuttlefish.patch +++ b/aosp_integration_patches/device_google_cuttlefish.patch @@ -1,8 +1,8 @@ diff --git a/shared/device.mk b/shared/device.mk -index c86840539..e61e5276b 100644 +index 54042c107..622c6a549 100644 --- a/shared/device.mk +++ b/shared/device.mk -@@ -558,6 +558,9 @@ endif +@@ -607,6 +607,9 @@ endif PRODUCT_PACKAGES += \ $(LOCAL_KEYMINT_PRODUCT_PACKAGE) @@ -13,10 +13,10 @@ index c86840539..e61e5276b 100644 ifneq ($(LOCAL_PREFER_VENDOR_APEX),true) PRODUCT_COPY_FILES += \ diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts -index cebac258e..42d3ad6f9 100644 +index 55b8d964e..a0e88fb2e 100644 --- a/shared/sepolicy/vendor/file_contexts +++ b/shared/sepolicy/vendor/file_contexts -@@ -93,6 +93,7 @@ +@@ -85,6 +85,7 @@ /vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0 /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0 /vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0 diff --git a/aosp_integration_patches/hardware_interfaces.patch b/aosp_integration_patches/hardware_interfaces.patch index e76fa74c..bd06e4cb 100644 --- a/aosp_integration_patches/hardware_interfaces.patch +++ b/aosp_integration_patches/hardware_interfaces.patch @@ -1,38 +1,17 @@ diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml -index 0b779eee4..33bd0d97d 100644 +index c39db36ae..39cce859b 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml -@@ -330,6 +330,13 @@ +@@ -355,6 +355,7 @@ + + IRemotelyProvisionedComponent default - - -+ -+ android.hardware.security.keymint -+ -+ IRemotelyProvisionedComponent + strongbox -+ -+ - - android.hardware.light - 1 -@@ -491,6 +498,14 @@ - default -+ -+ android.hardware.security.sharedsecret -+ 1 -+ -+ ISharedSecret -+ strongbox -+ -+ - - android.hardware.sensors - 1.0 + diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp -index 26ed34427..2d5bc9575 100644 +index 64550eff2..49283e8e3 100644 --- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp @@ -198,7 +198,7 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { @@ -42,9 +21,9 @@ index 26ed34427..2d5bc9575 100644 - ASSERT_EQ(ErrorCode::OK, + auto result = GenerateKey(AuthorizationSetBuilder() - .RsaSigningKey(2048, 65537) + .RsaKey(2048, 65537) .AttestKey() -@@ -209,7 +209,13 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { +@@ -209,7 +209,14 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { .Authorization(TAG_NO_AUTH_REQUIRED) .SetDefaultValidity(), {} /* attestation signing key */, &attest_key.keyBlob, @@ -56,24 +35,24 @@ index 26ed34427..2d5bc9575 100644 + return; + } + ASSERT_EQ(ErrorCode::OK, result); ++ EXPECT_GT(attest_key_cert_chain.size(), 1); verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false); -@@ -297,7 +303,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { +@@ -297,7 +304,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { attest_key_opt = attest_key; } - EXPECT_EQ(ErrorCode::OK, + auto result = GenerateKey(AuthorizationSetBuilder() - .RsaSigningKey(2048, 65537) + .RsaKey(2048, 65537) .AttestKey() -@@ -308,8 +314,13 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { +@@ -308,7 +315,14 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), attest_key_opt, &key_blob_list[i], &attested_key_characteristics, - &cert_chain_list[i])); -- + &cert_chain_list[i]); + // Strongbox does not support Factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { @@ -81,106 +60,78 @@ index 26ed34427..2d5bc9575 100644 + return; + } + ASSERT_EQ(ErrorCode::OK, result); ++ + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - ASSERT_GT(cert_chain_list[i].size(), 0); -@@ -369,7 +380,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { +@@ -369,7 +383,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { attest_key_opt = attest_key; } - EXPECT_EQ(ErrorCode::OK, + auto result = GenerateKey(AuthorizationSetBuilder() - .EcdsaSigningKey(EcCurve::P_256) + .EcdsaKey(EcCurve::P_256) .AttestKey() -@@ -380,8 +391,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { +@@ -380,7 +394,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { .Authorization(TAG_NO_AUTH_REQUIRED) .SetDefaultValidity(), attest_key_opt, &key_blob_list[i], &attested_key_characteristics, - &cert_chain_list[i])); -- + &cert_chain_list[i]); -+ // Strongbox does not support Factory provisioned attestation key. ++ // Strongbox does not support Factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); + return; + } + ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - ASSERT_GT(cert_chain_list[i].size(), 0); -@@ -442,35 +458,40 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { - attest_key.keyBlob = key_blob_list[i - 1]; +@@ -443,8 +463,9 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { attest_key_opt = attest_key; } -- + + ErrorCode result; if ((i & 0x1) == 1) { - EXPECT_EQ(ErrorCode::OK, -- GenerateKey(AuthorizationSetBuilder() -- .EcdsaSigningKey(EcCurve::P_256) -- .AttestKey() -- .AttestationChallenge("foo") -- .AttestationApplicationId("bar") -- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -- .Authorization(TAG_NO_AUTH_REQUIRED) -- .SetDefaultValidity(), -- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -- &cert_chain_list[i])); + result = -+ GenerateKey(AuthorizationSetBuilder() -+ .EcdsaSigningKey(EcCurve::P_256) -+ .AttestKey() -+ .AttestationChallenge("foo") -+ .AttestationApplicationId("bar") -+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .SetDefaultValidity(), -+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -+ &cert_chain_list[i]); + GenerateKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() +@@ -455,9 +476,9 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ &cert_chain_list[i]); } else { - EXPECT_EQ(ErrorCode::OK, -- GenerateKey(AuthorizationSetBuilder() -- .RsaSigningKey(2048, 65537) -- .AttestKey() -- .AttestationChallenge("foo") -- .AttestationApplicationId("bar") -- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -- .Authorization(TAG_NO_AUTH_REQUIRED) -- .SetDefaultValidity(), -- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -- &cert_chain_list[i])); + result = -+ GenerateKey(AuthorizationSetBuilder() -+ .RsaSigningKey(2048, 65537) -+ .AttestKey() -+ .AttestationChallenge("foo") -+ .AttestationApplicationId("bar") -+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .SetDefaultValidity(), -+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -+ &cert_chain_list[i]); - } -- + GenerateKey(AuthorizationSetBuilder() + .RsaKey(2048, 65537) + .AttestKey() +@@ -468,8 +489,14 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ &cert_chain_list[i]); ++ } + // Strongbox does not support Factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); + return; -+ } + } + ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - ASSERT_GT(cert_chain_list[i].size(), 0); diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp -index 20324117b..741bcf8f6 100644 +index 6140df135..b73d325c6 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp -@@ -1145,6 +1145,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { +@@ -1156,6 +1156,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { } } @@ -197,7 +148,7 @@ index 20324117b..741bcf8f6 100644 switch (SecLevel()) { case SecurityLevel::SOFTWARE: diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h -index ec3fcf6a3..0561a9b94 100644 +index 7b3b9d4b4..c564d509a 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -250,7 +250,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { @@ -232,10 +183,10 @@ index ec3fcf6a3..0561a9b94 100644 static vector build_params() { auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp -index 651b21f19..0e6e882d3 100644 +index 2a7911cc3..639b2a5dc 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp -@@ -908,8 +908,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { +@@ -912,8 +912,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { for (auto key_size : ValidKeySizes(Algorithm::RSA)) { vector key_blob; vector key_characteristics; @@ -246,7 +197,7 @@ index 651b21f19..0e6e882d3 100644 .RsaSigningKey(key_size, 65537) .Digest(Digest::NONE) .Padding(PaddingMode::NONE) -@@ -919,8 +919,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { +@@ -923,8 +923,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -262,7 +213,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); -@@ -1037,8 +1043,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { +@@ -1045,8 +1051,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { vector key_blob; vector key_characteristics; @@ -272,7 +223,7 @@ index 651b21f19..0e6e882d3 100644 .RsaEncryptionKey(key_size, 65537) .Padding(PaddingMode::NONE) .AttestationChallenge(challenge) -@@ -1047,8 +1052,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { +@@ -1055,8 +1060,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -288,7 +239,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); AuthorizationSet auths; for (auto& entry : key_characteristics) { -@@ -1149,15 +1160,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { +@@ -1157,15 +1168,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { vector key_blob; vector key_characteristics; @@ -313,7 +264,7 @@ index 651b21f19..0e6e882d3 100644 } /* -@@ -1267,8 +1284,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { +@@ -1275,8 +1292,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { for (auto key_size : ValidKeySizes(Algorithm::RSA)) { vector key_blob; vector key_characteristics; @@ -324,7 +275,7 @@ index 651b21f19..0e6e882d3 100644 .RsaSigningKey(key_size, 65537) .Digest(Digest::NONE) .Padding(PaddingMode::NONE) -@@ -1279,7 +1296,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { +@@ -1287,7 +1304,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -340,7 +291,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); -@@ -1410,8 +1434,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { +@@ -1418,8 +1442,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { for (auto curve : ValidCurves()) { vector key_blob; vector key_characteristics; @@ -351,7 +302,7 @@ index 651b21f19..0e6e882d3 100644 .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(curve) .Digest(Digest::NONE) -@@ -1420,7 +1444,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { +@@ -1428,7 +1452,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -368,7 +319,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); -@@ -1497,6 +1529,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { +@@ -1506,6 +1538,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { // Tag not required to be supported by all KeyMint implementations. continue; } @@ -381,7 +332,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_EQ(result, ErrorCode::OK); ASSERT_GT(key_blob.size(), 0U); -@@ -1546,8 +1584,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { +@@ -1555,8 +1593,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(); builder.push_back(tag); @@ -398,7 +349,7 @@ index 651b21f19..0e6e882d3 100644 } } -@@ -1583,6 +1627,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { +@@ -1756,6 +1800,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), &key_blob, &key_characteristics); @@ -412,7 +363,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_EQ(result, ErrorCode::OK); ASSERT_GT(key_blob.size(), 0U); -@@ -1661,13 +1712,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { +@@ -1834,13 +1885,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { vector key_blob; vector key_characteristics; @@ -435,7 +386,7 @@ index 651b21f19..0e6e882d3 100644 } /* -@@ -1724,14 +1781,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { +@@ -1897,14 +1954,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { const string app_id(length, 'a'); vector key_blob; vector key_characteristics; @@ -459,7 +410,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); -@@ -3762,25 +3826,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; +@@ -3945,25 +4009,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; * Verifies that raw RSA decryption works. */ TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { @@ -502,7 +453,7 @@ index 651b21f19..0e6e882d3 100644 } } -@@ -6297,7 +6363,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { +@@ -6503,7 +6569,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { size_t i; for (i = 0; i < max_operations; i++) { @@ -511,7 +462,7 @@ index 651b21f19..0e6e882d3 100644 if (ErrorCode::OK != result) { break; } -@@ -6305,12 +6371,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { +@@ -6511,12 +6577,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result); // Try again just in case there's a weird overflow bug EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, @@ -526,7 +477,7 @@ index 651b21f19..0e6e882d3 100644 AbortIfNeeded(); } -@@ -6409,7 +6475,6 @@ TEST_P(KeyAgreementTest, Ecdh) { +@@ -6615,7 +6681,6 @@ TEST_P(KeyAgreementTest, Ecdh) { OPENSSL_free(p); // Generate EC key in KeyMint (only access to public key material) @@ -534,7 +485,7 @@ index 651b21f19..0e6e882d3 100644 EXPECT_EQ( ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() -@@ -6418,7 +6483,6 @@ TEST_P(KeyAgreementTest, Ecdh) { +@@ -6624,7 +6689,6 @@ TEST_P(KeyAgreementTest, Ecdh) { .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) .Authorization(TAG_ALGORITHM, Algorithm::EC) .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) @@ -542,7 +493,7 @@ index 651b21f19..0e6e882d3 100644 .SetDefaultValidity())) << "Failed to generate key"; ASSERT_GT(cert_chain_.size(), 0); -@@ -6498,14 +6562,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { +@@ -6704,14 +6768,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { @@ -569,7 +520,7 @@ index 651b21f19..0e6e882d3 100644 } /* -@@ -6521,14 +6595,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { +@@ -6727,14 +6801,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { }); for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { @@ -594,7 +545,7 @@ index 651b21f19..0e6e882d3 100644 /* diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp -index 38f358686..74e44c7b4 100644 +index 76fb79b61..6e57d913b 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -164,6 +164,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "OmapiTransport.h" ++ ++namespace keymint::javacard { ++ ++class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; ++ ++bool OmapiTransport::initialize() { ++ std::vector readers = {}; ++ ++ LOG(DEBUG) << "Initialize the secure element connection"; ++ ++ // Get OMAPI vendor stable service handler ++ ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(omapiServiceName)); ++ omapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); ++ ++ if (omapiSeService == nullptr) { ++ LOG(ERROR) << "Failed to start omapiSeService null"; ++ return false; ++ } ++ ++ // reset readers, clear readers if already existing ++ if (mVSReaders.size() > 0) { ++ closeConnection(); ++ } ++ ++ // Get available readers ++ auto status = omapiSeService->getReaders(&readers); ++ if (!status.isOk()) { ++ LOG(ERROR) << "getReaders failed to get available readers: " << status.getMessage(); ++ return false; ++ } ++ ++ // Get SE readers handlers ++ for (auto readerName : readers) { ++ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; ++ status = omapiSeService->getReader(readerName, &reader); ++ if (!status.isOk()) { ++ LOG(ERROR) << "getReader for " << readerName.c_str() << " Failed: " ++ << status.getMessage(); ++ return false; ++ } ++ ++ mVSReaders[readerName] = reader; ++ } ++ ++ // Find eSE reader, as of now assumption is only eSE available on device ++ LOG(DEBUG) << "Finding eSE reader"; ++ eSEReader = nullptr; ++ if (mVSReaders.size() > 0) { ++ for (const auto& [name, reader] : mVSReaders) { ++ if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) { ++ LOG(DEBUG) << "eSE reader found: " << name; ++ eSEReader = reader; ++ } ++ } ++ } ++ ++ if (eSEReader == nullptr) { ++ LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; ++ return false; ++ } ++ ++ return true; ++} ++ ++bool OmapiTransport::internalTransmitApdu( ++ std::shared_ptr reader, ++ std::vector apdu, std::vector& transmitResponse) { ++ std::shared_ptr session; ++ std::shared_ptr channel; ++ auto mSEListener = std::make_shared(); ++ std::vector selectResponse = {}; ++ std::vector SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, ++ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}; ++ ++ LOG(DEBUG) << "internalTransmitApdu: trasmitting data to secure element"; ++ ++ if (reader == nullptr) { ++ LOG(ERROR) << "eSE reader is null"; ++ return false; ++ } ++ ++ bool status = false; ++ auto res = reader->isSecureElementPresent(&status); ++ if (!res.isOk()) { ++ LOG(ERROR) << "isSecureElementPresent error: " << res.getMessage(); ++ return false; ++ } ++ if (!status) { ++ LOG(ERROR) << "secure element not found"; ++ return false; ++ } ++ ++ res = reader->openSession(&session); ++ if (!res.isOk()) { ++ LOG(ERROR) << "openSession error: " << res.getMessage(); ++ return false; ++ } ++ if (session == nullptr) { ++ LOG(ERROR) << "Could not open session null"; ++ return false; ++ } ++ ++ res = session->openLogicalChannel(SELECTABLE_AID, 0x00, mSEListener, &channel); ++ if (!res.isOk()) { ++ LOG(ERROR) << "openLogicalChannel error: " << res.getMessage(); ++ return false; ++ } ++ if (channel == nullptr) { ++ LOG(ERROR) << "Could not open channel null"; ++ return false; ++ } ++ ++ res = channel->getSelectResponse(&selectResponse); ++ if (!res.isOk()) { ++ LOG(ERROR) << "getSelectResponse error: " << res.getMessage(); ++ return false; ++ } ++ if (selectResponse.size() < 2) { ++ LOG(ERROR) << "getSelectResponse size error"; ++ return false; ++ } ++ ++ res = channel->transmit(apdu, &transmitResponse); ++ if (channel != nullptr) channel->close(); ++ if (session != nullptr) session->close(); ++ ++ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() << " Message: " ++ << res.getMessage(); ++ if (!res.isOk()) { ++ LOG(ERROR) << "transmit error: " << res.getMessage(); ++ return false; ++ } ++ ++ return true; ++} ++ ++bool OmapiTransport::openConnection() { ++ ++ // if already conection setup done, no need to initialise it again. ++ if (isConnected()) { ++ return true; ++ } ++ ++ return initialize(); ++} ++ ++bool OmapiTransport::sendData(const vector& inData, vector& output) { ++ ++ if (!isConnected()) { ++ // Try to initialize connection to eSE ++ LOG(INFO) << "Failed to send data, try to initialize connection SE connection"; ++ if (!initialize()) { ++ LOG(ERROR) << "Failed to send data, initialization not completed"; ++ closeConnection(); ++ return false; ++ } ++ } ++ ++ if (eSEReader != nullptr) { ++ LOG(DEBUG) << "Sending apdu data to secure element: " << ESE_READER_PREFIX; ++ return internalTransmitApdu(eSEReader, inData, output); ++ } else { ++ LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; ++ return false; ++ } ++} ++ ++bool OmapiTransport::closeConnection() { ++ LOG(DEBUG) << "Closing all connections"; ++ if (omapiSeService != nullptr) { ++ if (mVSReaders.size() > 0) { ++ for (const auto& [name, reader] : mVSReaders) { ++ reader->closeSessions(); ++ } ++ mVSReaders.clear(); ++ } ++ } ++ return true; ++} ++ ++bool OmapiTransport::isConnected() { ++ // Check already initialization completed or not ++ if (omapiSeService != nullptr && eSEReader != nullptr) { ++ LOG(DEBUG) << "Connection initialization already completed"; ++ return true; ++ } ++ ++ LOG(DEBUG) << "Connection initialization not completed"; ++ return false; ++} ++ ++} +diff --git a/HAL/OmapiTransport.h b/HAL/OmapiTransport.h +new file mode 100644 +index 0000000..f7711e7 +--- /dev/null ++++ b/HAL/OmapiTransport.h +@@ -0,0 +1,61 @@ ++#pragma once ++ ++#include "ITransport.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace keymint::javacard { ++using std::vector; ++ ++/** ++ * OmapiTransport is derived from ITransport. This class gets the OMAPI service binder instance and uses IPC to ++ * communicate with OMAPI service. OMAPI inturn communicates with hardware via ISecureElement. ++ */ ++class OmapiTransport : public ITransport { ++ ++public: ++ ++ /** ++ * Gets the binder instance of ISEService, gets the reader corresponding to secure element, establishes a session ++ * and opens a basic channel. ++ */ ++ bool openConnection() override; ++ /** ++ * Transmists the data over the opened basic channel and receives the data back. ++ */ ++ bool sendData(const vector& inData, vector& output) override; ++ ++ /** ++ * Closes the connection. ++ */ ++ bool closeConnection() override; ++ /** ++ * Returns the state of the connection status. Returns true if the connection is active, false if connection is ++ * broken. ++ */ ++ bool isConnected() override; ++ ++private: ++ std::shared_ptr omapiSeService = nullptr; ++ std::shared_ptr eSEReader = nullptr; ++ std::map> ++ mVSReaders = {}; ++ std::string const ESE_READER_PREFIX = "eSE"; ++ constexpr static const char omapiServiceName[] = ++ "android.system.omapi.ISecureElementService/default"; ++ ++ bool initialize(); ++ bool internalTransmitApdu( ++ std::shared_ptr reader, ++ std::vector apdu, std::vector& transmitResponse); ++}; ++ ++} +\ No newline at end of file +diff --git a/HAL/service.cpp b/HAL/service.cpp +index 3d51877..14580f8 100644 +--- a/HAL/service.cpp ++++ b/HAL/service.cpp +@@ -22,18 +22,24 @@ + + #include "JavacardKeyMintDevice.h" + #include +- ++#include + #include "JavacardSecureElement.h" + #include "JavacardSharedSecret.h" + #include "keymint_utils.h" + #include "JavacardRemotelyProvisionedComponentDevice.h" + #include ++#include + + using aidl::android::hardware::security::keymint::JavacardKeyMintDevice; + using aidl::android::hardware::security::keymint::JavacardSharedSecret; + using aidl::android::hardware::security::keymint::SecurityLevel; + using namespace keymint::javacard; + ++#define PROP_BUILD_QEMU "ro.kernel.qemu" ++#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint" ++// Cuttlefish build fingerprint substring. ++#define CUTTLEFISH_FINGERPRINT_SS "aosp_cf_" ++ + template std::shared_ptr addService(Args&&... args) { + std::shared_ptr ser = ndk::SharedRefBase::make(std::forward(args)...); + auto instanceName = std::string(T::descriptor) + "/strongbox"; +@@ -44,11 +50,31 @@ template std::shared_ptr addService(Args&&... arg + return ser; + } + ++std::shared_ptr getTransportInstance() { ++ bool isEmulator = false; ++ // Check if the current build is for emulator or device. ++ isEmulator = android::base::GetBoolProperty(PROP_BUILD_QEMU, false); ++ if (!isEmulator) { ++ std::string fingerprint = android::base::GetProperty(PROP_BUILD_FINGERPRINT, ""); ++ if (!fingerprint.empty()) { ++ if (fingerprint.find(CUTTLEFISH_FINGERPRINT_SS, 0) != std::string::npos) { ++ isEmulator = true; ++ } ++ } ++ } ++ ++ if (!isEmulator) { ++ return std::make_shared(); ++ } else { ++ return std::make_shared(); ++ } ++} ++ + int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Javacard Secure Element + std::shared_ptr card = +- std::make_shared(std::make_shared(), getOsVersion(), ++ std::make_shared(getTransportInstance(), getOsVersion(), + getOsPatchlevel(), getVendorPatchlevel()); + // Add Keymint Service + addService(card); diff --git a/aosp_integration_patches/omapi_patches/packages_apps_SecureElement.patch b/aosp_integration_patches/omapi_patches/packages_apps_SecureElement.patch new file mode 100644 index 00000000..68879424 --- /dev/null +++ b/aosp_integration_patches/omapi_patches/packages_apps_SecureElement.patch @@ -0,0 +1,25 @@ +diff --git a/Android.bp b/Android.bp +index f86ad26..afea5c6 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -42,6 +42,9 @@ android_app { + "src/**/*.java", + ":statslog-secure-element-java-gen", + ], ++ vintf_fragments: [ ++ "secure_element-service.xml", ++ ], + platform_apis: true, + certificate: "platform", + static_libs: ["android.hardware.secure_element-V1.0-java", +diff --git a/res/values/config.xml b/res/values/config.xml +index 5811b10..da6e50e 100644 +--- a/res/values/config.xml ++++ b/res/values/config.xml +@@ -6,5 +6,5 @@ + + +- false ++ true + diff --git a/aosp_integration_patches/system_sepolicy.patch b/aosp_integration_patches/system_sepolicy.patch index e9240a37..3e0ce8d5 100644 --- a/aosp_integration_patches/system_sepolicy.patch +++ b/aosp_integration_patches/system_sepolicy.patch @@ -1,12 +1,20 @@ diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te -index faec07420..e73a47849 100644 +index e77ea9df0..74f799681 100644 --- a/public/hal_neverallows.te +++ b/public/hal_neverallows.te -@@ -39,6 +39,7 @@ neverallow { - -hal_wifi_supplicant_server - -hal_telephony_server - -hal_uwb_server +@@ -2,6 +2,7 @@ + # network capabilities + neverallow { + halserverdomain + -hal_keymint_server - } { - domain - userdebug_or_eng(`-su') + -hal_bluetooth_server + -hal_can_controller_server + -hal_wifi_server +@@ -37,6 +38,7 @@ neverallow { + + neverallow { + halserverdomain ++ -hal_keymint_server + -hal_automotive_socket_exemption + -hal_can_controller_server + -hal_tetheroffload_server