From 1dd1894182f5d15cf5a6f4b53438d62a1d867593 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Thu, 21 Oct 2021 13:34:41 +0000 Subject: [PATCH 01/33] 1. Corrected the HardwareAuthToken and VerificationToken validation. 2. Able to generate key when both FINGERPRINT and BIOMETRIC were passed as USER_AUTH types. 3. USER_SECURE_ID and NO_AUTH_REQUIRED Mutual exclusive. --- .../android/javacard/keymaster/KMUtils.java | 29 +- .../android/javacard/keymaster/KMUtils.java | 30 +- Applet/README.md | 2 + .../android/javacard/keymaster/KMEnumTag.java | 3 +- .../android/javacard/keymaster/KMInteger.java | 9 + .../javacard/keymaster/KMIntegerArrayTag.java | 13 +- .../javacard/keymaster/KMKeymasterApplet.java | 164 ++--- .../javacard/keymaster/KMOperationState.java | 69 ++- .../4.1/JavacardKeymaster4Device.cpp | 558 ++++++++++-------- .../include/JavacardKeymaster4Device.h | 35 +- ProvisioningTool/src/provision.cpp | 13 + 11 files changed, 578 insertions(+), 347 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index c2b5c7f3..49ddd16d 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -303,6 +303,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; @@ -343,7 +351,9 @@ public static void add(byte[] buf, short op1, short op2, short result) { byte carry = 0; short tmp; while (index >= 0) { - tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); + tmp = + (short) ((buf[(short) (op1 + index)] & 0xFF) + + (buf[(short) (op2 + index)] & 0xFF) + carry); carry = 0; if (tmp > 255) { carry = 1; // max unsigned byte value is 255 @@ -409,4 +419,19 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { return -1; } -} \ No newline at end of file + //i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) + public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, + short scratchPadOff) { + byte[] shiftPos = {9, 8, 7, 6, 5, 3}; + short index = 0; + while (index < (short) (shiftPos.length)) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + shiftLeft(buf, scratchPadOff, shiftPos[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/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index c2b5c7f3..88b7b4d1 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -303,6 +303,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; @@ -343,7 +351,9 @@ public static void add(byte[] buf, short op1, short op2, short result) { byte carry = 0; short tmp; while (index >= 0) { - tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); + tmp = + (short) ((buf[(short) (op1 + index)] & 0xFF) + + (buf[(short) (op2 + index)] & 0xFF) + carry); carry = 0; if (tmp > 255) { carry = 1; // max unsigned byte value is 255 @@ -409,4 +419,20 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { return -1; } -} \ No newline at end of file + // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) + public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, + short scratchPadOff) { + byte[] shiftPos = {9, 8, 7, 6, 5, 3}; + short index = 0; + while (index < (short) (shiftPos.length)) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + shiftLeft(buf, scratchPadOff, shiftPos[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/README.md b/Applet/README.md index 76af5b05..064fc9d9 100644 --- a/Applet/README.md +++ b/Applet/README.md @@ -17,3 +17,5 @@ which serves to intermediate between Android Keystore and this applet. - Install Javacard 3.0.5 classic sdk. - set JC_HOME_SIMULATOR environment variable to the installed sdk. - Give ant build from Applet folder. +- Download [gpapi-upgrade.jar](https://globalplatform.wpengine.com/specs-library/globalplatform-card-api-org-globalplatform-upgrade-v1/) and copy inside lib folder of both AndroidSEProvider and JCardSimProvider to resolve the compilation errors. + diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 7493aa3d..0e45414e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -106,8 +106,7 @@ public static void create() { new byte[]{RSA, DES, EC, AES, HMAC}, new byte[]{P_224, P_256, P_384, P_521}, 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/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index aee6f9d5..fd080198 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -147,6 +147,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) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + 2)); } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index e292c5e6..558e44e2 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); @@ -134,6 +134,17 @@ private static boolean validateTagType(short tagType) { return (tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG); } + public boolean contains(short tagValue) { + short index = 0; + while (index < length()) { + if (KMInteger.compare(tagValue, get(index)) == 0) { + return true; + } + index++; + } + return false; + } + public static boolean contains(short tagId, short tagValue, short params) { short tag = KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 0949177f..29413a45 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1968,7 +1968,7 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authenticateUser(); + authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad); } } @@ -2015,7 +2015,7 @@ private void validateVerificationToken(KMOperationState op, short verToken, byte validateVerificationToken(verToken, scratchPad); // validate operation handle. ptr = KMVerificationToken.cast(verToken).getChallenge(); - if (op.getHandle() != KMInteger.cast(ptr).getShort()) { + if (KMInteger.compare(ptr, op.getHandle()) != 0) { KMException.throwIt(KMError.VERIFICATION_FAILED); } } @@ -2529,10 +2529,7 @@ 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); + authorizeUserSecureIdAuthTimeout(op, scratchPad); authorizeDeviceUnlock(data[HW_TOKEN]); // 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 @@ -2729,55 +2726,83 @@ private void beginSignVerifyOperation(KMOperationState op) { } } - private void authorizeUserSecureIdAuthTimeout(KMOperationState op) { + private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { short authTime; + short authType; // Authorize User Secure Id and Auth timeout - tmpVariables[0] = + short userSecureIdPtr = KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = + 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); + } + // store authenticator type + 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 (tmpVariables[0] != 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(tmpVariables[0]).getValue(); + if (authTimeoutTagPtr != KMType.INVALID_VALUE) { // authenticate user - authenticateUser(); + authTokenMatches(userSecureIdPtr, authType, scratchPad); + + 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 = + addAuthTimeToTimeStamp(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() { - tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } + 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 void authTokenMatches(short userSecureIdsPtr, short authType, + byte[] scratchPad) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - // check user secure id - if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { + if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } // check auth type - tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { + if (((byte) tmpVariables[2] & (byte) authType) == 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } @@ -2795,18 +2820,15 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { len = 1; // concatenate challenge - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); len += 8; // concatenate user id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); len += 8; // concatenate authenticator id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); len += 8; // concatenate authenticator type - 4 bytes ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); @@ -3079,6 +3101,12 @@ private void importTDESKey(byte[] scratchPad) { data[KEY_BLOB] = KMArray.instance((short) 4); } + private void validateAesKeySize(short keySizeBits) { + if (keySizeBits != 128 && keySizeBits != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + private void importAESKey(byte[] scratchPad) { // Get Key tmpVariables[0] = KMArray.instance((short) 1); @@ -3093,17 +3121,18 @@ private void importAESKey(byte[] scratchPad) { // create 128 or 256 bit AES key tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. - tmpVariables[2] = + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 128 && tmpVariables[2] != 256) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } + + if (keysize != KMType.INVALID_VALUE) { + validateAesKeySize(keysize); } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + keysize = (short) ( 8 * KMByteBlob.cast(data[SECRET]).length()); + validateAesKeySize(keysize); + keysize = KMInteger.uint_16(keysize); + short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, tmpVariables[4], keysizeTag); tmpVariables[4] += 2; } // Check whether key can be created @@ -3929,38 +3958,29 @@ private static void sendError(APDU apdu, short err) { sendOutgoing(apdu); } - 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 addAuthTimeToTimeStamp(short authTime, short timeStamp, byte[] scratchPad) { + // Convert authTime to milliseconds + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 40, (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(authTime).length()), + KMInteger.cast(authTime).length()); + KMUtils.convertToMilliseconds(scratchPad, (short) 0, (short) 8, (short) 16); + + // Copy timestamp to scratchpad + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); 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)); - } - - private void add(byte[] buf, short op1, short op2, short result) { - byte index = 7; - byte carry = 0; - short tmp; - while (index >= 0) { - tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); - carry = 0; - if (tmp > 255) { - carry = 1; // max unsigned byte value is 255 - } - buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF); - index--; - } + (short) (8 - KMInteger.cast(timeStamp).length()), + KMInteger.cast(timeStamp).length()); + + // add authTime in millis to timestamp. + Util.arrayFillNonAtomic(scratchPad, (short) 16, (short) 8, (byte) 0); + KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); + return KMInteger.uint_64(scratchPad, (short) 16); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index d8705de4..85a346e6 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,6 +16,8 @@ package com.android.javacard.keymaster; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -27,7 +29,7 @@ */ public class KMOperationState { - public static final byte MAX_DATA = 20; + public static final byte MAX_DATA = 63; private static final byte OPERATION = 0; private static final byte TRUE = 1; private static final byte FALSE = 0; @@ -38,18 +40,23 @@ public class KMOperationState { private static final byte BLOCKMODE = 3; private static final byte DIGEST = 4; private static final byte FLAGS = 5; + private static final byte AUTH_TYPE = 6; // short type - private static final byte KEY_SIZE = 6; - private static final byte MAC_LENGTH = 8; + private static final byte KEY_SIZE = 7; + private static final byte MAC_LENGTH = 9; // Handle - currently this is short - private static final byte OP_HANDLE = 10; + private static final byte OP_HANDLE = 11; // Auth time 64 bits - private static final byte AUTH_TIME = 12; + private static final byte AUTH_TIME = 13; + // 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. + private static final byte USER_SECURE_ID = 21; // Flag masks private static final byte AUTH_PER_OP_REQD = 1; private static final byte SECURE_USER_ID_REQD = 2; private static final byte AUTH_TIMEOUT_VALIDATED = 4; private static final byte AES_GCM_UPDATE_ALLOWED = 8; + private static final byte MAX_SECURE_USER_IDS = 5; // Object References private byte[] data; @@ -165,6 +172,58 @@ public void setAuthTime(byte[] timeBuf, short start) { dataUpdated(); } + public void setAuthType(byte authType) { + data[AUTH_TYPE] = authType; + dataUpdated(); + } + + public short getAuthType() { + return data[AUTH_TYPE]; + } + + public short getUserSecureId() { + short offset = USER_SECURE_ID; + short length = Util.getShort(data, USER_SECURE_ID); + if (length == 0) { + return KMType.INVALID_VALUE; + } + short arrObj = KMArray.instance(length); + short index = 0; + short obj; + offset = (short) (2 + USER_SECURE_ID); + while (index < length) { + obj = KMInteger.instance(data, (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(data, USER_SECURE_ID, (short) (MAX_SECURE_USER_IDS * 8) , (byte) 0); + short index = 0; + short obj; + short offset = USER_SECURE_ID; + Util.setShort(data, offset, length); + offset += 2; + while (index < length) { + obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index); + Util.arrayCopy( + KMInteger.cast(obj).getBuffer(), + KMInteger.cast(obj).getStartOff(), + data, + (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index), + KMInteger.cast(obj).length() + ); + index++; + } + dataUpdated(); + } + public void setOneTimeAuthReqd(boolean flag) { if (flag) { data[FLAGS] = (byte) (data[FLAGS] | SECURE_USER_ID_REQD); diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index 87daddbc..46a12557 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -63,10 +63,11 @@ namespace javacard { static std::unique_ptr pTransportFactory = nullptr; constexpr size_t kOperationTableSize = 4; -/* Key is the newly generated operation handle. Value is a pair with first element having - * original operation handle and second element represents SW or SB operation. +/* + * Key is the operation handle generated by either SoftKM or StrongboxKM and + * value is either PUBLIC_OPERATION or PRIVATE_OPERATION */ -std::map> operationTable; +std::map operationTable; struct KM_AUTH_LIST_Delete { void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); } @@ -198,61 +199,31 @@ static T translateExtendedErrorsToHalErrors(T& errorCode) { return err; } -/* Generate new operation handle */ -static ErrorCode generateOperationHandle(uint64_t& oprHandle) { - std::map>::iterator it; - do { - keymaster_error_t err = GenerateRandom(reinterpret_cast(&oprHandle), (size_t)sizeof(oprHandle)); - if (err != KM_ERROR_OK) { - return legacy_enum_conversion(err); - } - it = operationTable.find(oprHandle); - } while (it != operationTable.end()); - return ErrorCode::OK; -} - -/* Create a new operation handle entry in operation table.*/ -static ErrorCode createOprHandleEntry(uint64_t origOprHandle, uint64_t keymasterSrc, uint64_t& newOperationHandle) { - ErrorCode errorCode = ErrorCode::OK; - if (ErrorCode::OK != (errorCode = generateOperationHandle(newOperationHandle))) { - return errorCode; - } - operationTable[newOperationHandle] = std::make_pair(origOprHandle, keymasterSrc); - return errorCode; -} - -/* Get original operation handle generated by softkeymaster/strongboxkeymaster. */ -static ErrorCode getOrigOperationHandle(uint64_t halGeneratedOperationHandle, uint64_t& origOprHandle) { - std::map>::iterator it = operationTable.find(halGeneratedOperationHandle); - if (it == operationTable.end()) { - return ErrorCode::INVALID_OPERATION_HANDLE; +/* Returns true if operation handle exists, otherwise false */ +static inline bool isOperationHandleExists(uint64_t opHandle) { + if (operationTable.end() == operationTable.find(opHandle)) { + return false; } - origOprHandle = it->second.first; - return ErrorCode::OK; + return true; } -/* Tells if the operation handle belongs to strongbox keymaster. */ -static bool isStrongboxOperation(uint64_t halGeneratedOperationHandle) { - std::map>::iterator it = operationTable.find(halGeneratedOperationHandle); +static inline OperationType getOperationType(uint64_t operationHandle) { + auto it = operationTable.find(operationHandle); if (it == operationTable.end()) { - return false; + return OperationType::UNKNOWN; } - return (SB_KM_OPR == it->second.second); -} - -/* Delete the operation handle entry from operation table. */ -static void deleteOprHandleEntry(uint64_t halGeneratedOperationHandle) { - operationTable.erase(halGeneratedOperationHandle); + return it->second; } /* Clears all the strongbox operation handle entries from operation table */ static void clearStrongboxOprHandleEntries(const std::unique_ptr& oprCtx) { - LOG(INFO) << "Secure Element reset or applet upgrade detected. Removing existing operation handles"; + LOG(INFO) + << "Secure Element reset or applet upgrade detected. Removing existing operation handles"; auto it = operationTable.begin(); while (it != operationTable.end()) { - if (it->second.second == SB_KM_OPR) { //Strongbox operation + if (it->second == OperationType::PRIVATE_OPERATION) { // Strongbox operation LOG(INFO) << "operation handle: " << it->first << " is removed"; - oprCtx->clearOperationData(it->second.first); + oprCtx->clearOperationData(it->first); it = operationTable.erase(it); } else { ++it; @@ -1010,53 +981,85 @@ Return JavacardKeymaster4Device::destroyAttestationIds() { Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, const HardwareAuthToken& authToken, begin_cb _hidl_cb) { ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - hidl_vec outParams; uint64_t operationHandle = 0; - hidl_vec resultParams; - uint64_t generatedOpHandle = 0; - - if(keyBlob.size() == 0) { - LOG(ERROR) << "Error in INS_BEGIN_OPERATION_CMD, keyblob size is 0"; - _hidl_cb(ErrorCode::INVALID_ARGUMENT, resultParams, operationHandle); - return Void(); - } - /* Asymmetric public key operations like RSA Verify, RSA Encrypt, ECDSA verify - * are handled by softkeymaster. + OperationType operType = OperationType::PRIVATE_OPERATION; + hidl_vec outParams; + LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; + /* + * Asymmetric public key operations are processed inside softkeymaster and private + * key operations are processed inside strongbox keymaster. + * All symmetric key operations are processed inside strongbox keymaster. + * If the purpose is either ENCRYPT / VERIFY then the operation type is set + * to public operation and in case if the key turned out to be a symmetric key then + * handleBeginOperation() function fallbacks to private key operation. */ LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; if (KeyPurpose::ENCRYPT == purpose || KeyPurpose::VERIFY == purpose) { - BeginOperationRequest request(softKm_->message_version()); - request.purpose = legacy_enum_conversion(purpose); - request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - BeginOperationResponse response(softKm_->message_version()); - /* For Symmetric key operation, the BeginOperation returns KM_ERROR_INCOMPATIBLE_ALGORITHM error. */ - softKm_->BeginOperation(request, &response); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD softkm BeginOperation status: " << (int32_t) errorCode; - if (errorCode != ErrorCode::OK) - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in softkm BeginOperation status: " << (int32_t) errorCode; - - if (response.error == KM_ERROR_OK) { - resultParams = kmParamSet2Hidl(response.output_params); - } - if (response.error != KM_ERROR_INCOMPATIBLE_ALGORITHM) { /*Incompatible algorithm could be handled by JavaCard*/ - errorCode = legacy_enum_conversion(response.error); - /* Create a new operation handle and add a entry inside the operation table map with - * key - new operation handle - * value - hal generated operation handle. - */ - if (errorCode == ErrorCode::OK) { - errorCode = createOprHandleEntry(response.op_handle, SW_KM_OPR, generatedOpHandle); - if (errorCode != ErrorCode::OK) - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error while creating new operation handle: " << (int32_t) errorCode; + operType = OperationType::PUBLIC_OPERATION; + } + errorCode = handleBeginOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle, operType); + if (errorCode == ErrorCode::OK && isOperationHandleExists(operationHandle)) { + LOG(DEBUG) << "Operation handle " << operationHandle + << "already exists" + "in the opertion table. so aborting this opertaion."; + // abort the operation. + errorCode = abortOperation(operationHandle, operType); + if (errorCode == ErrorCode::OK) { + // retry begin to get an another operation handle. + errorCode = handleBeginOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle, operType); + if (errorCode == ErrorCode::OK && isOperationHandleExists(operationHandle)) { + errorCode = ErrorCode::UNKNOWN_ERROR; + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: Failed in begin operation as the" + "operation handle already exists in the operation table." + << (int32_t)errorCode; + // abort the operation. + auto abortErr = abortOperation(operationHandle, operType); + if (abortErr != ErrorCode::OK) { + LOG(ERROR) << "Fail to abort the operation."; + errorCode = abortErr; + } } - _hidl_cb(errorCode, resultParams, generatedOpHandle); - return Void(); } } + // Create an entry inside the operation table for the new operation + // handle. + if (ErrorCode::OK == errorCode) operationTable[operationHandle] = operType; + + _hidl_cb(errorCode, outParams, operationHandle); + return Void(); +} +ErrorCode JavacardKeymaster4Device::handleBeginPublicKeyOperation( + KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, + hidl_vec& outParams, uint64_t& operationHandle) { + BeginOperationRequest request(softKm_->message_version()); + request.purpose = legacy_enum_conversion(purpose); + request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); + request.additional_params.Reinitialize(KmParamSet(inParams)); + + BeginOperationResponse response(softKm_->message_version()); + /* For Symmetric key operation, the BeginOperation returns + * KM_ERROR_INCOMPATIBLE_ALGORITHM error. */ + softKm_->BeginOperation(request, &response); + ErrorCode errorCode = legacy_enum_conversion(response.error); + LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD softkm BeginOperation status: " << (int32_t)errorCode; + if (ErrorCode::OK == errorCode) { + outParams = kmParamSet2Hidl(response.output_params); + operationHandle = response.op_handle; + } else { + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in softkm BeginOperation status: " + << (int32_t)errorCode; + } + return errorCode; +} + +ErrorCode JavacardKeymaster4Device::handleBeginPrivateKeyOperation( + KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, + const HardwareAuthToken& authToken, hidl_vec& outParams, + uint64_t& operationHandle) { + ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; cppbor::Array array; std::vector cborOutData; std::unique_ptr item; @@ -1071,78 +1074,107 @@ Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec< cborConverter_.addHardwareAuthToken(array, authToken); std::vector cborData = array.encode(); - // keyCharacteristics.hardwareEnforced is required to store algorithm, digest and padding values in operationInfo - // structure. To retrieve keyCharacteristics.hardwareEnforced, call getKeyCharacateristics. - // By calling getKeyCharacateristics also helps in finding a corrupted keyblob. + // keyCharacteristics.hardwareEnforced is required to store algorithm, digest + // and padding values in operationInfo structure. To retrieve + // keyCharacteristics.hardwareEnforced, call getKeyCharacateristics. By + // calling getKeyCharacateristics also helps in finding a corrupted keyblob. hidl_vec applicationId; hidl_vec applicationData; - if(getTag(inParams, Tag::APPLICATION_ID, param)) { + if (getTag(inParams, Tag::APPLICATION_ID, param)) { applicationId = param.blob; } - if(getTag(inParams, Tag::APPLICATION_DATA, param)) { + if (getTag(inParams, Tag::APPLICATION_DATA, param)) { applicationData = param.blob; } - //Call to getKeyCharacteristics. + // Call to getKeyCharacteristics. getKeyCharacteristics(keyBlob, applicationId, applicationData, - [&](ErrorCode error, KeyCharacteristics keyChars) { - errorCode = error; - keyCharacteristics = keyChars; - }); - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD getKeyCharacteristics status: " << (int32_t) errorCode; - - if(errorCode == ErrorCode::OK) { + [&](ErrorCode error, KeyCharacteristics keyChars) { + errorCode = error; + keyCharacteristics = keyChars; + }); + LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD StrongboxKM getKeyCharacteristics status: " + << (int32_t)errorCode; + + if (errorCode == ErrorCode::OK) { errorCode = ErrorCode::UNKNOWN_ERROR; - if(getTag(keyCharacteristics.hardwareEnforced, Tag::ALGORITHM, param)) { + if (getTag(keyCharacteristics.hardwareEnforced, Tag::ALGORITHM, param)) { errorCode = sendData(Instruction::INS_BEGIN_OPERATION_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); + if (errorCode == ErrorCode::OK) { + // Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = + decodeData(cborConverter_, + std::vector(cborOutData.begin(), cborOutData.end() - 2), + true, oprCtx_); if (item != nullptr) { - if(!cborConverter_.getKeyParameters(item, 1, outParams) || - !cborConverter_.getUint64(item, 2, operationHandle)) { + if (!cborConverter_.getKeyParameters(item, 1, outParams) || + !cborConverter_.getUint64(item, 2, operationHandle)) { errorCode = ErrorCode::UNKNOWN_ERROR; outParams.setToExternal(nullptr, 0); operationHandle = 0; - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: error in converting cbor data, status: " << (int32_t) errorCode; + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: error in converting cbor " + "data, status: " + << (int32_t)errorCode; } else { /* Store the operationInfo */ - oprCtx_->setOperationInfo(operationHandle, purpose, param.f.algorithm, inParams); + oprCtx_->setOperationInfo(operationHandle, purpose, param.f.algorithm, + inParams); } } } } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD couldn't find algorithm tag: " << (int32_t)Tag::ALGORITHM; + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD couldn't find algorithm tag: " + << (int32_t)Tag::ALGORITHM; } } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in getKeyCharacteristics status: " << (int32_t) errorCode; + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in getKeyCharacteristics status: " + << (int32_t)errorCode; } - /* Create a new operation handle and add a entry inside the operation table map with - * key - new operation handle - * value - hal generated operation handle. - */ - if (ErrorCode::OK == errorCode) - errorCode = createOprHandleEntry(operationHandle, SB_KM_OPR, generatedOpHandle); + return errorCode; +} - _hidl_cb(errorCode, outParams, generatedOpHandle); - return Void(); +ErrorCode JavacardKeymaster4Device::handleBeginOperation( + KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, + const HardwareAuthToken& authToken, hidl_vec& outParams, + uint64_t& operationHandle, OperationType& operType) { + ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; + if (operType == OperationType::PUBLIC_OPERATION) { + errorCode = + handleBeginPublicKeyOperation(purpose, keyBlob, inParams, outParams, operationHandle); + + // For Symmetric operations handleBeginPublicKeyOperation function + // returns INCOMPATIBLE_ALGORITHM error. Based on this error + // condition it fallbacks to private key operation. + if (errorCode == ErrorCode::INCOMPATIBLE_ALGORITHM) { + operType = OperationType::PRIVATE_OPERATION; + } + } + + if (operType == OperationType::PRIVATE_OPERATION) { + errorCode = handleBeginPrivateKeyOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle); + } + return errorCode; } -Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, const hidl_vec& inParams, const hidl_vec& input, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, update_cb _hidl_cb) { +Return +JavacardKeymaster4Device::update(uint64_t operationHandle, const hidl_vec& inParams, + const hidl_vec& input, const HardwareAuthToken& authToken, + const VerificationToken& verificationToken, update_cb _hidl_cb) { ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; uint32_t inputConsumed = 0; hidl_vec outParams; hidl_vec output; - uint64_t operationHandle; UpdateOperationResponse response(softKm_->message_version()); - if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" - << " secure element reset occurred."; - _hidl_cb(errorCode, inputConsumed, outParams, output); + OperationType operType = getOperationType(operationHandle); + if (OperationType::UNKNOWN == operType) { // operation handle not found + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle " + "is passed or if" + << " secure element reset occurred."; + _hidl_cb(ErrorCode::INVALID_OPERATION_HANDLE, inputConsumed, outParams, output); return Void(); } - if (!isStrongboxOperation(halGeneratedOprHandle)) { + if (OperationType::PUBLIC_OPERATION == operType) { /* SW keymaster (Public key operation) */ LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm operation "; UpdateOperationRequest request(softKm_->message_version()); @@ -1153,14 +1185,14 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co softKm_->UpdateOperation(request, &response); errorCode = legacy_enum_conversion(response.error); LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm update operation status: " - << (int32_t) errorCode; + << (int32_t)errorCode; if (response.error == KM_ERROR_OK) { inputConsumed = response.input_consumed; outParams = kmParamSet2Hidl(response.output_params); output = kmBuffer2hidlVec(response.output); } else { - LOG(ERROR) << "INS_UPDATE_OPERATION_CMD - error swkm update operation status: " - << (int32_t) errorCode; + LOG(ERROR) << "INS_UPDATE_OPERATION_CMD - error swkm update operation status: " + << (int32_t)errorCode; } } else { /* Strongbox Keymaster operation */ @@ -1173,20 +1205,24 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co std::unique_ptr item; std::vector cborOutData; std::vector asn1ParamsVerified; - // For symmetic ciphers only block aligned data is send to javacard Applet to reduce the number of calls to - //javacard. If the input message is less than block size then it is buffered inside the HAL. so in case if + // For symmetic ciphers only block aligned data is send to javacard Applet to reduce the + // number of calls to + // javacard. If the input message is less than block size then it is buffered inside the + // HAL. so in case if // after buffering there is no data to send to javacard don't call javacard applet. - //For AES GCM operations, even though the input length is 0(which is not block aligned), if there is - //ASSOCIATED_DATA present in KeyParameters. Then we need to make a call to javacard Applet. - if(data.size() == 0 && !findTag(inParams, Tag::ASSOCIATED_DATA)) { - //Return OK, since this is not error case. + // For AES GCM operations, even though the input length is 0(which is not block + // aligned), if there is ASSOCIATED_DATA present in KeyParameters. Then we need to make + // a call to javacard Applet. + if (data.size() == 0 && !findTag(inParams, Tag::ASSOCIATED_DATA)) { + // Return OK, since this is not error case. LOG(DEBUG) << "sendDataCallback: data size is zero"; return ErrorCode::OK; } - if(ErrorCode::OK != (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { + if (ErrorCode::OK != + (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { LOG(ERROR) << "sendDataCallback: error in encodeParametersVerified status: " - << (int32_t) errorCode; + << (int32_t)errorCode; return errorCode; } @@ -1200,65 +1236,77 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co errorCode = sendData(Instruction::INS_UPDATE_OPERATION_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); + if (errorCode == ErrorCode::OK) { + // Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = + decodeData(cborConverter_, + std::vector(cborOutData.begin(), cborOutData.end() - 2), + true, oprCtx_); if (item != nullptr) { /*Ignore inputConsumed from javacard SE since HAL consumes all the input */ - //cborConverter_.getUint64(item, 1, inputConsumed); - //This callback function may gets called multiple times so parse and get the outParams only once. - //Otherwise there can be chance of duplicate entries in outParams. Use tempOut to collect all the - //cipher text and finally copy it to the output. getBinaryArray function appends the new cipher text - //at the end of the tempOut(std::vector). - if((outParams.size() == 0 && !cborConverter_.getKeyParameters(item, 2, outParams)) || - !cborConverter_.getBinaryArray(item, 3, tempOut)) { + // cborConverter_.getUint64(item, 1, inputConsumed); + // This callback function may gets called multiple times so parse and get the + // outParams only once. Otherwise there can be chance of duplicate entries in + // outParams. Use tempOut to collect all the cipher text and finally copy it to + // the output. getBinaryArray function appends the new cipher text at the end of + // the tempOut(std::vector). + if ((outParams.size() == 0 && + !cborConverter_.getKeyParameters(item, 2, outParams)) || + !cborConverter_.getBinaryArray(item, 3, tempOut)) { outParams.setToExternal(nullptr, 0); tempOut.clear(); errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "sendDataCallback: INS_UPDATE_OPERATION_CMD: error while converting cbor data, status: " << (int32_t) errorCode; + LOG(ERROR) << "sendDataCallback: INS_UPDATE_OPERATION_CMD: error while " + "converting cbor data, status: " + << (int32_t)errorCode; } } } return errorCode; }; - if(ErrorCode::OK == (errorCode = oprCtx_->update(operationHandle, std::vector(input), - sendDataCallback))) { + if (ErrorCode::OK == + (errorCode = + oprCtx_->update(operationHandle, std::vector(input), sendDataCallback))) { /* Consumed all the input */ inputConsumed = input.size(); output = tempOut; } - LOG(DEBUG) << "Update operation status: " << (int32_t) errorCode; - if(ErrorCode::OK != errorCode) { - LOG(ERROR) << "Error in update operation, status: " << (int32_t) errorCode; - abort(halGeneratedOprHandle); + LOG(DEBUG) << "Update operation status: " << (int32_t)errorCode; + if (ErrorCode::OK != errorCode) { + LOG(ERROR) << "Error in update operation, status: " << (int32_t)errorCode; + abort(operationHandle); } } - if(ErrorCode::OK != errorCode) { + if (ErrorCode::OK != errorCode) { /* Delete the entry from operation table. */ - LOG(ERROR) << "Delete entry from operation table, status: " << (int32_t) errorCode; - deleteOprHandleEntry(halGeneratedOprHandle); + LOG(ERROR) << "Delete entry from operation table, status: " << (int32_t)errorCode; + operationTable.erase(operationHandle); } _hidl_cb(errorCode, inputConsumed, outParams, output); return Void(); } -Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, const hidl_vec& inParams, const hidl_vec& input, const hidl_vec& signature, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, finish_cb _hidl_cb) { +Return +JavacardKeymaster4Device::finish(uint64_t operationHandle, const hidl_vec& inParams, + const hidl_vec& input, const hidl_vec& signature, + const HardwareAuthToken& authToken, + const VerificationToken& verificationToken, finish_cb _hidl_cb) { ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint64_t operationHandle; hidl_vec outParams; hidl_vec output; FinishOperationResponse response(softKm_->message_version()); + OperationType operType = getOperationType(operationHandle); - if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" - << " secure element reset occurred."; - _hidl_cb(errorCode, outParams, output); + if (OperationType::UNKNOWN == operType) { // operation handle not found + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle " + "is passed or if" + << " secure element reset occurred."; + _hidl_cb(ErrorCode::INVALID_OPERATION_HANDLE, outParams, output); return Void(); } - if (!isStrongboxOperation(halGeneratedOprHandle)) { + if (OperationType::PUBLIC_OPERATION == operType) { /* SW keymaster (Public key operation) */ LOG(DEBUG) << "FINISH - swkm operation "; FinishOperationRequest request(softKm_->message_version()); @@ -1270,13 +1318,13 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co softKm_->FinishOperation(request, &response); errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "FINISH - swkm operation, status: " << (int32_t) errorCode; + LOG(DEBUG) << "FINISH - swkm operation, status: " << (int32_t)errorCode; if (response.error == KM_ERROR_OK) { outParams = kmParamSet2Hidl(response.output_params); output = kmBuffer2hidlVec(response.output); } else { - LOG(ERROR) << "Error in finish operation, status: " << (int32_t) errorCode; + LOG(ERROR) << "Error in finish operation, status: " << (int32_t)errorCode; } } else { /* Strongbox Keymaster operation */ @@ -1286,8 +1334,8 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co * may be called multiple times if the input data is larger than MAX_ALLOWED_INPUT_SIZE. * This callback function decides whether to call update/finish instruction based on the * input received from the OperationContext through finish variable. - * if finish variable is false update instruction is called, if it is true finish instruction - * is called. + * if finish variable is false update instruction is called, if it is true finish + * instruction is called. */ auto sendDataCallback = [&](std::vector& data, bool finish) -> ErrorCode { cppbor::Array array; @@ -1297,23 +1345,26 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co int keyParamPos, outputPos; std::vector asn1ParamsVerified; - if(ErrorCode::OK != (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(ERROR) << "sendDataCallback: Error in encodeParametersVerified, status: " << (int32_t) errorCode; + if (ErrorCode::OK != + (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { + LOG(ERROR) << "sendDataCallback: Error in encodeParametersVerified, status: " + << (int32_t)errorCode; return errorCode; } - //In case if there is ASSOCIATED_DATA present in the keyparams, then make sure it is either passed with - //update call or finish call. Don't send ASSOCIATED_DATA in both update and finish calls. aadTag is used to - //check if ASSOCIATED_DATA is already sent in update call. If addTag is true then skip ASSOCIATED_DATA from - //keyparams in finish call. - // Convert input data to cbor format + // In case if there is ASSOCIATED_DATA present in the keyparams, then make sure it is + // either passed with update call or finish call. Don't send ASSOCIATED_DATA in both + // update and finish calls. aadTag is used to check if ASSOCIATED_DATA is already sent + // in update call. If addTag is true then skip ASSOCIATED_DATA from keyparams in finish + // call. + // Convert input data to cbor format array.add(operationHandle); - if(finish) { + if (finish) { std::vector finishParams; LOG(DEBUG) << "sendDataCallback: finish operation"; - if(aadTag) { - for(int i = 0; i < inParams.size(); i++) { - if(inParams[i].tag != Tag::ASSOCIATED_DATA) + if (aadTag) { + for (int i = 0; i < inParams.size(); i++) { + if (inParams[i].tag != Tag::ASSOCIATED_DATA) finishParams.push_back(inParams[i]); } } else { @@ -1327,7 +1378,7 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co outputPos = 2; } else { LOG(DEBUG) << "sendDataCallback: update operation"; - if(findTag(inParams, Tag::ASSOCIATED_DATA)) { + if (findTag(inParams, Tag::ASSOCIATED_DATA)) { aadTag = true; } cborConverter_.addKeyparameters(array, inParams); @@ -1341,52 +1392,74 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co std::vector cborData = array.encode(); errorCode = sendData(ins, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); + if (errorCode == ErrorCode::OK) { + // Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = + decodeData(cborConverter_, + std::vector(cborOutData.begin(), cborOutData.end() - 2), + true, oprCtx_); if (item != nullptr) { - //There is a change that this finish callback may gets called multiple times if the input data size - //is larger the MAX_ALLOWED_INPUT_SIZE (Refer OperationContext) so parse and get the outParams only - //once. Otherwise there can be chance of duplicate entries in outParams. Use tempOut to collect all - //the cipher text and finally copy it to the output. getBinaryArray function appends the new cipher - //text at the end of the tempOut(std::vector). - if((outParams.size() == 0 && !cborConverter_.getKeyParameters(item, keyParamPos, outParams)) || - !cborConverter_.getBinaryArray(item, outputPos, tempOut)) { + // There is a change that this finish callback may gets called multiple times if + // the input data size is larger the MAX_ALLOWED_INPUT_SIZE (Refer + // OperationContext) so parse and get the outParams only once. Otherwise there + // can be chance of duplicate entries in outParams. Use tempOut to collect all + // the cipher text and finally copy it to the output. getBinaryArray function + // appends the new cipher text at the end of the tempOut(std::vector). + if ((outParams.size() == 0 && + !cborConverter_.getKeyParameters(item, keyParamPos, outParams)) || + !cborConverter_.getBinaryArray(item, outputPos, tempOut)) { outParams.setToExternal(nullptr, 0); tempOut.clear(); errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "sendDataCallback: error while converting cbor data in operation: " << (int32_t)ins << " decodeData, status: " << (int32_t) errorCode; + LOG(ERROR) + << "sendDataCallback: error while converting cbor data in operation: " + << (int32_t)ins << " decodeData, status: " << (int32_t)errorCode; } } } return errorCode; }; - if(ErrorCode::OK == (errorCode = oprCtx_->finish(operationHandle, std::vector(input), - sendDataCallback))) { + if (ErrorCode::OK == + (errorCode = + oprCtx_->finish(operationHandle, std::vector(input), sendDataCallback))) { output = tempOut; } if (ErrorCode::OK != errorCode) { - LOG(ERROR) << "Error in finish operation, status: " << (int32_t) errorCode; - abort(halGeneratedOprHandle); + LOG(ERROR) << "Error in finish operation, status: " << (int32_t)errorCode; + abort(operationHandle); } } /* Delete the entry from operation table. */ - deleteOprHandleEntry(halGeneratedOprHandle); + operationTable.erase(operationHandle); oprCtx_->clearOperationData(operationHandle); - LOG(DEBUG) << "finish operation, status: " << (int32_t) errorCode; + LOG(DEBUG) << "finish operation, status: " << (int32_t)errorCode; _hidl_cb(errorCode, outParams, output); return Void(); } -Return JavacardKeymaster4Device::abort(uint64_t halGeneratedOprHandle) { +ErrorCode JavacardKeymaster4Device::abortPrivateKeyOperation(uint64_t operationHandle) { ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint64_t operationHandle; - if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" - << " secure element reset occurred."; - return errorCode; + cppbor::Array array; + std::unique_ptr item; + std::vector cborOutData; + + /* Convert input data to cbor format */ + array.add(operationHandle); + std::vector cborData = array.encode(); + + errorCode = sendData(Instruction::INS_ABORT_OPERATION_CMD, cborData, cborOutData); + + if (errorCode == ErrorCode::OK) { + // Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = decodeData( + cborConverter_, std::vector(cborOutData.begin(), cborOutData.end() - 2), true, + oprCtx_); } + return errorCode; +} + +ErrorCode JavacardKeymaster4Device::abortPublicKeyOperation(uint64_t operationHandle) { + ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; AbortOperationRequest request(softKm_->message_version()); request.op_handle = operationHandle; @@ -1394,72 +1467,35 @@ Return JavacardKeymaster4Device::abort(uint64_t halGeneratedOprHandle softKm_->AbortOperation(request, &response); errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "swkm abort operation, status: " << (int32_t) errorCode; - if (response.error == KM_ERROR_INVALID_OPERATION_HANDLE) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - - /* Convert input data to cbor format */ - array.add(operationHandle); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_ABORT_OPERATION_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - } - /* Delete the entry on this operationHandle */ - oprCtx_->clearOperationData(operationHandle); - deleteOprHandleEntry(halGeneratedOprHandle); return errorCode; } -// Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. -Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; - std::vector asn1ParamsVerified; - ErrorCode ret = ErrorCode::UNKNOWN_ERROR; - - if(ErrorCode::OK != (ret = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(DEBUG) << "INS_DEVICE_LOCKED_CMD: Error in encodeParametersVerified, status: " << (int32_t) errorCode; - return errorCode; - } +ErrorCode JavacardKeymaster4Device::abortOperation(uint64_t operationHandle, + OperationType operType) { + if (operType == OperationType::UNKNOWN) return ErrorCode::UNKNOWN_ERROR; - /* Convert input data to cbor format */ - array.add(passwordOnly); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - - ret = sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData); - - if(ret == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); + if (OperationType::PUBLIC_OPERATION == operType) { + return abortPublicKeyOperation(operationHandle); + } else { + return abortPrivateKeyOperation(operationHandle); } - return errorCode; } -Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::earlyBootEnded() { - std::unique_ptr item; - std::string message; - std::vector cborOutData; - std::vector cborInput; - ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; - - ErrorCode ret = sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData); +Return JavacardKeymaster4Device::abort(uint64_t operationHandle) { + ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; + OperationType operType = getOperationType(operationHandle); + if (OperationType::UNKNOWN == operType) { // operation handle not found + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid " + "operation handle is passed or if" + << " secure element reset occurred."; + return ErrorCode::INVALID_OPERATION_HANDLE; + } - if(ret == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); + errorCode = abortOperation(operationHandle, operType); + if (errorCode == ErrorCode::OK) { + /* Delete the entry on this operationHandle */ + oprCtx_->clearOperationData(operationHandle); + operationTable.erase(operationHandle); } return errorCode; } diff --git a/HAL/keymaster/include/JavacardKeymaster4Device.h b/HAL/keymaster/include/JavacardKeymaster4Device.h index c8ada383..e34a2aee 100644 --- a/HAL/keymaster/include/JavacardKeymaster4Device.h +++ b/HAL/keymaster/include/JavacardKeymaster4Device.h @@ -54,6 +54,14 @@ using ::android::hardware::keymaster::V4_0::Tag; using V41ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode; +enum class OperationType { + /* Public operations are processed inside softkeymaster */ + PUBLIC_OPERATION = 0, + /* Private operations are processed inside strongbox */ + PRIVATE_OPERATION = 1, + UNKNOWN = 2, +}; + class JavacardKeymaster4Device : public IKeymasterDevice { public: @@ -87,8 +95,31 @@ class JavacardKeymaster4Device : public IKeymasterDevice { protected: CborConverter cborConverter_; - -private: + + private: + ErrorCode handleBeginPublicKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + hidl_vec& outParams, + uint64_t& operationHandle); + + ErrorCode handleBeginPrivateKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + const HardwareAuthToken& authToken, + hidl_vec& outParams, + uint64_t& operationHandle); + + ErrorCode handleBeginOperation(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + const HardwareAuthToken& authToken, + hidl_vec& outParams, uint64_t& operationHandle, + OperationType& operType); + + ErrorCode abortOperation(uint64_t operationHandle, OperationType operType); + + ErrorCode abortPublicKeyOperation(uint64_t operationHandle); + + ErrorCode abortPrivateKeyOperation(uint64_t operationHandle); + std::unique_ptr<::keymaster::AndroidKeymaster> softKm_; std::unique_ptr oprCtx_; bool isEachSystemPropertySet; diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index 2007e08e..69ac3621 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -26,6 +26,7 @@ #include #include +#define SE_POWER_RESET_STATUS_FLAG (1 << 30) enum ProvisionStatus { NOT_PROVISIONED = 0x00, PROVISION_STATUS_ATTESTATION_KEY = 0x01, @@ -112,6 +113,17 @@ int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t &v return SUCCESS; } + +uint64_t unmaskPowerResetFlag(uint64_t errorCode) { + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + printf("\n Secure element reset happened\n"); + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return errorCode; +} + int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response) { if (SUCCESS != sendData(pSocket, apdu, response)) { return FAILURE; @@ -128,6 +140,7 @@ int provisionData(std::shared_ptr& pSocket, std::string apdu, s const Uint* uintVal = item.get()->asUint(); err = uintVal->value(); } + err = unmaskPowerResetFlag(err); if (err != 0) { printf("\n Failed with error:%ld", err); return FAILURE; From 7a5f77f70ac37be299e2fad6889d998ef2215952 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Fri, 22 Oct 2021 17:22:09 +0000 Subject: [PATCH 02/33] 1. Store AUTH_TIMEOUT_MILLIS in keyblob. 2. Validate Tokens in littleEndian and BigEndian formats. --- .../android/javacard/keymaster/KMArray.java | 6 + .../javacard/keymaster/KMIntegerTag.java | 4 +- .../javacard/keymaster/KMKeyParameters.java | 70 +++++ .../javacard/keymaster/KMKeymasterApplet.java | 267 +++++++++++++----- .../android/javacard/keymaster/KMType.java | 2 + 5 files changed, 280 insertions(+), 69 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index bfa09269..adf61723 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -92,6 +92,12 @@ public void add(short index, short objPtr) { (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index * 2)), objPtr); } + + public void deleteLastEntry() { + short len = length(); + Util.setShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), (short) (len -1)); + } + public short get(short index) { short len = length(); diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 6ddec4bd..c4bab026 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -48,7 +48,9 @@ public class KMIntegerTag extends KMTag { ACTIVE_DATETIME, ORIGINATION_EXPIRE_DATETIME, USAGE_EXPIRE_DATETIME, - CREATION_DATETIME + CREATION_DATETIME, + // Custom tag. + AUTH_TIMEOUT_MILLIS }; private KMIntegerTag() { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 0ef85ae4..ef1e1e00 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -26,6 +26,9 @@ * arrayPtr is a pointer to array with any KMTag subtype instances. */ public class KMKeyParameters extends KMType { + + + private static short[] customTags; private static KMKeyParameters prototype; @@ -211,6 +214,9 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, .instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr); Util.setShort(scratchPad, arrInd, bootPatchTag); arrInd += 2; + // 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)); } @@ -318,4 +324,68 @@ public static short createKeyParameters(byte[] ptrArr, short len) { } return KMKeyParameters.instance(arrPtr); } + + private static short[] getCustomTags() { + if (customTags == null) { + customTags = new short[] { + KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + }; + } + return customTags; + } + + public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { + short[] customTags = getCustomTags(); + short index = 0; + short tagPtr; + short len = (short) customTags.length; + 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[] customTags = getCustomTags(); + 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 29413a45..a7ec86a8 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -966,6 +966,9 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { parseEncryptedKeyBlob(scratchPad); // Check Version and Patch Level checkVersionAndPatchLevel(scratchPad); + // Remove custom tags from key characteristics + short hwParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); + KMKeyParameters.cast(hwParams).deleteCustomTags(); // make response. tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); @@ -1953,7 +1956,13 @@ 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); + // 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) { @@ -2005,48 +2014,65 @@ 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 (KMInteger.compare(ptr, op.getHandle()) != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); + private boolean verifyVerificationTokenMacInBigEndian(short verToken, short key, 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); + short len = (short) authVerification.length; + // concatenate challenge - 8 bytes + short ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate security level - 4 bytes + ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate Parameters verified - blob of encoded data. + ptr = KMVerificationToken.cast(verToken).getParametersVerified(); + if (KMByteBlob.cast(ptr).length() != 0) { + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); } + // hmac the data + ptr = KMVerificationToken.cast(verToken).getMac(); + + return 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()); } - 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 + private boolean verifyVerificationTokenMacInLittleEndian(short verToken, short key, 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.arrayCopy(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); + short len = (short) authVerification.length; // concatenate challenge - 8 bytes - ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + short ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); len += 8; // concatenate timestamp -8 bytes ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); len += 8; // concatenate security level - 4 bytes ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + scratchPad[len] = KMEnum.cast(ptr).getVal(); len += 4; // concatenate Parameters verified - blob of encoded data. ptr = KMVerificationToken.cast(verToken).getParametersVerified(); @@ -2055,21 +2081,31 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { } // hmac the data ptr = KMVerificationToken.cast(verToken).getMac(); + + return 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()); + } + + 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); + } 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) { - KMException.throwIt(KMError.VERIFICATION_FAILED); + if (!verifyVerificationTokenMacInLittleEndian(verToken, key, scratchPad) && + !verifyVerificationTokenMacInBigEndian(verToken, key, scratchPad)) { + // Throw Exception if none of the combination works. + KMException.throwIt(KMError.VERIFICATION_FAILED); } } @@ -2752,12 +2788,17 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc // authenticate user authTokenMatches(userSecureIdPtr, authType, scratchPad); + 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 = - addAuthTimeToTimeStamp(authTime, + addIntegers(authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad); op.setAuthTime( KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff()); @@ -2793,9 +2834,7 @@ private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, private void authTokenMatches(short userSecureIdsPtr, short authType, byte[] scratchPad) { - if (!validateHwToken(data[HW_TOKEN], scratchPad)) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } + validateHwToken(data[HW_TOKEN], scratchPad); if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } @@ -2806,20 +2845,14 @@ private void authTokenMatches(short userSecureIdsPtr, short authType, 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 verifyHwTokenMacAsPerSpecification(short hwToken, short key, byte[] scratchPad) { + 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).toLittleEndian(scratchPad, len); len += 8; // concatenate user id - 8 bytes @@ -2836,12 +2869,97 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { 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(), + scratchPad, + (short) 0, + len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + } + + private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, byte[] scratchPad) { + 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) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + 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; - // hmac the data + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short key = repository.getComputedHmacKey(); + + return 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()); + + } + + private boolean verifyHwTokenMacInLittleEndian(short hwToken, short key, byte[] scratchPad) { + 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(); + scratchPad[len] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + len += 8; + + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + return seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), @@ -2854,6 +2972,23 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMByteBlob.cast(ptr).length()); } + private void 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) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + short key = repository.getComputedHmacKey(); + + if (!verifyHwTokenMacInLittleEndian(hwToken, key, scratchPad) && + !verifyHwTokenMacInBigEndian(hwToken, key, scratchPad) && + !verifyHwTokenMacAsPerSpecification(hwToken, key, scratchPad)) { + // Throw Exception if none of the combination works. + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + } + private void processImportKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); @@ -3958,28 +4093,24 @@ private static void sendError(APDU apdu, short err) { sendOutgoing(apdu); } - private short addAuthTimeToTimeStamp(short authTime, short timeStamp, byte[] scratchPad) { - // Convert authTime to milliseconds - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 40, (byte) 0); + private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0); Util.arrayCopyNonAtomic( KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff(), scratchPad, - (short) (8 - KMInteger.cast(authTime).length()), - KMInteger.cast(authTime).length()); - KMUtils.convertToMilliseconds(scratchPad, (short) 0, (short) 8, (short) 16); + (short) (8 - KMInteger.cast(timeStamp).length()), + KMInteger.cast(timeStamp).length()); // Copy timestamp to scratchpad - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); Util.arrayCopyNonAtomic( KMInteger.cast(timeStamp).getBuffer(), KMInteger.cast(timeStamp).getStartOff(), scratchPad, - (short) (8 - KMInteger.cast(timeStamp).length()), + (short) (16 - KMInteger.cast(timeStamp).length()), KMInteger.cast(timeStamp).length()); // add authTime in millis to timestamp. - Util.arrayFillNonAtomic(scratchPad, (short) 16, (short) 8, (byte) 0); KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); return KMInteger.uint_64(scratchPad, (short) 16); } diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index f571275c..00704df2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -186,6 +186,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 From 76f2f1d3b9caa2f28479665214371cdd7476b970 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Tue, 26 Oct 2021 16:24:26 +0000 Subject: [PATCH 03/33] Added missed functions in 4.1 --- .../4.1/JavacardKeymaster4Device.cpp | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index 46a12557..ffca0856 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -1500,6 +1500,52 @@ Return JavacardKeymaster4Device::abort(uint64_t operationHandle) { return errorCode; } +// Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. +Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) { + cppbor::Array array; + std::unique_ptr item; + std::vector cborOutData; + ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; + std::vector asn1ParamsVerified; + ErrorCode ret = ErrorCode::UNKNOWN_ERROR; + + if(ErrorCode::OK != (ret = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { + LOG(DEBUG) << "INS_DEVICE_LOCKED_CMD: Error in encodeParametersVerified, status: " << (int32_t) errorCode; + return errorCode; + } + + /* Convert input data to cbor format */ + array.add(passwordOnly); + cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); + std::vector cborData = array.encode(); + + ret = sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData); + + if(ret == ErrorCode::OK) { + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( + cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); + } + return errorCode; +} + +Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::earlyBootEnded() { + std::unique_ptr item; + std::string message; + std::vector cborOutData; + std::vector cborInput; + ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; + + ErrorCode ret = sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData); + + if(ret == ErrorCode::OK) { + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( + cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); + } + return errorCode; +} + } // javacard } // namespace V4_1 } // namespace keymaster From 0b6aeb1495e92f0235402a74805b0a448589a66c Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Fri, 29 Oct 2021 11:21:53 +0000 Subject: [PATCH 04/33] CTS Fixes --- .../keymaster/KMAndroidSEProvider.java | 2 +- .../javacard/keymaster/KMKeymasterApplet.java | 66 +++++----- .../javacard/keymaster/KMRepository.java | 12 +- .../4.1/JavacardOperationContext.cpp | 116 +++++++++--------- .../include/JavacardOperationContext.h | 8 +- ProvisioningTool/sample_json_cf.txt | 2 +- 6 files changed, 104 insertions(+), 102 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index f64858f5..230725c1 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -114,7 +114,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short AES_GCM_NONCE_LENGTH = 12; public static final byte KEYSIZE_128_OFFSET = 0x00; public static final byte KEYSIZE_256_OFFSET = 0x01; - public static final short TMP_ARRAY_SIZE = 256; + public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index a7ec86a8..df0fa1a2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1726,29 +1726,29 @@ private void processFinishOperationCmd(APDU apdu) { private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + short blockSize; switch (op.getAlgorithm()) { case KMType.AES: case KMType.DES: if (op.getAlgorithm() == KMType.AES) { - tmpVariables[0] = AES_BLOCK_SIZE; + blockSize = AES_BLOCK_SIZE; } else { - tmpVariables[0] = DES_BLOCK_SIZE; + blockSize = DES_BLOCK_SIZE; } // If no padding then data length must be block aligned if ((op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) && op.getPadding() == KMType.PADDING_NONE - && ((short) (len % tmpVariables[0]) != 0)) { + && ((short) (len % blockSize) != 0)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } else if (op.getBlockMode() == KMType.GCM) { // update aad if there is any updateAAD(op, (byte) 0x01); // Get the output size len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); - data[OUTPUT_DATA] = KMByteBlob.instance(len); } // 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 - tmpVariables[0] = KMByteBlob.instance((short) (len + tmpVariables[0])); + tmpVariables[0] = KMByteBlob.instance((short) (len + blockSize)); len = op.getOperation() .finish( @@ -1772,6 +1772,7 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + short blockSize; switch (op.getAlgorithm()) { case KMType.RSA: // Fill the scratch pad with zero @@ -1793,14 +1794,13 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { case KMType.AES: case KMType.DES: if (op.getAlgorithm() == KMType.AES) { - tmpVariables[0] = AES_BLOCK_SIZE; + blockSize = AES_BLOCK_SIZE; } else { - tmpVariables[0] = DES_BLOCK_SIZE; + blockSize = DES_BLOCK_SIZE; } - tmpVariables[1] = repository.alloc(len); if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB) && len > 0 - && (len % tmpVariables[0]) != 0) { + && (len % blockSize) != 0) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } else if (op.getBlockMode() == KMType.GCM) { // update aad if there is any @@ -1809,18 +1809,18 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { if ((len < (short) (op.getMacLength() / 8))) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - // Get the output size - in case of JCardSim this will more then input size - tmpVariables[0] = + // Get the output size - in case of JCardSim this might be more than input size + len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); - tmpVariables[1] = repository.alloc(tmpVariables[0]); } + tmpVariables[1] = repository.alloc((short) (len + blockSize)); byte[] heap = repository.getHeap(); len = op.getOperation() .finish( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - len, + KMByteBlob.cast(data[INPUT_DATA]).length(), heap, tmpVariables[1]); @@ -2144,6 +2144,7 @@ private void processUpdateOperationCmd(APDU apdu) { } // authorize the update operation authorizeUpdateFinishOperation(op, scratchPad); + short inputConsumed = 0; // If signing without digest then do length validation checks if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); @@ -2160,41 +2161,40 @@ private void processUpdateOperationCmd(APDU apdu) { if (op.getAlgorithm() == KMType.RSA) { KMException.throwIt(KMError.OPERATION_CANCELLED); } - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + short inputLen = KMByteBlob.cast(data[INPUT_DATA]).length(); short additionalExpOutLen = 0; if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.GCM) { - updateAAD(op, (byte) 0x00); - // if input data present - if (tmpVariables[0] > 0) { - if (tmpVariables[0] % AES_BLOCK_SIZE != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // no more future updateAAD allowed if input data present. - if (op.isAesGcmUpdateAllowed()) { - op.setAesGcmUpdateComplete(); - } - } - additionalExpOutLen = 16; - } else { + if (op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB) { // input data must be block aligned. // 128 bit block size - HAL must send block aligned data - if (tmpVariables[0] % AES_BLOCK_SIZE != 0 || tmpVariables[0] <= 0) { + if (inputLen % AES_BLOCK_SIZE != 0 || inputLen <= 0) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } + } else { /* CTR or GCM Modes */ + additionalExpOutLen = AES_BLOCK_SIZE; + if (op.getBlockMode() == KMType.GCM) { + updateAAD(op, (byte) 0x00); + // if input data present + if (inputLen > 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 (tmpVariables[0] % DES_BLOCK_SIZE != 0) { + if (inputLen % 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) (tmpVariables[0] + additionalExpOutLen)); + data[OUTPUT_DATA] = KMByteBlob.instance((short) (inputLen + additionalExpOutLen)); // 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. - tmpVariables[3] = tmpVariables[0]; + inputConsumed = inputLen; try { tmpVariables[0] = op.getOperation() @@ -2230,7 +2230,7 @@ private void processUpdateOperationCmd(APDU apdu) { data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[3])); + KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(inputConsumed)); KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 6dfc2d0d..2bc3d735 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -380,6 +380,9 @@ public short allocAvailableMemory() { } public short alloc(short length) { + if (length < 0) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } if ((((short) (heapIndex[0] + length)) > heap.length) || (((short) (heapIndex[0] + length)) > reclaimIndex[0])) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); @@ -389,6 +392,9 @@ public short alloc(short length) { } private short dataAlloc(short length) { + if (length < 0) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } if (((short) (dataIndex + length)) > dataTable.length) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -617,7 +623,7 @@ public short getVerifiedBootHash() { public boolean getBootLoaderLock() { short blob = readData(BOOT_DEVICE_LOCKED_STATUS); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; } public byte getBootState() { @@ -677,9 +683,9 @@ public void clearAndroidSystemProperties() { public void setBootloaderLocked(boolean flag) { short start = alloc(DEVICE_LOCK_FLAG_SIZE); if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x01); + (getHeap())[start] = (byte) 0x01; } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFE); + (getHeap())[start] = (byte) 0x00; } writeDataEntry(BOOT_DEVICE_LOCKED_STATUS, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); } diff --git a/HAL/keymaster/4.1/JavacardOperationContext.cpp b/HAL/keymaster/4.1/JavacardOperationContext.cpp index 7319cb3c..34e9d8e1 100644 --- a/HAL/keymaster/4.1/JavacardOperationContext.cpp +++ b/HAL/keymaster/4.1/JavacardOperationContext.cpp @@ -18,7 +18,7 @@ #include #include -#define MAX_ALLOWED_INPUT_SIZE 512 +#define MAX_ALLOWED_INPUT_SIZE 256 #define AES_BLOCK_SIZE 16 #define DES_BLOCK_SIZE 8 #define RSA_INPUT_MSG_LEN 256 @@ -50,6 +50,10 @@ inline ErrorCode hidlParamSet2OperatinInfo(const hidl_vec& params, case Tag::BLOCK_MODE: info.mode = static_cast(param.f.integer); break; + case Tag::MAC_LENGTH: + // Convert to bytes. + info.macLength = (param.f.integer / 8); + break; default: continue; } @@ -61,12 +65,12 @@ ErrorCode OperationContext::setOperationInfo(uint64_t operationHandle, KeyPurpos const hidl_vec& params) { ErrorCode errorCode = ErrorCode::OK; OperationData data; + memset((void *)&data, 0, sizeof(OperationData)); if(ErrorCode::OK != (errorCode = hidlParamSet2OperatinInfo(params, data.info))) { return errorCode; } data.info.purpose = purpose; data.info.alg = alg; - memset((void*)&(data.data), 0x00, sizeof(data.data)); operationTable[operationHandle] = data; return ErrorCode::OK; } @@ -112,9 +116,7 @@ ErrorCode OperationContext::validateInputData(uint64_t operHandle, Operation opr //combine both the data in a single buffer. This helps in making sure that no data is left out in the buffer after //finish opertion. if((oprData.data.buf_len+actualInput.size()) > MAX_ALLOWED_INPUT_SIZE) { - for(size_t i = 0; i < oprData.data.buf_len; ++i) { - input.push_back(oprData.data.buf[i]); - } + input.insert(input.end(), oprData.data.buf, oprData.data.buf + oprData.data.buf_len); input.insert(input.end(), actualInput.begin(), actualInput.end()); //As buffered data is already consumed earse the buffer. if(oprData.data.buf_len != 0) { @@ -145,20 +147,20 @@ ErrorCode OperationContext::update(uint64_t operHandle, const std::vector newInput(first, end); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput.data(), newInput.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput, Operation::Update, cb))) { return errorCode; } } if(extraData > 0) { std::vector finalInput(input.cend()-extraData, input.cend()); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput.data(), finalInput.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput, Operation::Update, cb))) { return errorCode; } } } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input.data(), input.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input, Operation::Update, cb))) { return errorCode; } @@ -183,13 +185,13 @@ ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector newInput(first, end); if(extraData == 0 && (i == noOfChunks - 1)) { //Last chunk - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput.data(), newInput.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput, Operation::Finish, cb, true))) { return errorCode; } } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput.data(), newInput.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput, Operation::Update, cb))) { return errorCode; } @@ -197,13 +199,13 @@ ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector 0) { std::vector finalInput(input.cend()-extraData, input.cend()); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput.data(), finalInput.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput, Operation::Finish, cb, true))) { return errorCode; } } } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input.data(), input.size(), + if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input, Operation::Finish, cb, true))) { return errorCode; } @@ -223,7 +225,7 @@ ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector& input, Operation opr, std::vector& out) { size_t dataToSELen = 0;/*Length of the data to be send to the Applet.*/ size_t inputConsumed = 0;/*Length of the data consumed from input */ @@ -240,33 +242,44 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, uint8_t* in if(opr == Operation::Finish) { //Copy the buffer to be send to SE. - for(int i = 0; i < data.buf_len; i++) - { - out.push_back(data.buf[i]); - } - dataToSELen = data.buf_len + input_len; + out.insert(out.end(), data.buf, data.buf + data.buf_len); + dataToSELen = data.buf_len + input.size(); } else { /*Update */ //Calculate the block sized length on combined input of both buffered data and input data. - size_t blockAlignedLen = ((data.buf_len + input_len)/blockSize) * blockSize; //For symmetric ciphers, decryption operation and PKCS7 padding mode or AES GCM operation save the last 16 bytes //of block and send this block in finish operation. This is done to make sure that there will be always a 16 //bytes of data left for finish operation so that javacard Applet may remove PKCS7 padding if any or get the tag //data for AES GCM operation for authentication purpose. - if(((operationTable[operHandle].info.alg == Algorithm::AES) || - (operationTable[operHandle].info.alg == Algorithm::TRIPLE_DES)) && - (operationTable[operHandle].info.pad == PaddingMode::PKCS7 || - operationTable[operHandle].info.mode == BlockMode::GCM) && - (operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT)) { - if(blockAlignedLen >= blockSize) blockAlignedLen -= blockSize; + if (operationTable[operHandle].info.pad == PaddingMode::PKCS7 && + operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { + /* Buffer till we receive more than blockSize of data of atleast one byte*/ + dataToSELen = ((data.buf_len + input.size())/blockSize) * blockSize; + size_t remaining = ((data.buf_len + input.size()) % blockSize); + if (dataToSELen >= blockSize && remaining == 0) { + dataToSELen -= blockSize; + } + } else if (operationTable[operHandle].info.mode == BlockMode::GCM && + operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { + /* Always Buffer mac length bytes */ + dataToSELen = 0; + if ((data.buf_len + input.size()) > operationTable[operHandle].info.macLength) { + dataToSELen = (data.buf_len + input.size()) - operationTable[operHandle].info.macLength; + } + } else if (operationTable[operHandle].info.mode == BlockMode::GCM || + operationTable[operHandle].info.mode == BlockMode::CTR) { + /* No Buffering */ + dataToSELen = input.size(); + } else { + /* Buffer (BlockSize - 1) bytes */ + dataToSELen = ((data.buf_len + input.size())/blockSize) * blockSize; } //Copy data to be send to SE from buffer, only if atleast a minimum block aligned size is available. - if(blockAlignedLen >= blockSize) { - for(size_t pos = 0; pos < std::min(blockAlignedLen, data.buf_len); pos++) { + if(dataToSELen > 0) { + for(size_t pos = 0; pos < std::min(dataToSELen, data.buf_len); pos++) { out.push_back(data.buf[pos]); } } - dataToSELen = blockAlignedLen; } if(dataToSELen > 0) { @@ -276,16 +289,13 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, uint8_t* in //data i.e. AES/TDES Decryption with PKC7Padding or AES GCM Decryption operations. inputConsumed = (data.buf_len > dataToSELen) ? 0 : (dataToSELen - data.buf_len); - //Copy the buffer to be send to SE. - for(int i = 0; i < inputConsumed; i++) - { - out.push_back(input[i]); - } + // Copy the buffer to be send to SE. + out.insert(out.end(), input.begin(), input.begin() + inputConsumed); if(data.buf_len > dataToSELen) { //Only blockAlignedLen data is consumed from buffer so reorder the buffer data. - memcpy(data.buf, data.buf+dataToSELen, data.buf_len-dataToSELen); - memset(data.buf+dataToSELen, 0x00, data.buf_len-dataToSELen); + memcpy(data.buf, (data.buf + dataToSELen), (data.buf_len - dataToSELen)); + memset((data.buf + data.buf_len - dataToSELen), 0x00, dataToSELen); data.buf_len -= dataToSELen; bufIndex = data.buf_len; } else { @@ -298,22 +308,19 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, uint8_t* in } //Store the remaining buffer for later use. - data.buf_len += (input_len - inputConsumed); - for(int i = 0; i < (input_len - inputConsumed); i++) - { - data.buf[bufIndex+i] = input[inputConsumed+i]; - } + data.buf_len += (input.size() - inputConsumed); + std::copy(input.begin() + inputConsumed, input.end(), data.buf + bufIndex); return ErrorCode::OK; } -ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, uint8_t* data, size_t len, Operation opr, +ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, std::vector& data, Operation opr, sendDataToSE_cb cb, bool finish) { ErrorCode errorCode = ErrorCode::OK; std::vector out; if(Algorithm::AES == operationTable[operHandle].info.alg || Algorithm::TRIPLE_DES == operationTable[operHandle].info.alg) { /*Symmetric */ - if(ErrorCode::OK != (errorCode = getBlockAlignedData(operHandle, data, len, + if(ErrorCode::OK != (errorCode = getBlockAlignedData(operHandle, data, opr, out))) { return errorCode; } @@ -335,13 +342,9 @@ ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, uint8_t* d //update call and send it to SE in finish call. if(finish) { //If finish flag is true all the data has to be sent to javacard. - size_t i = 0; - for(; i < operationTable[operHandle].data.buf_len; ++i) { - out.push_back(operationTable[operHandle].data.buf[i]); - } - for(i = 0; i < len; ++i) { - out.push_back(data[i]); - } + out.insert(out.end(), operationTable[operHandle].data.buf, operationTable[operHandle].data.buf + + operationTable[operHandle].data.buf_len); + out.insert(out.end(), data.begin(), data.end()); //As buffered data is already consumed earse the buffer. if(operationTable[operHandle].data.buf_len != 0) { memset(operationTable[operHandle].data.buf, 0x00, sizeof(operationTable[operHandle].data.buf)); @@ -355,21 +358,14 @@ ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, uint8_t* d //256 and for EC it should not be more than 32. This validation is already happening in //validateInputData function. size_t bufIndex = operationTable[operHandle].data.buf_len; - size_t pos = 0; - for(; pos < len; ++pos) - { - operationTable[operHandle].data.buf[bufIndex+pos] = data[pos]; - } - operationTable[operHandle].data.buf_len += pos; + std::copy(data.begin(), data.end(), operationTable[operHandle].data.buf + bufIndex); + operationTable[operHandle].data.buf_len += data.size(); } } else { /* With Digest */ - for(size_t j=0; j < len; ++j) - { - out.push_back(data[j]); - } + out.insert(out.end(), data.begin(), data.end()); //if len=0, then no need to call the callback, since there is no information to be send to javacard, // but if finish flag is true irrespective of length the callback should be called. - if(len != 0 || finish) { + if(!out.empty() || finish) { if(ErrorCode::OK != (errorCode = cb(out, finish))) { return errorCode; } diff --git a/HAL/keymaster/include/JavacardOperationContext.h b/HAL/keymaster/include/JavacardOperationContext.h index 53d08dd9..809ac744 100644 --- a/HAL/keymaster/include/JavacardOperationContext.h +++ b/HAL/keymaster/include/JavacardOperationContext.h @@ -136,14 +136,14 @@ class OperationContext { * reamining data for update calls only. For finish calls it extracts all the buffered data combines it with * input data. */ - ErrorCode getBlockAlignedData(uint64_t operHandle, uint8_t* input, size_t input_len, Operation opr, std::vector& - out); + ErrorCode getBlockAlignedData(uint64_t operHandle, std::vector& input, + Operation opr, std::vector& out); /** * This function sends the data back to the caller using callback functions. It does some processing on input data * for Asymmetic operations. */ - ErrorCode handleInternalUpdate(uint64_t operHandle, uint8_t* data, size_t len, Operation opr, - sendDataToSE_cb cb, bool finish=false); + ErrorCode handleInternalUpdate(uint64_t operHandle, std::vector& data, Operation opr, + sendDataToSE_cb cb, bool finish = false); }; diff --git a/ProvisioningTool/sample_json_cf.txt b/ProvisioningTool/sample_json_cf.txt index f9667dba..709c9de2 100644 --- a/ProvisioningTool/sample_json_cf.txt +++ b/ProvisioningTool/sample_json_cf.txt @@ -15,7 +15,7 @@ "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", "boot_state": 2, - "device_locked": 0 + "device_locked": 1 }, "attest_key": "test_resources/batch_key.der", "attest_cert_chain": [ From 8003cc6f42597fdd3662e90b5976f211116eb82d Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Fri, 29 Oct 2021 15:28:19 +0000 Subject: [PATCH 05/33] CTS Fixes --- .../4.1/JavacardOperationContext.cpp | 54 +++++++++++-------- .../include/JavacardOperationContext.h | 3 +- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/HAL/keymaster/4.1/JavacardOperationContext.cpp b/HAL/keymaster/4.1/JavacardOperationContext.cpp index 34e9d8e1..46816cfc 100644 --- a/HAL/keymaster/4.1/JavacardOperationContext.cpp +++ b/HAL/keymaster/4.1/JavacardOperationContext.cpp @@ -220,17 +220,19 @@ ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector& input, Operation opr, std::vector& out) { - size_t dataToSELen = 0;/*Length of the data to be send to the Applet.*/ - size_t inputConsumed = 0;/*Length of the data consumed from input */ - size_t blockSize = 0; BufferedData& data = operationTable[operHandle].data; + int dataToSELen = 0;/*Length of the data to be send to the Applet.*/ + int inputConsumed = 0;/*Length of the data consumed from input */ + int bufferLengthConsumed = 0; /* Length of the data consumed from Buffer */ + int blockSize = 0; int bufIndex = data.buf_len; if(Algorithm::AES == operationTable[operHandle].info.alg) { blockSize = AES_BLOCK_SIZE; @@ -244,18 +246,27 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, std::vector //Copy the buffer to be send to SE. out.insert(out.end(), data.buf, data.buf + data.buf_len); dataToSELen = data.buf_len + input.size(); + bufferLengthConsumed = data.buf_len; } else { /*Update */ //Calculate the block sized length on combined input of both buffered data and input data. - //For symmetric ciphers, decryption operation and PKCS7 padding mode or AES GCM operation save the last 16 bytes - //of block and send this block in finish operation. This is done to make sure that there will be always a 16 - //bytes of data left for finish operation so that javacard Applet may remove PKCS7 padding if any or get the tag - //data for AES GCM operation for authentication purpose. + // AES/TDES, ECB and CBC Modes: + // Buffer till (blockSize - 1) bytes of data is received. + // AES GCM(Encrypt) or CTR Modes: + // No Buffering. + // AES/TDES, Decrypt PKCS7 Padding: + // Buffer till blockSize of data is received. + // AES GCM Decrypt: + // Buffer tag length bytes of data. + //For symmetric ciphers, decryption operation and PKCS7 padding mode or AES GCM operation save the last + // blockSize/tagSize bytes of data and send this block in finish operation. This is done to make sure + // that there will be always a blockSize/tagSize bytes of data left for finish operation so that javacard + // Applet may remove PKCS7 padding if any or get the tag data for AES GCM operation for authentication purpose. if (operationTable[operHandle].info.pad == PaddingMode::PKCS7 && operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { /* Buffer till we receive more than blockSize of data of atleast one byte*/ dataToSELen = ((data.buf_len + input.size())/blockSize) * blockSize; - size_t remaining = ((data.buf_len + input.size()) % blockSize); + int remaining = ((data.buf_len + input.size()) % blockSize); if (dataToSELen >= blockSize && remaining == 0) { dataToSELen -= blockSize; } @@ -276,9 +287,8 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, std::vector } //Copy data to be send to SE from buffer, only if atleast a minimum block aligned size is available. if(dataToSELen > 0) { - for(size_t pos = 0; pos < std::min(dataToSELen, data.buf_len); pos++) { - out.push_back(data.buf[pos]); - } + bufferLengthConsumed = (dataToSELen > data.buf_len) ? data.buf_len : dataToSELen; + out.insert(out.end(), data.buf, data.buf + bufferLengthConsumed); } } @@ -290,13 +300,15 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, std::vector inputConsumed = (data.buf_len > dataToSELen) ? 0 : (dataToSELen - data.buf_len); // Copy the buffer to be send to SE. - out.insert(out.end(), input.begin(), input.begin() + inputConsumed); + if (inputConsumed > 0) { + out.insert(out.end(), input.begin(), input.begin() + inputConsumed); + } - if(data.buf_len > dataToSELen) { - //Only blockAlignedLen data is consumed from buffer so reorder the buffer data. - memcpy(data.buf, (data.buf + dataToSELen), (data.buf_len - dataToSELen)); - memset((data.buf + data.buf_len - dataToSELen), 0x00, dataToSELen); - data.buf_len -= dataToSELen; + if (bufferLengthConsumed < data.buf_len) { + // Only a portion of data is consumed from buffer so reorder the buffer data. + memmove(data.buf, (data.buf + bufferLengthConsumed), (data.buf_len - bufferLengthConsumed)); + memset((data.buf + data.buf_len - bufferLengthConsumed), 0x00, bufferLengthConsumed); + data.buf_len -= bufferLengthConsumed; bufIndex = data.buf_len; } else { // All the data is consumed so clear buffer diff --git a/HAL/keymaster/include/JavacardOperationContext.h b/HAL/keymaster/include/JavacardOperationContext.h index 809ac744..ff12dbb1 100644 --- a/HAL/keymaster/include/JavacardOperationContext.h +++ b/HAL/keymaster/include/JavacardOperationContext.h @@ -49,7 +49,7 @@ enum class Operation; */ struct BufferedData { uint8_t buf[MAX_BUF_SIZE]; - size_t buf_len; + uint32_t buf_len; }; /** @@ -61,6 +61,7 @@ struct OperationInfo { Digest digest; PaddingMode pad; BlockMode mode; + uint32_t macLength; }; /** From 3f78abfa4c3447603a1adff0f93386b36e1c986c Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Fri, 29 Oct 2021 23:04:23 +0000 Subject: [PATCH 06/33] Reduced writes in SEProvider --- .../keymaster/KMAndroidSEProvider.java | 147 ++++++------------ .../javacard/keymaster/KMOperationImpl.java | 40 ++--- .../javacard/keymaster/KMKeymasterApplet.java | 2 +- 3 files changed, 64 insertions(+), 125 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 230725c1..89f5ae15 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -117,6 +117,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. + private static final short MAX_OPERATIONS = 4; final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, @@ -273,10 +274,7 @@ private boolean isSignerAlgorithm(byte alg) { private void initializeOperationPool() { short index = 0; while (index < 4) { - operationPool[index] = new KMInstance(); - ((KMInstance) operationPool[index]).instanceCount = 1; - ((KMInstance) operationPool[index]).object = new KMOperationImpl(); - ((KMInstance) operationPool[index]).reserved = 0; + operationPool[index] = new KMOperationImpl(); index++; } } @@ -285,10 +283,7 @@ private void initializeOperationPool() { private void initializeSigPool() { short index = 0; while (index < SIG_ALGS.length) { - sigPool[index] = new KMInstance(); - ((KMInstance) sigPool[index]).instanceCount = 1; - ((KMInstance) sigPool[index]).object = getSignatureInstance(SIG_ALGS[index]); - ((KMInstance) sigPool[index]).reserved = 0; + sigPool[index] = getSignatureInstance(SIG_ALGS[index]); index++; } } @@ -312,44 +307,46 @@ private Cipher getCipherInstance(byte alg) { } } - private byte getCipherAlgorithm(Cipher c) { - return c.getAlgorithm(); - } - // Create a cipher instance of each algorithm once. private void initializeCipherPool() { short index = 0; while (index < CIPHER_ALGS.length) { - cipherPool[index] = new KMInstance(); - ((KMInstance) cipherPool[index]).instanceCount = 1; - ((KMInstance) cipherPool[index]).object = getCipherInstance(CIPHER_ALGS[index]); - ((KMInstance) cipherPool[index]).reserved = 0; + cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]); index++; } } private KMOperationImpl getOperationInstanceFromPool() { - return (KMOperationImpl) getInstanceFromPool(operationPool, (byte) 0x00); - } - - public void releaseOperationInstance(KMOperationImpl operation) { - releaseInstance(operationPool, operation); + short index = 0; + KMOperationImpl impl; + while (index < operationPool.length) { + impl = (KMOperationImpl) operationPool[index]; + // Mode is always set. so compare using mode value. + if (impl.getMode() == KMType.INVALID_VALUE) { + return impl; + } + index++; + } + return null; } private Signature getSignatureInstanceFromPool(byte alg) { return (Signature) getInstanceFromPool(sigPool, alg); } - public void releaseSignatureInstance(Signature signer) { - releaseInstance(sigPool, signer); - } - private Cipher getCipherInstanceFromPool(byte alg) { return (Cipher) getInstanceFromPool(cipherPool, alg); } - public void releaseCipherInstance(Cipher cipher) { - releaseInstance(cipherPool, cipher); + private boolean isResourceBusy(Object obj) { + short index = 0; + while (index < MAX_OPERATIONS) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj)) { + return true; + } + index++; + } + return false; } // This pool implementation can create a maximum of total 4 instances per @@ -363,79 +360,36 @@ public void releaseCipherInstance(Cipher cipher) { private Object getInstanceFromPool(Object[] pool, byte alg) { short index = 0; short instanceCount = 0; - Object object = null; boolean isCipher = isCipherAlgorithm(alg); boolean isSigner = isSignerAlgorithm(alg); - short len = (short) pool.length; - while (index < len) { + while (index < (short) pool.length) { + if (instanceCount >= MAX_OPERATIONS) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + break; + } if (null == pool[index]) { // No instance of cipher/signature with this algorithm is found - if (instanceCount < 4) { - pool[index] = new KMInstance(); JCSystem.beginTransaction(); - ((KMInstance) pool[index]).instanceCount = (byte) (++instanceCount); - if (isCipher) { - ((KMInstance) pool[index]).object = object = getCipherInstance(alg); + if (isCipher) { // Cipher + pool[index] = getCipherInstance(alg); + } else if (isSigner) { // Signature + pool[index] = getSignatureInstance(alg); } else { - // Signature - ((KMInstance) pool[index]).object = object = getSignatureInstance(alg); + KMException.throwIt(KMError.INVALID_ARGUMENT); } - ((KMInstance) pool[index]).reserved = 1; JCSystem.commitTransaction(); - break; - } else { - // Cipher/Signature instance count reached its maximum limit. - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - break; - } + return pool[index]; } - object = ((KMInstance) pool[index]).object; - if ((isCipher && (alg == getCipherAlgorithm((Cipher) object))) - || ((isSigner && (alg == ((Signature) object).getAlgorithm())))) { - instanceCount = ((KMInstance) pool[index]).instanceCount; - if (((KMInstance) pool[index]).reserved == 0) { - JCSystem.beginTransaction(); - ((KMInstance) pool[index]).reserved = 1; - JCSystem.commitTransaction(); - break; - } - } else { - if (!isCipher && !isSigner) { - // OperationImpl - if (((KMInstance) pool[index]).reserved == 0) { - JCSystem.beginTransaction(); - ((KMInstance) pool[index]).reserved = 1; - JCSystem.commitTransaction(); - break; - } - } - } - object = null; - index++; - } - return object; - } - - private void releaseInstance(Object[] pool, short index) { - if (((KMInstance) pool[index]).reserved != 0) { - JCSystem.beginTransaction(); - ((KMInstance) pool[index]).reserved = 0; - JCSystem.commitTransaction(); - } - } - - private void releaseInstance(Object[] pool, Object object) { - short index = 0; - short len = (short) pool.length; - while (index < len) { - if (pool[index] != null) { - if (object == ((KMInstance) pool[index]).object) { - releaseInstance(pool, index); - break; + if ((isCipher && (alg == ((Cipher) pool[index]).getAlgorithm())) + || ((isSigner && (alg == ((Signature) pool[index]).getAlgorithm())))) { + if (!isResourceBusy(pool[index])) { + return pool[index]; } + instanceCount++; } index++; } + return null; } public AESKey createAESKey(short keysize) { @@ -1007,6 +961,7 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, Signature signerVerifier = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, keyLength); opr = getOperationInstanceFromPool(); + opr.setMode(purpose); opr.setSignature(signerVerifier); break; default: @@ -1094,6 +1049,7 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, Signature signer = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); opr = getOperationInstanceFromPool(); + opr.setMode(purpose); opr.setSignature(signer); break; default: @@ -1279,21 +1235,12 @@ public KMPreSharedKey getPresharedKey() { return (KMPreSharedKey) preSharedKey; } - private void releasePool(Object[] pool) { + @Override + public void releaseAllOperations() { short index = 0; - short len = (short) pool.length; - while (index < len) { - if (pool[index] != null) { - releaseInstance(pool, index); - } + while (index < operationPool.length) { + ((KMOperationImpl) operationPool[index]).abort(); index++; } } - - @Override - public void releaseAllOperations() { - releasePool(cipherPool); - releasePool(sigPool); - releasePool(operationPool); - } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index aa133bda..3fd3cbe2 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -87,15 +87,20 @@ public void setCipher(Cipher cipher) { public void setSignature(Signature signer) { operationInst[0] = signer; } + + public boolean isResourceMatches(Object object) { + return operationInst[0] == object; + } + - private void resetCipher() { + private void reset() { operationInst[0] = null; - parameters[MAC_LENGTH_OFFSET] = 0; + parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; - parameters[BLOCK_MODE_OFFSET] = 0; - parameters[OPER_MODE_OFFSET] = 0; - parameters[CIPHER_ALG_OFFSET] = 0; - parameters[PADDING_OFFSET] = 0; + parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE;; + parameters[OPER_MODE_OFFSET] = KMType.INVALID_VALUE;; + parameters[CIPHER_ALG_OFFSET] = KMType.INVALID_VALUE;; + parameters[PADDING_OFFSET] = KMType.INVALID_VALUE;; } @Override @@ -196,8 +201,7 @@ public short finish(byte[] inputDataBuf, short inputDataStart, } } finally { KMAndroidSEProvider.getInstance().clean(); - KMAndroidSEProvider.getInstance().releaseCipherInstance(cipher); - resetCipher(); + reset(); } return len; } @@ -210,8 +214,7 @@ public short sign(byte[] inputDataBuf, short inputDataStart, len = ((Signature) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart); } finally { - KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); - operationInst[0] = null; + reset(); } return len; } @@ -224,25 +227,14 @@ public boolean verify(byte[] inputDataBuf, short inputDataStart, ret = ((Signature) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength); } finally { - KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); - operationInst[0] = null; + reset(); } return ret; } @Override public void abort() { - if (operationInst[0] != null) { - if (parameters[OPER_MODE_OFFSET] == KMType.ENCRYPT || - parameters[OPER_MODE_OFFSET] == KMType.DECRYPT) { - KMAndroidSEProvider.getInstance().releaseCipherInstance((Cipher) operationInst[0]); - resetCipher(); - } else { - KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); - } - operationInst[0] = null; - } - KMAndroidSEProvider.getInstance().releaseOperationInstance(this); + reset(); } @Override @@ -258,4 +250,4 @@ public short getAESGCMOutputSize(short dataSize, short macLength) { return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); } } -} \ No newline at end of file +} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index df0fa1a2..3ac736b0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -490,8 +490,8 @@ && isProvisioningComplete())) { sendError(apdu, KMException.getReason()); exception.clear(); } catch (ISOException exp) { - sendError(apdu, mapISOErrorToKMError(exp.getReason())); freeOperations(); + sendError(apdu, mapISOErrorToKMError(exp.getReason())); } catch (CryptoException e) { freeOperations(); sendError(apdu, mapCryptoErrorToKMError(e.getReason())); From db9106473ef9d18a1a11da8e8195f4473133c506 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sat, 30 Oct 2021 07:04:06 +0000 Subject: [PATCH 07/33] Updated Provision json sample files --- ProvisioningTool/sample_json_cf.txt | 4 ++-- ProvisioningTool/sample_json_gf.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ProvisioningTool/sample_json_cf.txt b/ProvisioningTool/sample_json_cf.txt index 709c9de2..486374f2 100644 --- a/ProvisioningTool/sample_json_cf.txt +++ b/ProvisioningTool/sample_json_cf.txt @@ -12,9 +12,9 @@ "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", "set_boot_params": { "boot_patch_level": 0, - "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", + "verified_boot_key": "268CCCE87338C993759F96124A232710E4ECFF38A83E96DC74765CB2DA89A787", "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", - "boot_state": 2, + "boot_state": 0, "device_locked": 1 }, "attest_key": "test_resources/batch_key.der", diff --git a/ProvisioningTool/sample_json_gf.txt b/ProvisioningTool/sample_json_gf.txt index 0974ec0a..89ad6c3b 100644 --- a/ProvisioningTool/sample_json_gf.txt +++ b/ProvisioningTool/sample_json_gf.txt @@ -12,10 +12,10 @@ "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", "set_boot_params": { "boot_patch_level": 0, - "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", + "verified_boot_key": "268CCCE87338C993759F96124A232710E4ECFF38A83E96DC74765CB2DA89A787", "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", - "boot_state": 2, - "device_locked": 0 + "boot_state": 0, + "device_locked": 1 }, "attest_key": "test_resources/batch_key.der", "attest_cert_chain": [ From cdb65262615662f8911ab4bb0771e9b42a757a0c Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sat, 30 Oct 2021 22:44:19 +0000 Subject: [PATCH 08/33] Fixed CTS issues --- .../keymaster/KMAndroidSEProvider.java | 17 ++++------- .../javacard/keymaster/KMJCardSimulator.java | 16 ++++------- .../javacard/keymaster/KMKeymasterApplet.java | 28 +++++-------------- .../javacard/keymaster/KMRepository.java | 16 ++++++++--- .../javacard/keymaster/KMSEProvider.java | 13 ++++----- .../android.hardware.strongbox_keystore.xml | 1 + 6 files changed, 37 insertions(+), 54 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 89f5ae15..3e7ebbdc 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -368,8 +368,7 @@ private Object getInstanceFromPool(Object[] pool, byte alg) { break; } if (null == pool[index]) { - // No instance of cipher/signature with this algorithm is found - JCSystem.beginTransaction(); + // No instance of cipher/signature with this algorithm is found if (isCipher) { // Cipher pool[index] = getCipherInstance(alg); } else if (isSigner) { // Signature @@ -377,7 +376,6 @@ private Object getInstanceFromPool(Object[] pool, byte alg) { } else { KMException.throwIt(KMError.INVALID_ARGUMENT); } - JCSystem.commitTransaction(); return pool[index]; } if ((isCipher && (alg == ((Cipher) pool[index]).getAlgorithm())) @@ -1086,7 +1084,7 @@ public void clearCertificateChain() { //This function supports multi-part request data. @Override - public void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen) { + public void persistCertificateChain(byte[] buf, short offset, short len) { // _____________________________________________________ // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... // |_________|________|_________|_______|________|_______ @@ -1094,17 +1092,12 @@ public void persistPartialCertificateChain(byte[] buf, short offset, short len, // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - short persistedLen = Util.getShort(certificateChain, (short) 0); - if (persistedLen > totalLen) { + if (len > (short) (CERT_CHAIN_MAX_SIZE - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(certificateChain, (short) 0, (short) (len + persistedLen)); - Util.arrayCopyNonAtomic(buf, offset, certificateChain, - (short) (persistedLen + 2), len); + Util.setShort(certificateChain, (short) 0, len); + Util.arrayCopyNonAtomic(buf, offset, certificateChain, (short) 2, len); JCSystem.commitTransaction(); } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index 46bd03aa..d66825ba 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -1199,9 +1199,10 @@ public void clearCertificateChain() { JCSystem.commitTransaction(); } + + //This function supports multi-part request data. @Override - public void persistPartialCertificateChain(byte[] buf, short offset, - short len, short totalLen) { + public void persistCertificateChain(byte[] buf, short offset, short len) { // _____________________________________________________ // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... // |_________|________|_________|_______|________|_______ @@ -1209,17 +1210,12 @@ public void persistPartialCertificateChain(byte[] buf, short offset, // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - short persistedLen = Util.getShort(certificateChain, (short) 0); - if (persistedLen > totalLen) { + if (len > (short) (CERT_CHAIN_MAX_SIZE - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(certificateChain, (short) 0, (short) (len + persistedLen)); - Util.arrayCopyNonAtomic(buf, offset, certificateChain, - (short) (persistedLen + 2), len); + Util.setShort(certificateChain, (short) 0, len); + Util.arrayCopyNonAtomic(buf, offset, certificateChain, (short) 2, len); JCSystem.commitTransaction(); } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 3ac736b0..b35b16f1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -747,30 +747,16 @@ private void processProvisionAttestationCertChainCmd(APDU apdu) { //Clear the previous certificate chain. seProvider.clearCertificateChain(); } - byte[] srcBuffer = apdu.getBuffer(); - short recvLen = apdu.setIncomingAndReceive(); - short srcOffset = apdu.getOffsetCdata(); - bufferProp[BUF_LEN_OFFSET] = apdu.getIncomingLength(); - bufferProp[BUF_START_OFFSET] = repository.alloc(bufferProp[BUF_LEN_OFFSET]); - short bytesRead = 0; - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - recvLen); - // tmpVariables[1] holds the total length + Header length. + receiveIncoming(apdu); tmpVariables[1] = decoder.readCertificateChainLengthAndHeaderLen((byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], recvLen); - while (recvLen > 0 && ((short) bytesRead <= bufferProp[BUF_LEN_OFFSET])) { - seProvider.persistPartialCertificateChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - recvLen, bufferProp[BUF_LEN_OFFSET]); - bytesRead += recvLen; - recvLen = apdu.receiveBytes(srcOffset); - if (recvLen > 0) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - recvLen); - } - } - if (tmpVariables[1] != bytesRead) { + bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); + if (tmpVariables[1] != bufferProp[BUF_LEN_OFFSET]) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } + seProvider.persistCertificateChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], + bufferProp[BUF_LEN_OFFSET]); + //reclaim memory + repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); } private void processProvisionAttestationKey(APDU apdu) { diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 2bc3d735..5528a86a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -398,7 +398,9 @@ private short dataAlloc(short length) { if (((short) (dataIndex + length)) > dataTable.length) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + JCSystem.beginTransaction(); dataIndex += length; + JCSystem.commitTransaction(); return (short) (dataIndex - length); } @@ -427,34 +429,40 @@ public byte[] getDataTable() { } 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); + // Begin Transaction + 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(); + // End Transaction } else { if (len != dataLen) { KMException.throwIt(KMError.UNKNOWN_ERROR); } dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + // Begin Transaction + JCSystem.beginTransaction(); Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); + // End Transaction } - JCSystem.commitTransaction(); } private short readDataEntry(short id, byte[] buf, short offset) { diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 167aa5b2..b9c2b9b7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -223,7 +223,7 @@ boolean aesGCMDecrypt( * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as * defined in android keymaster hal definition. * - * @param instance of pre-shared key. + * @param hmacKey instance of pre-shared key. * @param label is the label to be used for ckdf. * @param labelStart is the start of label. * @param labelLen is the length of the label. @@ -272,7 +272,7 @@ short hmacSign( * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive * the key, which is used to encrypt the keyblob. * - * @param instance of masterkey. + * @param masterKey instance of masterkey. * @param data is the buffer containing data to be signed. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -281,7 +281,7 @@ short hmacSign( * @return length of the signature buffer in bytes. */ short hmacKDF( - KMMasterKey masterkey, + KMMasterKey masterKey, byte[] data, short dataStart, short dataLength, @@ -347,7 +347,7 @@ short rsaDecipherOAEP256( /** * This is a oneshot operation that signs the data using EC private key. * - * @param instance of KMAttestationKey. + * @param ecPrivKey instance of KMAttestationKey. * @param inputDataBuf is the buffer of the input data. * @param inputDataStart is the start of the input data buffer. * @param inputDataLength is the length of the inpur data buffer in bytes. @@ -445,14 +445,13 @@ KMOperation initAsymmetricOperation( KMAttestationCert getAttestationCert(boolean rsaCert); /** - * This operation persists the certificate chain in the persistent memory in multiple requests. + * This operation persists the certificate chain in the persistent memory. * * @param buf buffer containing certificate chain. * @param offset is the start of the buffer. * @param len is the length of the buffer. - * @param totalLen is the total length of cert chain. */ - void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen); + void persistCertificateChain(byte[] buf, short offset, short len); /** * This operation clears the certificate chain from persistent memory. diff --git a/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml b/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml index ee3ed8fa..4418ca77 100644 --- a/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml +++ b/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml @@ -13,4 +13,5 @@ + From 08bde9ec4dd316e0d433c9f4d6cc207ed3e091e1 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sun, 31 Oct 2021 15:38:31 +0000 Subject: [PATCH 09/33] Add configurations class --- .../keymaster/KMAndroidSEProvider.java | 9 ++- ...{KMInstance.java => KMConfigurations.java} | 13 +-- .../javacard/keymaster/KMJCardSimulator.java | 8 +- .../javacard/keymaster/KMKeymasterApplet.java | 81 ++++++------------- ProvisioningTool/src/provision.cpp | 19 ++--- 5 files changed, 53 insertions(+), 77 deletions(-) rename Applet/AndroidSEProvider/src/com/android/javacard/keymaster/{KMInstance.java => KMConfigurations.java} (62%) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 3e7ebbdc..c716770c 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -116,7 +116,6 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final byte KEYSIZE_256_OFFSET = 0x01; public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; - public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. private static final short MAX_OPERATIONS = 4; final byte[] CIPHER_ALGS = { @@ -218,7 +217,8 @@ public KMAndroidSEProvider() { rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); //Allocate buffer for certificate chain. if (!isUpgrading()) { - certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + // First 2 bytes for length. + certificateChain = new byte[(short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2)]; // Initialize attestationKey and preShared key with zeros. Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); // Create attestation key of P-256 curve. @@ -1078,7 +1078,8 @@ public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, @Override public void clearCertificateChain() { JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(certificateChain, (short) 0, CERT_CHAIN_MAX_SIZE, (byte) 0); + Util.arrayFillNonAtomic(certificateChain, (short) 0, + (short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2), (byte) 0); JCSystem.commitTransaction(); } @@ -1092,7 +1093,7 @@ public void persistCertificateChain(byte[] buf, short offset, short len) { // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - if (len > (short) (CERT_CHAIN_MAX_SIZE - 2)) { + if (len > KMConfigurations.CERT_CHAIN_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java similarity index 62% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java rename to Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 5178d4e2..14877f8a 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -8,16 +8,19 @@ * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, + * 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 KMInstance { +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; - public byte reserved; - public Object object; - public byte instanceCount; + // Maximum cert chain size + public static final short CERT_CHAIN_MAX_SIZE = 2500; } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index d66825ba..f89012fa 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -72,7 +72,6 @@ public class KMJCardSimulator implements KMSEProvider { public static final short MAX_RND_NUM_SIZE = 64; public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - private static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. private static final short RSA_KEY_SIZE = 256; @@ -114,7 +113,7 @@ public KMJCardSimulator() { aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); // various ciphers //Allocate buffer for certificate chain. - certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + certificateChain = new byte[(short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2)]; jCardSimulator = this; } @@ -1195,7 +1194,8 @@ public short ecSign256(KMAttestationKey attestationKey, @Override public void clearCertificateChain() { JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(certificateChain, (short) 0, CERT_CHAIN_MAX_SIZE, (byte) 0); + Util.arrayFillNonAtomic(certificateChain, (short) 0, + (short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2), (byte) 0); JCSystem.commitTransaction(); } @@ -1210,7 +1210,7 @@ public void persistCertificateChain(byte[] buf, short offset, short len) { // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - if (len > (short) (CERT_CHAIN_MAX_SIZE - 2)) { + if (len > KMConfigurations.CERT_CHAIN_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b35b16f1..b33544f6 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -2087,11 +2087,15 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { KMException.throwIt(KMError.INVALID_MAC_LENGTH); } short key = repository.getComputedHmacKey(); - - if (!verifyVerificationTokenMacInLittleEndian(verToken, key, scratchPad) && - !verifyVerificationTokenMacInBigEndian(verToken, key, scratchPad)) { - // Throw Exception if none of the combination works. - KMException.throwIt(KMError.VERIFICATION_FAILED); + boolean verify; + if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { + verify = verifyVerificationTokenMacInLittleEndian(verToken, key, scratchPad); + } else { + verify = verifyVerificationTokenMacInBigEndian(verToken, key, scratchPad); + } + if (!verify) { + // Throw Exception if none of the combination works. + KMException.throwIt(KMError.VERIFICATION_FAILED); } } @@ -2831,48 +2835,10 @@ private void authTokenMatches(short userSecureIdsPtr, short authType, KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } - - private boolean verifyHwTokenMacAsPerSpecification(short hwToken, short key, byte[] scratchPad) { - 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(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - } private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, 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); @@ -2918,6 +2884,8 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, byte[] scr } private boolean verifyHwTokenMacInLittleEndian(short hwToken, short key, 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); @@ -2936,12 +2904,12 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, short key, byte[] len += 8; // concatenate authenticator type - 4 bytes ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - //scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - scratchPad[len] = KMEnum.cast(ptr).getVal(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); len += 4; - // concatenate timestamp -8 bytes + // concatenate timestamp - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; ptr = KMHardwareAuthToken.cast(hwToken).getMac(); @@ -2966,12 +2934,15 @@ private void validateHwToken(short hwToken, byte[] scratchPad) { KMException.throwIt(KMError.INVALID_MAC_LENGTH); } short key = repository.getComputedHmacKey(); - - if (!verifyHwTokenMacInLittleEndian(hwToken, key, scratchPad) && - !verifyHwTokenMacInBigEndian(hwToken, key, scratchPad) && - !verifyHwTokenMacAsPerSpecification(hwToken, key, scratchPad)) { - // Throw Exception if none of the combination works. - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + boolean verify; + if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { + verify = verifyHwTokenMacInLittleEndian(hwToken, key, scratchPad); + } else { + verify = verifyHwTokenMacInBigEndian(hwToken, key, scratchPad); + } + if (!verify) { + // Throw Exception if none of the combination works. + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index 69ac3621..997774e4 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -152,7 +152,8 @@ int provisionData(std::shared_ptr& pSocket, std::string apdu, s return SUCCESS; } -int provisionData(std::shared_ptr& pSocket, const char* jsonKey, std::vector& response) { +int provisionData(std::shared_ptr& pSocket, const char* jsonKey) { + std::vector response; Json::Value val = root.get(jsonKey, Json::Value::nullRef); if (!val.isNull()) { if (val.isString()) { @@ -200,21 +201,21 @@ int processInputFile() { if (keymasterVersion == KEYMASTER_VERSION) { printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); - if (0 != provisionData(pSocket, kAttestKey, response) || - 0 != provisionData(pSocket, kAttestCertChain, response) || - 0 != provisionData(pSocket, kAttestCertParams, response)) { + if (0 != provisionData(pSocket, kAttestKey) || + 0 != provisionData(pSocket, kAttestCertChain) || + 0 != provisionData(pSocket, kAttestCertParams)) { return FAILURE; } } else { printf("\n Selected keymint version(%f) for provisioning \n", keymasterVersion); - if ( 0 != provisionData(pSocket, kDeviceUniqueKey, response) || - 0 != provisionData(pSocket, kAdditionalCertChain, response)) { + if ( 0 != provisionData(pSocket, kDeviceUniqueKey) || + 0 != provisionData(pSocket, kAdditionalCertChain)) { return FAILURE; } } - if (0 != provisionData(pSocket, kAttestationIds, response) || - 0 != provisionData(pSocket, kSharedSecret, response) || - 0 != provisionData(pSocket, kBootParams, response)) { + if (0 != provisionData(pSocket, kAttestationIds) || + 0 != provisionData(pSocket, kSharedSecret) || + 0 != provisionData(pSocket, kBootParams)) { return FAILURE; } return SUCCESS; From ddec6bc6b8aba70337fb0fb9d4f1582cb0f7aa6d Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sun, 31 Oct 2021 17:39:23 +0000 Subject: [PATCH 10/33] Added KMConfiguration class --- .../javacard/keymaster/KMConfigurations.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java 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..14877f8a --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -0,0 +1,26 @@ +/* + * 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; + + // Maximum cert chain size + public static final short CERT_CHAIN_MAX_SIZE = 2500; +} From 5fd558af9ffd9c64a660afcbc7898ecada303462 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Wed, 3 Nov 2021 13:33:20 +0000 Subject: [PATCH 11/33] Separated the provision buffer --- .../keymaster/KMAndroidSEProvider.java | 54 ++++- .../keymaster/KMAttestationCertImpl.java | 8 +- .../javacard/keymaster/KMConfigurations.java | 2 + .../android/javacard/keymaster/KMUtils.java | 29 +-- .../keymaster/KMAttestationCertImpl.java | 9 +- .../javacard/keymaster/KMConfigurations.java | 2 + .../javacard/keymaster/KMJCardSimulator.java | 87 ++++--- .../javacard/test/KMFunctionalTest.java | 27 ++- .../javacard/keymaster/KMKeymasterApplet.java | 54 ++++- .../javacard/keymaster/KMRepository.java | 229 +++++++++++------- .../javacard/keymaster/KMSEProvider.java | 29 ++- 11 files changed, 349 insertions(+), 181 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index c716770c..4693b808 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -169,6 +169,8 @@ public class KMAndroidSEProvider implements KMSEProvider { private RandomData rng; //For storing root certificate and intermediate certificates. private byte[] certificateChain; + private byte[] certIssuer; + private byte[] certExpiry; private KMAESKey masterKey; private KMECPrivateKey attestationKey; private KMHmacKey preSharedKey; @@ -219,6 +221,9 @@ public KMAndroidSEProvider() { if (!isUpgrading()) { // First 2 bytes for length. certificateChain = new byte[(short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2)]; + certIssuer = new byte[(short) (KMConfigurations.CERT_ISSUER_SIZE + 2)]; + certExpiry = new byte[(short) (KMConfigurations.CERT_EXPIRY_SIZE + 2)]; + // Initialize attestationKey and preShared key with zeros. Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); // Create attestation key of P-256 curve. @@ -1074,18 +1079,34 @@ public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, contextStart, contextLength); return key.getKey(keyBuf, keyStart); } + + private byte[] getProvisionDataBuffer(byte dataType) { + switch(dataType) { + case CERTIFICATE_CHAIN: + return certificateChain; + case CERTIFICATE_ISSUER: + return certIssuer; + case CERTIFICATE_EXPIRY: + return certExpiry; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return null; + } @Override - public void clearCertificateChain() { + public void clearProvisionData(byte dataType) { + byte[] buffer = getProvisionDataBuffer(dataType); JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(certificateChain, (short) 0, - (short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2), (byte) 0); + Util.arrayFillNonAtomic(buffer, (short) 0, (short) (buffer.length), (byte) 0); JCSystem.commitTransaction(); } //This function supports multi-part request data. @Override - public void persistCertificateChain(byte[] buf, short offset, short len) { + public void persistProvisionData(byte dataType, byte[] buf, short offset, short len) { + // All the buffers uses first two bytes for length. The certificate chain + // is stored as shown below. // _____________________________________________________ // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... // |_________|________|_________|_______|________|_______ @@ -1093,25 +1114,28 @@ public void persistCertificateChain(byte[] buf, short offset, short len) { // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - if (len > KMConfigurations.CERT_CHAIN_MAX_SIZE) { + byte[] data = getProvisionDataBuffer(dataType); + if (len > (short) (data.length - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(certificateChain, (short) 0, len); - Util.arrayCopyNonAtomic(buf, offset, certificateChain, (short) 2, len); + Util.setShort(data, (short) 0, len); + Util.arrayCopyNonAtomic(buf, offset, data, (short) 2, len); JCSystem.commitTransaction(); } @Override - public short readCertificateChain(byte[] buf, short offset) { - short len = Util.getShort(certificateChain, (short) 0); - Util.arrayCopyNonAtomic(certificateChain, (short) 2, buf, offset, len); + public short readProvisionData(byte dataType, byte[] buf, short offset) { + byte[] data = getProvisionDataBuffer(dataType); + short len = Util.getShort(data, (short) 0); + Util.arrayCopyNonAtomic(data, (short) 2, buf, offset, len); return len; } @Override - public short getCertificateChainLength() { - return Util.getShort(certificateChain, (short) 0); + public short getProvisionDataLength(byte dataType) { + byte[] data = getProvisionDataBuffer(dataType); + return Util.getShort(data, (short) 0); } @Override @@ -1132,6 +1156,8 @@ public void clearDeviceBooted(boolean resetBootFlag) { @Override public void onSave(Element element) { element.write(certificateChain); + element.write(certIssuer); + element.write(certExpiry); KMAESKey.onSave(element, masterKey); KMECPrivateKey.onSave(element, attestationKey); KMHmacKey.onSave(element, preSharedKey); @@ -1140,6 +1166,8 @@ public void onSave(Element element) { @Override public void onRestore(Element element) { certificateChain = (byte[]) element.readObject(); + certIssuer = (byte[]) element.readObject(); + certExpiry = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); @@ -1157,7 +1185,7 @@ public short getBackupPrimitiveByteCount() { @Override public short getBackupObjectCount() { short count = - (short) (1 /*Certificate chain */ + + (short) (3 + /* Cert chain, Cert Issuer, CertExpiry */ KMAESKey.getBackupObjectCount() + KMECPrivateKey.getBackupObjectCount() + KMHmacKey.getBackupObjectCount()); diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 8abbd04e..e35853ca 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -166,12 +166,16 @@ private static void init() { @Override public KMAttestationCert verifiedBootHash(short obj) { + if (obj == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_DATA); verifiedHash = obj; return this; } @Override public KMAttestationCert verifiedBootKey(short obj) { + if (obj == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_DATA); verifiedBootKey = obj; return this; } @@ -255,6 +259,8 @@ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) { @Override public KMAttestationCert issuer(short obj) { + if (obj == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_DATA); issuer = obj; return this; } @@ -325,7 +331,7 @@ private static void pushExtensions() { // Time SEQUENCE{UTCTime, UTC or Generalized Time) private static void pushValidity() { short last = stackPtr; - if (notAfter != 0) { + if (notAfter != KMType.INVALID_VALUE) { pushBytes( KMByteBlob.cast(notAfter).getBuffer(), KMByteBlob.cast(notAfter).getStartOff(), diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 14877f8a..1db04d57 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -23,4 +23,6 @@ public class KMConfigurations { // Maximum cert chain size public static final short CERT_CHAIN_MAX_SIZE = 2500; + public static final short CERT_ISSUER_SIZE = 250; + public static final short CERT_EXPIRY_SIZE = 16; } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index 49ddd16d..88b7b4d1 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -419,19 +419,20 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { return -1; } - //i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) + // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, - short scratchPadOff) { - byte[] shiftPos = {9, 8, 7, 6, 5, 3}; - short index = 0; - while (index < (short) (shiftPos.length)) { - Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); - shiftLeft(buf, scratchPadOff, shiftPos[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++; - } - } + short scratchPadOff) { + byte[] shiftPos = {9, 8, 7, 6, 5, 3}; + short index = 0; + while (index < (short) (shiftPos.length)) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + shiftLeft(buf, scratchPadOff, shiftPos[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/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 1f242f38..6ca72904 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -160,12 +160,16 @@ private static void init() { @Override public KMAttestationCert verifiedBootHash(short obj) { + if (obj == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_DATA); verifiedHash = obj; return this; } @Override public KMAttestationCert verifiedBootKey(short obj) { + if (obj == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_DATA); verifiedBootKey = obj; return this; } @@ -249,6 +253,8 @@ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) { @Override public KMAttestationCert issuer(short obj) { + if (obj == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_DATA); issuer = obj; return this; } @@ -319,7 +325,7 @@ private static void pushExtensions() { // Time SEQUENCE{UTCTime, UTC or Generalized Time) private static void pushValidity() { short last = stackPtr; - if (notAfter != 0) { + if (notAfter != KMType.INVALID_VALUE) { pushBytes( KMByteBlob.cast(notAfter).getBuffer(), KMByteBlob.cast(notAfter).getStartOff(), @@ -664,7 +670,6 @@ private static void pushEnumTag(short tagId, byte val) { private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) { short last = stackPtr; pushInteger(buf, start, len); - // pushIntegerHeader((short) (last - stackPtr)); pushTagIdHeader(tagId, (short) (last - stackPtr)); } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 14877f8a..1db04d57 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -23,4 +23,6 @@ public class KMConfigurations { // Maximum cert chain size public static final short CERT_CHAIN_MAX_SIZE = 2500; + public static final short CERT_ISSUER_SIZE = 250; + public static final short CERT_EXPIRY_SIZE = 16; } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index f89012fa..9376bc5e 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -85,6 +85,8 @@ public class KMJCardSimulator implements KMSEProvider { private static byte[] entropyPool; private static byte[] rndNum; private byte[] certificateChain; + private byte[] certIssuer; + private byte[] certExpiry; private KMAESKey masterKey; private KMECPrivateKey attestationKey; private KMHmacKey preSharedKey; @@ -112,8 +114,10 @@ public KMJCardSimulator() { } aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); // various ciphers - //Allocate buffer for certificate chain. + //Allocate buffer for certificate chain and cert parameters. certificateChain = new byte[(short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2)]; + certIssuer = new byte[(short) (KMConfigurations.CERT_ISSUER_SIZE + 2)]; + certExpiry = new byte[(short) (KMConfigurations.CERT_EXPIRY_SIZE + 2)]; jCardSimulator = this; } @@ -1166,43 +1170,33 @@ public KMAttestationCert getAttestationCert(boolean rsaCert) { return KMAttestationCertImpl.instance(rsaCert); } - public short readCertificateChain(byte[] buf, short offset) { - short len = Util.getShort(certificateChain, (short) 0); - Util.arrayCopyNonAtomic(certificateChain, (short) 2, buf, offset, len); - return len; - } - - @Override - public short getCertificateChainLength() { - return Util.getShort(certificateChain, (short) 0); - } - - @Override - public short ecSign256(KMAttestationKey attestationKey, - byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - - ECPrivateKey key = ((KMECPrivateKey) attestationKey).getPrivateKey(); - - Signature signer = Signature - .getInstance(Signature.ALG_ECDSA_SHA_256, false); - signer.init(key, Signature.MODE_SIGN); - return signer.sign(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); + private byte[] getProvisionDataBuffer(byte dataType) { + switch(dataType) { + case CERTIFICATE_CHAIN: + return certificateChain; + case CERTIFICATE_ISSUER: + return certIssuer; + case CERTIFICATE_EXPIRY: + return certExpiry; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return null; } @Override - public void clearCertificateChain() { + public void clearProvisionedData(byte dataType) { + byte[] buffer = getProvisionDataBuffer(dataType); JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(certificateChain, (short) 0, - (short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2), (byte) 0); + Util.arrayFillNonAtomic(buffer, (short) 0, (short) (buffer.length), (byte) 0); JCSystem.commitTransaction(); } - //This function supports multi-part request data. @Override - public void persistCertificateChain(byte[] buf, short offset, short len) { + public void persistProvisionData(byte dataType, byte[] buf, short offset, short len) { + // All the buffers uses first two bytes for length. The certificate chain + // is stored as shown below. // _____________________________________________________ // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... // |_________|________|_________|_______|________|_______ @@ -1210,15 +1204,44 @@ public void persistCertificateChain(byte[] buf, short offset, short len) { // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - if (len > KMConfigurations.CERT_CHAIN_MAX_SIZE) { + byte[] data = getProvisionDataBuffer(dataType); + if (len > (short) (data.length - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(certificateChain, (short) 0, len); - Util.arrayCopyNonAtomic(buf, offset, certificateChain, (short) 2, len); + Util.setShort(data, (short) 0, len); + Util.arrayCopyNonAtomic(buf, offset, data, (short) 2, len); JCSystem.commitTransaction(); } + @Override + public short readProvisionedData(byte dataType, byte[] buf, short offset) { + byte[] data = getProvisionDataBuffer(dataType); + short len = Util.getShort(data, (short) 0); + Util.arrayCopyNonAtomic(data, (short) 2, buf, offset, len); + return len; + } + + @Override + public short getProvisionedDataLength(byte dataType) { + byte[] data = getProvisionDataBuffer(dataType); + return Util.getShort(data, (short) 0); + } + + @Override + public short ecSign256(KMAttestationKey attestationKey, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + + ECPrivateKey key = ((KMECPrivateKey) attestationKey).getPrivateKey(); + + Signature signer = Signature + .getInstance(Signature.ALG_ECDSA_SHA_256, false); + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } + @Override public boolean isBootSignalEventSupported() { return false; diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index 1e80f4b2..60fe778b 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -20,6 +20,7 @@ import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMConfigurations; import com.android.javacard.keymaster.KMJCardSimApplet; import com.android.javacard.keymaster.KMJCardSimulator; import com.android.javacard.keymaster.KMSEProvider; @@ -951,7 +952,7 @@ public void testDeviceLocked() { // create verification token short verToken = KMVerificationToken.instance(); KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short) 1)); - verToken = signVerificationToken(verToken); + verToken = signVerificationToken(verToken, KMConfigurations.TEE_MACHINE_TYPE); // device locked request deviceLock(verToken); // decrypt should fail @@ -1046,7 +1047,7 @@ private void deviceLock(short verToken) { Assert.assertEquals(respBuf[0], KMError.OK); } - private short signVerificationToken(short verToken) { + private short signVerificationToken(short verToken, byte machineType) { byte[] scratchPad = new byte[256]; byte[] authVer = "Auth Verification".getBytes(); //print(authVer,(short)0,(short)authVer.length); @@ -1058,17 +1059,29 @@ private short signVerificationToken(short verToken) { short len = (short) authVer.length; // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + if (machineType == KMConfigurations.LITTLE_ENDIAN) { + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + } else { + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + } len += 8; // concatenate timestamp -8 bytes ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + if (machineType == KMConfigurations.LITTLE_ENDIAN) { + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + } else { + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + } len += 8; // concatenate security level - 4 bytes ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + if (machineType == KMConfigurations.LITTLE_ENDIAN) { + scratchPad[len] = KMEnum.cast(ptr).getVal(); + } else { + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + } len += 4; // concatenate Parameters verified - blob of encoded data. ptr = KMVerificationToken.cast(verToken).getParametersVerified(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b33544f6..e06a0f71 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -693,10 +693,23 @@ private void processSetVersionAndPatchLevels(APDU apdu) { sendError(apdu, KMError.OK); } + + private short getProvisionedCertificateData(byte dataType) { + short len = seProvider.getProvisionedDataLength(dataType); + if (len == 0) { + KMException.throwIt(KMError.INVALID_DATA); + } + short ptr = KMByteBlob.instance(len); + seProvider.readProvisionedData( + dataType, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff()); + return ptr; + } private void processGetCertChainCmd(APDU apdu) { // Make the response - tmpVariables[0] = seProvider.getCertificateChainLength(); + tmpVariables[0] = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); short int32Ptr = buildErrorStatus(KMError.OK); //Total Extra length // Add arrayHeader and (PowerResetStatus + KMError.OK) @@ -708,7 +721,10 @@ private void processGetCertChainCmd(APDU apdu) { bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[1]).length(); // read the cert chain from non-volatile memory. Cert chain is already in // CBOR format. - seProvider.readCertificateChain((byte[]) bufferRef[0], (short) (bufferProp[BUF_START_OFFSET] + tmpVariables[2])); + seProvider.readProvisionedData( + KMSEProvider.CERTIFICATE_CHAIN, + (byte[]) bufferRef[0], + (short) (bufferProp[BUF_START_OFFSET] + tmpVariables[2])); // Encode cert chain. encoder.encodeCertChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET], int32Ptr); sendOutgoing(apdu); @@ -728,24 +744,36 @@ private void processProvisionAttestationCertParams(APDU apdu) { // save issuer - DER Encoded tmpVariables[0] = KMArray.cast(args).get((short) 0); - repository.setIssuer( + short len = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_ISSUER); + if (len != 0) { + // Clear issuer buffer. + seProvider.clearProvisionedData(KMSEProvider.CERTIFICATE_ISSUER); + } + seProvider.persistProvisionData( + KMSEProvider.CERTIFICATE_ISSUER, KMByteBlob.cast(tmpVariables[0]).getBuffer(), KMByteBlob.cast(tmpVariables[0]).getStartOff(), KMByteBlob.cast(tmpVariables[0]).length()); // save expiry time - UTC or General Time - YYMMDDhhmmssZ or YYYYMMDDhhmmssZ. tmpVariables[0] = KMArray.cast(args).get((short) 1); - repository.setCertExpiryTime( + len = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_EXPIRY); + if (len != 0) { + // Clear issuer buffer. + seProvider.clearProvisionedData(KMSEProvider.CERTIFICATE_EXPIRY); + } + seProvider.persistProvisionData( + KMSEProvider.CERTIFICATE_EXPIRY, KMByteBlob.cast(tmpVariables[0]).getBuffer(), KMByteBlob.cast(tmpVariables[0]).getStartOff(), KMByteBlob.cast(tmpVariables[0]).length()); } private void processProvisionAttestationCertChainCmd(APDU apdu) { - tmpVariables[0] = seProvider.getCertificateChainLength(); + tmpVariables[0] = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); if (tmpVariables[0] != 0) { //Clear the previous certificate chain. - seProvider.clearCertificateChain(); + seProvider.clearProvisionedData(KMSEProvider.CERTIFICATE_CHAIN); } receiveIncoming(apdu); tmpVariables[1] = decoder.readCertificateChainLengthAndHeaderLen((byte[]) bufferRef[0], @@ -753,8 +781,8 @@ private void processProvisionAttestationCertChainCmd(APDU apdu) { if (tmpVariables[1] != bufferProp[BUF_LEN_OFFSET]) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - seProvider.persistCertificateChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - bufferProp[BUF_LEN_OFFSET]); + seProvider.persistProvisionData(KMSEProvider.CERTIFICATE_CHAIN, (byte[]) bufferRef[0], + bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); //reclaim memory repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); } @@ -843,6 +871,7 @@ private void processProvisionAttestIdsCmd(APDU apdu) { repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); + repository.deleteAttIds(); // persist attestation Ids - if any is missing then exception occurs saveAttId(KMType.ATTESTATION_ID_BRAND); saveAttId(KMType.ATTESTATION_ID_DEVICE); @@ -1474,14 +1503,17 @@ private void processAttestKeyCmd(APDU apdu) { // expiry time - byte blob tmpVariables[2] = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, data[SW_PARAMETERS]); - cert.notAfter(tmpVariables[2], repository.getCertExpiryTime(), scratchPad, (short) 0); + cert.notAfter(tmpVariables[2], + getProvisionedCertificateData(KMSEProvider.CERTIFICATE_EXPIRY), + scratchPad, + (short) 0); addTags(KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(), true, cert); addTags( KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(), false, cert); cert.deviceLocked(repository.getBootLoaderLock()); - cert.issuer(repository.getIssuer()); + cert.issuer(getProvisionedCertificateData(KMSEProvider.CERTIFICATE_ISSUER)); cert.publicKey(data[PUB_KEY]); cert.verifiedBootHash(repository.getVerifiedBootHash()); @@ -1544,7 +1576,7 @@ private void addAttestationIds(KMAttestationCert cert) { storedAttId = repository.getAttId(mapToAttId(attTags[index])); // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or // Attestation IDs are deleted. - if (storedAttId == 0 || + if (storedAttId == KMType.INVALID_VALUE || isEmpty(KMByteBlob.cast(storedAttId).getBuffer(), KMByteBlob.cast(storedAttId).getStartOff(), KMByteBlob.cast(storedAttId).length())) { diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 5528a86a..e86fca68 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -29,10 +29,24 @@ */ public class KMRepository implements KMUpgradable { - // Data table configuration - public static final short DATA_INDEX_SIZE = 22; + public static final byte DEFAULT_TABLE_TABLE = 0; + public static final byte ATTEST_IDS_DATA_TABLE = 1; + // Data table configuration for attestation ids + public static final short ATTEST_IDS_DATA_INDEX_SIZE = 8; + public static final short ATTEST_IDS_DATA_TABLE_SIZE = 300; + public static final byte ATT_ID_BRAND = 0; + public static final byte ATT_ID_DEVICE = 1; + public static final byte ATT_ID_PRODUCT = 2; + public static final byte ATT_ID_SERIAL = 3; + public static final byte ATT_ID_IMEI = 4; + public static final byte ATT_ID_MEID = 5; + public static final byte ATT_ID_MANUFACTURER = 6; + public static final byte ATT_ID_MODEL = 7; + + // Data table configuration other non provisioned parameters. + public static final short DATA_INDEX_SIZE = 13; public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_MEM_SIZE = 2048; + public static final short DATA_MEM_SIZE = 300; public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -45,28 +59,19 @@ public class KMRepository implements KMUpgradable { private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; // Data table offsets - public static final byte COMPUTED_HMAC_KEY = 8; - public static final byte HMAC_NONCE = 9; - public static final byte ATT_ID_BRAND = 0; - public static final byte ATT_ID_DEVICE = 1; - public static final byte ATT_ID_PRODUCT = 2; - public static final byte ATT_ID_SERIAL = 3; - public static final byte ATT_ID_IMEI = 4; - public static final byte ATT_ID_MEID = 5; - public static final byte ATT_ID_MANUFACTURER = 6; - public static final byte ATT_ID_MODEL = 7; - public static final byte CERT_ISSUER = 10; - public static final byte CERT_EXPIRY_TIME = 11; - public static final byte BOOT_OS_VERSION = 12; - public static final byte BOOT_OS_PATCH_LEVEL = 13; - public static final byte VENDOR_PATCH_LEVEL = 14; - public static final byte BOOT_PATCH_LEVEL = 15; - public static final byte BOOT_VERIFIED_BOOT_KEY = 16; - public static final byte BOOT_VERIFIED_BOOT_HASH = 17; - public static final byte BOOT_VERIFIED_BOOT_STATE = 18; - public static final byte BOOT_DEVICE_LOCKED_STATUS = 19; - public static final byte DEVICE_LOCKED_TIME = 20; - public static final byte DEVICE_LOCKED = 21; + public static final byte 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_LEVEL = 3; + public static final byte VENDOR_PATCH_LEVEL = 4; + public static final byte BOOT_PATCH_LEVEL = 5; + public static final byte BOOT_VERIFIED_BOOT_KEY = 6; + public static final byte BOOT_VERIFIED_BOOT_HASH = 7; + public static final byte BOOT_VERIFIED_BOOT_STATE = 8; + public static final byte BOOT_DEVICE_LOCKED_STATUS = 9; + public static final byte DEVICE_LOCKED_TIME = 10; + public static final byte DEVICE_LOCKED = 11; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 12; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -83,6 +88,8 @@ public class KMRepository implements KMUpgradable { public static final short MAX_OPS = 4; public static final byte BOOT_KEY_MAX_SIZE = 32; public static final byte BOOT_HASH_MAX_SIZE = 32; + private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; + // Class Attributes private Object[] operationStateTable; @@ -90,6 +97,8 @@ public class KMRepository implements KMUpgradable { private short[] heapIndex; private byte[] dataTable; private short dataIndex; + private byte[] attestIdsTable; + private short attestIdsIdsIndex; private short[] reclaimIndex; // This variable is used to monitor the power reset status as the Applet does not get // any power reset event. Initially the value of this variable is set to POWER_RESET_STATUS_FLAG. @@ -391,16 +400,17 @@ public short alloc(short length) { return (short) (heapIndex[0] - length); } - private short dataAlloc(short length) { + private short dataAlloc(byte dataTableType, short length) { + byte[] dataTable = getDataTable(dataTableType); + short dataIndex = getDataTableIndex(dataTableType); if (length < 0) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (((short) (dataIndex + length)) > dataTable.length) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - JCSystem.beginTransaction(); dataIndex += length; - JCSystem.commitTransaction(); + setDataTableIndex(dataTableType, dataIndex); return (short) (dataIndex - length); } @@ -411,24 +421,43 @@ private void newDataTable(boolean isUpgrading) { dataTable = new byte[DATA_MEM_SIZE]; dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); } + if (attestIdsTable == null) { + attestIdsTable = new byte[ATTEST_IDS_DATA_TABLE_SIZE]; + attestIdsIdsIndex = (short) (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + } } } - public void restoreData(short blob) { - JCSystem.beginTransaction(); - Util.arrayCopy( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), dataTable, - (short) 0, - KMByteBlob.cast(blob).length() - ); - JCSystem.commitTransaction(); + public byte[] getDataTable(byte dataTableType) { + if (dataTableType == ATTEST_IDS_DATA_TABLE) { + return this.attestIdsTable; + } else { + return this.dataTable; + } } - public byte[] getDataTable() { - return dataTable; + private short getDataTableIndex(byte dataTableType) { + if (dataTableType == ATTEST_IDS_DATA_TABLE) { + return this.attestIdsIdsIndex; + } else { + return this.dataIndex; + } } - private void clearDataEntry(short id) { + private void setDataTableIndex(byte dataTableType, short index) { + if (dataTableType == ATTEST_IDS_DATA_TABLE) { + JCSystem.beginTransaction(); + this.attestIdsIdsIndex = index; + JCSystem.commitTransaction(); + } else { + JCSystem.beginTransaction(); + this.dataIndex = index; + JCSystem.commitTransaction(); + } + } + + private void clearDataEntry(byte dataTableType, short id) { + byte[] dataTable = getDataTable(dataTableType); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen != 0) { @@ -439,12 +468,13 @@ private void clearDataEntry(short id) { } } - private void writeDataEntry(short id, byte[] buf, short offset, short len) { + private void writeDataEntry(byte dataTableType, short id, byte[] buf, short offset, short len) { short dataPtr; + byte[] dataTable = getDataTable(dataTableType); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen == 0) { - dataPtr = dataAlloc(len); + dataPtr = dataAlloc(dataTableType, len); // Begin Transaction JCSystem.beginTransaction(); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); @@ -465,7 +495,8 @@ private void writeDataEntry(short id, byte[] buf, short offset, short len) { } } - private short readDataEntry(short id, byte[] buf, short offset) { + private short readDataEntry(byte dataTableType, short id, byte[] buf, short offset) { + byte[] dataTable = getDataTable(dataTableType); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (len != 0) { @@ -479,11 +510,28 @@ private short readDataEntry(short id, byte[] buf, short offset) { return len; } - private short dataLength(short id) { + private void clearDataEntry(short id) { + clearDataEntry(DEFAULT_TABLE_TABLE, id); + } + + private void writeDataEntry(short id, byte[] buf, short offset, short len) { + writeDataEntry(DEFAULT_TABLE_TABLE, id, buf, offset, len); + } + + private short readDataEntry(short id, byte[] buf, short offset) { + return readDataEntry(DEFAULT_TABLE_TABLE, id, buf, offset); + } + + private short dataLength(byte dataTableType, short id) { + byte[] dataTable = getDataTable(dataTableType); id = (short) (id * DATA_INDEX_ENTRY_SIZE); return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); } + private short dataLength(short id) { + return dataLength(DEFAULT_TABLE_TABLE, id); + } + public byte[] getHeap() { return heap; } @@ -497,55 +545,38 @@ public short getComputedHmacKey() { } public void persistAttId(byte id, byte[] buf, short start, short len) { - writeDataEntry(id, buf, start, len); + writeDataEntry(ATTEST_IDS_DATA_TABLE, id, buf, start, len); } public short getAttId(byte id) { - return readData(id); + return readData(ATTEST_IDS_DATA_TABLE, id); } public void deleteAttIds() { - clearDataEntry(ATT_ID_BRAND); - clearDataEntry(ATT_ID_MEID); - clearDataEntry(ATT_ID_DEVICE); - clearDataEntry(ATT_ID_IMEI); - clearDataEntry(ATT_ID_MODEL); - clearDataEntry(ATT_ID_PRODUCT); - clearDataEntry(ATT_ID_SERIAL); - clearDataEntry(ATT_ID_MANUFACTURER); - } - - public short getIssuer() { - return readData(CERT_ISSUER); + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic(attestIdsTable, (short) 0, (short) attestIdsTable.length, (byte) 0); + attestIdsIdsIndex = (short) (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + JCSystem.commitTransaction(); } - 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; + public short readData(byte dataTableType, short id) { + short len = dataLength(dataTableType, id); + if (len != 0) { + short blob = KMByteBlob.instance(len); + readDataEntry(dataTableType, id, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff()); + return blob; } - return blob; - } - - public void setIssuer(byte[] buf, short start, short len) { - writeDataEntry(CERT_ISSUER, buf, start, len); + return KMType.INVALID_VALUE; } - - public short getCertExpiryTime() { - return readData(CERT_EXPIRY_TIME); - } - - public void setCertExpiryTime(byte[] buf, short start, short len) { - writeDataEntry(CERT_EXPIRY_TIME, buf, start, len); + public short readData(short id) { + return readData(DEFAULT_TABLE_TABLE, id); } - 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 { @@ -555,7 +586,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 { @@ -565,7 +596,7 @@ public short getVendorPatchLevel() { public short getBootPatchLevel() { short blob = readData(BOOT_PATCH_LEVEL); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -575,7 +606,7 @@ public short getBootPatchLevel() { public short getOsPatch() { short blob = readData(BOOT_OS_PATCH_LEVEL); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -631,27 +662,39 @@ public short getVerifiedBootHash() { public boolean getBootLoaderLock() { short blob = readData(BOOT_DEVICE_LOCKED_STATUS); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; } public byte getBootState() { short blob = readData(BOOT_VERIFIED_BOOT_STATE); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } return (getHeap())[KMByteBlob.cast(blob).getStartOff()]; } public boolean getDeviceLock() { short blob = readData(DEVICE_LOCKED); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; } public boolean getDeviceLockPasswordOnly() { - short blob = readData(DEVICE_LOCKED); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFD) != 0; + short blob = readData(DEVICE_LOCKED_PASSWORD_ONLY); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; } 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 { @@ -701,9 +744,9 @@ public void setBootloaderLocked(boolean flag) { public void setDeviceLock(boolean flag) { short start = alloc(DEVICE_LOCK_FLAG_SIZE); 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); } @@ -711,11 +754,11 @@ public void setDeviceLock(boolean flag) { public void setDeviceLockPasswordOnly(boolean flag) { short start = alloc(DEVICE_LOCK_FLAG_SIZE); if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x02); + (getHeap())[start] = (byte) 0x01; } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFD); + (getHeap())[start] = (byte) 0x00; } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(DEVICE_LOCKED_PASSWORD_ONLY, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); } public void setDeviceLockTimestamp(byte[] buf, short start, short len) { @@ -760,24 +803,28 @@ public void setBootState(byte state) { @Override public void onSave(Element ele) { ele.write(dataIndex); + ele.write(attestIdsIdsIndex); ele.write(dataTable); + ele.write(attestIdsTable); } @Override public void onRestore(Element ele) { dataIndex = ele.readShort(); + attestIdsIdsIndex = ele.readShort(); dataTable = (byte[]) ele.readObject(); + attestIdsTable = (byte[]) ele.readObject(); } @Override public short getBackupPrimitiveByteCount() { // dataIndex - return (short) 2; + return (short) 4; } @Override public short getBackupObjectCount() { // dataTable - return (short) 1; + return (short) 2; } } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index b9c2b9b7..dba79839 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -25,6 +25,11 @@ */ public interface KMSEProvider extends KMUpgradable { + // Provision related constants. + public static final byte CERTIFICATE_CHAIN = 0; + public static final byte CERTIFICATE_EXPIRY = 1; + public static final byte CERTIFICATE_ISSUER = 2; + /** * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it * should throw a CryptoException. @@ -445,34 +450,39 @@ KMOperation initAsymmetricOperation( KMAttestationCert getAttestationCert(boolean rsaCert); /** - * This operation persists the certificate chain in the persistent memory. + * This operation persists the provision data in the persistent memory. * + * @param dataType type of the provision data to read. * @param buf buffer containing certificate chain. * @param offset is the start of the buffer. * @param len is the length of the buffer. */ - void persistCertificateChain(byte[] buf, short offset, short len); + void persistProvisionData(byte dataType, byte[] buf, short offset, short len); /** - * This operation clears the certificate chain from persistent memory. + * This operation clears the provisioned data from persistent memory. + * + * @param dataType type of the provision data to read. */ - void clearCertificateChain(); + void clearProvisionedData(byte dataType); /** - * The operation reads the certificate chain from persistent memory. + * The operation reads the provisioned data from persistent memory. * + * @param dataType type of the provision data to read. * @param buf is the start of data buffer. * @param offset is the start of the data. * @return the length of the data buffer in bytes. */ - short readCertificateChain(byte[] buf, short offset); + short readProvisionedData(byte dataType, byte[] buf, short offset); /** - * This function returns the cert chain length. + * This function returns the provisioned data length. * + * @param dataType type of the provision data to read. * @return length of the certificate chain. */ - short getCertificateChainLength(); + short getProvisionedDataLength(byte dataType); /** * This function tells if boot signal event is supported or not. @@ -559,8 +569,7 @@ KMOperation initAsymmetricOperation( KMPreSharedKey getPresharedKey(); /** - * Releases all the instance back to pool. - * Generally this is used when card is reset. + * Releases all the instance back to pool. Generally this is used when card is reset. */ void releaseAllOperations(); From f09d41af8105e5412aee1c306741bc95ca293ec9 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Fri, 5 Nov 2021 22:13:08 +0000 Subject: [PATCH 12/33] 1. Corrected some mismatches with 4.1 specification. 2. Combined cert chain and cert params command in a single command --- .../keymaster/KMAndroidSEProvider.java | 94 +++--- .../javacard/keymaster/KMConfigurations.java | 4 +- .../javacard/keymaster/KMConfigurations.java | 4 +- .../javacard/keymaster/KMJCardSimulator.java | 78 +++-- .../android/javacard/keymaster/KMDecoder.java | 27 +- .../android/javacard/keymaster/KMEncoder.java | 10 +- .../javacard/keymaster/KMKeyParameters.java | 8 +- .../javacard/keymaster/KMKeymasterApplet.java | 295 ++++++++++-------- .../javacard/keymaster/KMSEProvider.java | 23 +- .../4.1/JavacardKeymaster4Device.cpp | 14 +- ProvisioningTool/include/constants.h | 26 +- ProvisioningTool/src/construct_apdus.cpp | 124 +++----- ProvisioningTool/src/provision.cpp | 29 +- 13 files changed, 368 insertions(+), 368 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 4693b808..617f9ff3 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -117,6 +117,11 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; private static final short MAX_OPERATIONS = 4; + + private static final short CERT_CHAIN_OFFSET = 0; + private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; + private static final short CERT_EXPIRY_OFFSET = + (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, @@ -168,9 +173,7 @@ public class KMAndroidSEProvider implements KMSEProvider { // Entropy private RandomData rng; //For storing root certificate and intermediate certificates. - private byte[] certificateChain; - private byte[] certIssuer; - private byte[] certExpiry; + private byte[] provisionData; private KMAESKey masterKey; private KMECPrivateKey attestationKey; private KMHmacKey preSharedKey; @@ -219,10 +222,10 @@ public KMAndroidSEProvider() { rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); //Allocate buffer for certificate chain. if (!isUpgrading()) { - // First 2 bytes for length. - certificateChain = new byte[(short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2)]; - certIssuer = new byte[(short) (KMConfigurations.CERT_ISSUER_SIZE + 2)]; - certExpiry = new byte[(short) (KMConfigurations.CERT_EXPIRY_SIZE + 2)]; + // First 2 bytes is reserved for length for all the 3 buffers. + short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + + KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); + provisionData = new byte[totalLen]; // Initialize attestationKey and preShared key with zeros. Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); @@ -1080,31 +1083,48 @@ public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, return key.getKey(keyBuf, keyStart); } - private byte[] getProvisionDataBuffer(byte dataType) { + private short getProvisionDataBufferOffset(byte dataType) { switch(dataType) { case CERTIFICATE_CHAIN: - return certificateChain; + return CERT_CHAIN_OFFSET; case CERTIFICATE_ISSUER: - return certIssuer; + return CERT_ISSUER_OFFSET; case CERTIFICATE_EXPIRY: - return certExpiry; + return CERT_EXPIRY_OFFSET; default: KMException.throwIt(KMError.INVALID_ARGUMENT); } - return null; + return 0; } - - @Override - public void clearProvisionData(byte dataType) { - byte[] buffer = getProvisionDataBuffer(dataType); + + private void persistProvisionData(byte[] buf, short off, short len, short maxSize, short copyToOff) { + if (len > maxSize) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(buffer, (short) 0, (short) (buffer.length), (byte) 0); + Util.setShort(provisionData, copyToOff, len); + Util.arrayCopyNonAtomic(buf, off, provisionData, (short) (copyToOff + 2), len); JCSystem.commitTransaction(); } + + private void persistCertificateChain(byte[] certChain, short certChainOff, short certChainLen) { + persistProvisionData(certChain, certChainOff, certChainLen, + KMConfigurations.CERT_CHAIN_MAX_SIZE, CERT_CHAIN_OFFSET); + } + + private void persistCertficateIssuer(byte[] certIssuer, short certIssuerOff, short certIssuerLen) { + persistProvisionData(certIssuer, certIssuerOff, certIssuerLen, + KMConfigurations.CERT_ISSUER_MAX_SIZE, CERT_ISSUER_OFFSET); + } + + private void persistCertificateExpiryTime(byte[] certExpiry, short certExpiryOff, short certExpiryLen) { + persistProvisionData(certExpiry, certExpiryOff, certExpiryLen, + KMConfigurations.CERT_EXPIRY_MAX_SIZE, CERT_EXPIRY_OFFSET); + } - //This function supports multi-part request data. @Override - public void persistProvisionData(byte dataType, byte[] buf, short offset, short len) { + public void persistProvisionData(byte[] buffer, short certChainOff, short certChainLen, + short certIssuerOff, short certIssuerLen, short certExpiryOff ,short certExpiryLen) { // All the buffers uses first two bytes for length. The certificate chain // is stored as shown below. // _____________________________________________________ @@ -1114,28 +1134,28 @@ public void persistProvisionData(byte dataType, byte[] buf, short offset, short // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - byte[] data = getProvisionDataBuffer(dataType); - if (len > (short) (data.length - 2)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } + // clear buffer. JCSystem.beginTransaction(); - Util.setShort(data, (short) 0, len); - Util.arrayCopyNonAtomic(buf, offset, data, (short) 2, len); + Util.arrayFillNonAtomic(provisionData, (short) 0, (short) provisionData.length, (byte) 0); JCSystem.commitTransaction(); + // Persist data. + persistCertificateChain(buffer, certChainOff, certChainLen); + persistCertficateIssuer(buffer, certIssuerOff, certIssuerLen); + persistCertificateExpiryTime(buffer, certExpiryOff, certExpiryLen); } @Override - public short readProvisionData(byte dataType, byte[] buf, short offset) { - byte[] data = getProvisionDataBuffer(dataType); - short len = Util.getShort(data, (short) 0); - Util.arrayCopyNonAtomic(data, (short) 2, buf, offset, len); + public short readProvisionedData(byte dataType, byte[] buf, short offset) { + short provisionBufOffset = getProvisionDataBufferOffset(dataType); + short len = Util.getShort(provisionData, provisionBufOffset); + Util.arrayCopyNonAtomic(provisionData, (short) (2 + provisionBufOffset), buf, offset, len); return len; } @Override - public short getProvisionDataLength(byte dataType) { - byte[] data = getProvisionDataBuffer(dataType); - return Util.getShort(data, (short) 0); + public short getProvisionedDataLength(byte dataType) { + short provisionBufOffset = getProvisionDataBufferOffset(dataType); + return Util.getShort(provisionData, provisionBufOffset); } @Override @@ -1155,9 +1175,7 @@ public void clearDeviceBooted(boolean resetBootFlag) { @Override public void onSave(Element element) { - element.write(certificateChain); - element.write(certIssuer); - element.write(certExpiry); + element.write(provisionData); KMAESKey.onSave(element, masterKey); KMECPrivateKey.onSave(element, attestationKey); KMHmacKey.onSave(element, preSharedKey); @@ -1165,9 +1183,7 @@ public void onSave(Element element) { @Override public void onRestore(Element element) { - certificateChain = (byte[]) element.readObject(); - certIssuer = (byte[]) element.readObject(); - certExpiry = (byte[]) element.readObject(); + provisionData = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); @@ -1185,7 +1201,7 @@ public short getBackupPrimitiveByteCount() { @Override public short getBackupObjectCount() { short count = - (short) (3 + /* Cert chain, Cert Issuer, CertExpiry */ + (short) (1 + /* provisionData buffer */ KMAESKey.getBackupObjectCount() + KMECPrivateKey.getBackupObjectCount() + KMHmacKey.getBackupObjectCount()); diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 1db04d57..fd122b16 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -23,6 +23,6 @@ public class KMConfigurations { // Maximum cert chain size public static final short CERT_CHAIN_MAX_SIZE = 2500; - public static final short CERT_ISSUER_SIZE = 250; - public static final short CERT_EXPIRY_SIZE = 16; + public static final short CERT_ISSUER_MAX_SIZE = 250; + public static final short CERT_EXPIRY_MAX_SIZE = 20; } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 1db04d57..fd122b16 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -23,6 +23,6 @@ public class KMConfigurations { // Maximum cert chain size public static final short CERT_CHAIN_MAX_SIZE = 2500; - public static final short CERT_ISSUER_SIZE = 250; - public static final short CERT_EXPIRY_SIZE = 16; + public static final short CERT_ISSUER_MAX_SIZE = 250; + public static final short CERT_EXPIRY_MAX_SIZE = 20; } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index 9376bc5e..749264cc 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -73,7 +73,10 @@ public class KMJCardSimulator implements KMSEProvider { public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; private static final short RSA_KEY_SIZE = 256; - + private static final short CERT_CHAIN_OFFSET = 0; + private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; + private static final short CERT_EXPIRY_OFFSET = + (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); public static boolean jcardSim = false; private static Signature kdf; @@ -84,9 +87,7 @@ public class KMJCardSimulator implements KMSEProvider { private static Cipher aesRngCipher; private static byte[] entropyPool; private static byte[] rndNum; - private byte[] certificateChain; - private byte[] certIssuer; - private byte[] certExpiry; + private byte[] provisionData; private KMAESKey masterKey; private KMECPrivateKey attestationKey; private KMHmacKey preSharedKey; @@ -115,9 +116,10 @@ public KMJCardSimulator() { aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); // various ciphers //Allocate buffer for certificate chain and cert parameters. - certificateChain = new byte[(short) (KMConfigurations.CERT_CHAIN_MAX_SIZE + 2)]; - certIssuer = new byte[(short) (KMConfigurations.CERT_ISSUER_SIZE + 2)]; - certExpiry = new byte[(short) (KMConfigurations.CERT_EXPIRY_SIZE + 2)]; + // First 2 bytes is reserved for length for all the 3 buffers. + short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + + KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); + provisionData = new byte[totalLen]; jCardSimulator = this; } @@ -1169,32 +1171,48 @@ public void addRngEntropy(byte[] num, short offset, short length) { public KMAttestationCert getAttestationCert(boolean rsaCert) { return KMAttestationCertImpl.instance(rsaCert); } - - private byte[] getProvisionDataBuffer(byte dataType) { + private short getProvisionDataBufferOffset(byte dataType) { switch(dataType) { case CERTIFICATE_CHAIN: - return certificateChain; + return CERT_CHAIN_OFFSET; case CERTIFICATE_ISSUER: - return certIssuer; + return CERT_ISSUER_OFFSET; case CERTIFICATE_EXPIRY: - return certExpiry; + return CERT_EXPIRY_OFFSET; default: KMException.throwIt(KMError.INVALID_ARGUMENT); } - return null; + return 0; } - @Override - public void clearProvisionedData(byte dataType) { - byte[] buffer = getProvisionDataBuffer(dataType); + private void persistProvisionData(byte[] buf, short off, short len, short maxSize, short copyToOff) { + if (len > maxSize) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(buffer, (short) 0, (short) (buffer.length), (byte) 0); + Util.setShort(provisionData, copyToOff, len); + Util.arrayCopyNonAtomic(buf, off, provisionData, (short) (copyToOff + 2), len); JCSystem.commitTransaction(); } - //This function supports multi-part request data. + private void persistCertificateChain(byte[] certChain, short certChainOff, short certChainLen) { + persistProvisionData(certChain, certChainOff, certChainLen, + KMConfigurations.CERT_CHAIN_MAX_SIZE, CERT_CHAIN_OFFSET); + } + + private void persistCertficateIssuer(byte[] certIssuer, short certIssuerOff, short certIssuerLen) { + persistProvisionData(certIssuer, certIssuerOff, certIssuerLen, + KMConfigurations.CERT_ISSUER_MAX_SIZE, CERT_ISSUER_OFFSET); + } + + private void persistCertificateExpiryTime(byte[] certExpiry, short certExpiryOff, short certExpiryLen) { + persistProvisionData(certExpiry, certExpiryOff, certExpiryLen, + KMConfigurations.CERT_EXPIRY_MAX_SIZE, CERT_EXPIRY_OFFSET); + } + @Override - public void persistProvisionData(byte dataType, byte[] buf, short offset, short len) { + public void persistProvisionData(byte[] buffer, short certChainOff, short certChainLen, + short certIssuerOff, short certIssuerLen, short certExpiryOff ,short certExpiryLen) { // All the buffers uses first two bytes for length. The certificate chain // is stored as shown below. // _____________________________________________________ @@ -1204,28 +1222,28 @@ public void persistProvisionData(byte dataType, byte[] buf, short offset, short // CBOR format: // Next single byte holds the byte string header. // Next 3 bytes holds the total length of the certificate chain. - byte[] data = getProvisionDataBuffer(dataType); - if (len > (short) (data.length - 2)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } + // clear buffer. JCSystem.beginTransaction(); - Util.setShort(data, (short) 0, len); - Util.arrayCopyNonAtomic(buf, offset, data, (short) 2, len); + Util.arrayFillNonAtomic(provisionData, (short) 0, (short) provisionData.length, (byte) 0); JCSystem.commitTransaction(); + // Persist data. + persistCertificateChain(buffer, certChainOff, certChainLen); + persistCertficateIssuer(buffer, certIssuerOff, certIssuerLen); + persistCertificateExpiryTime(buffer, certExpiryOff, certExpiryLen); } @Override public short readProvisionedData(byte dataType, byte[] buf, short offset) { - byte[] data = getProvisionDataBuffer(dataType); - short len = Util.getShort(data, (short) 0); - Util.arrayCopyNonAtomic(data, (short) 2, buf, offset, len); + short provisionBufOffset = getProvisionDataBufferOffset(dataType); + short len = Util.getShort(provisionData, provisionBufOffset); + Util.arrayCopyNonAtomic(provisionData, (short) (2 + provisionBufOffset), buf, offset, len); return len; } @Override public short getProvisionedDataLength(byte dataType) { - byte[] data = getProvisionDataBuffer(dataType); - return Util.getShort(data, (short) 0); + short provisionBufOffset = getProvisionDataBufferOffset(dataType); + return Util.getShort(provisionData, provisionBufOffset); } @Override diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index c2579372..e305913c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -435,14 +435,29 @@ private void incrementStartOff(short inc) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } - - public short readCertificateChainLengthAndHeaderLen(byte[] buf, short bufOffset, - short bufLen) { + +//Reads the offset and length values of the ByteBlobs from a CBOR array buffer. + public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOffset, short bufLen, + byte[] out, short outOff) { + // Read Array length bufferRef[0] = buf; scratchBuf[START_OFFSET] = bufOffset; scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); - short totalLen = readMajorTypeWithPayloadLength(BYTES_TYPE); - totalLen += (short) (scratchBuf[START_OFFSET] - bufOffset); - return totalLen; + short byteBlobLength = 0; + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (expectedArrLen != payloadLength) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short index = 0; + while (index < payloadLength) { + incrementStartOff(byteBlobLength); + byteBlobLength = readMajorTypeWithPayloadLength(BYTES_TYPE); + Util.setShort(out, outOff, scratchBuf[START_OFFSET]); // offset + outOff += 2; + Util.setShort(out, outOff, byteBlobLength); // length + outOff += 2; + index++; + } } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index 14d8ef4c..1ae67595 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -89,16 +89,16 @@ public short encode(short object, byte[] buffer, short startOff) { return (short) (scratchBuf[START_OFFSET] - startOff); } - // array{KMError.OK,Array{KMByteBlobs}} - public void encodeCertChain(byte[] buffer, short offset, short length, short errInt32Ptr) { + // array{KMError.OK, KMByteBlob} + public void encodeCertChain(byte[] buffer, short offset, short length, short errInt32Ptr, short certChainOff, short certChainLen) { bufferRef[0] = buffer; scratchBuf[START_OFFSET] = offset; - scratchBuf[LEN_OFFSET] = (short) (offset + 1); - //Total length is ArrayHeader + [UIntHeader + length(errInt32Ptr)] - scratchBuf[LEN_OFFSET] += (short) (1 + getEncodedIntegerLength(errInt32Ptr)); + scratchBuf[LEN_OFFSET] = (short) (offset + length + 1); writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements encodeInteger(errInt32Ptr); + writeMajorTypeWithLength(BYTES_TYPE, certChainLen); + writeBytes(buffer, certChainOff, certChainLen); } //array{KMError.OK,Array{KMByteBlobs}} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index ef1e1e00..17b3311d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -168,7 +168,9 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION + KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, }; byte index = 0; short tagInd; @@ -227,7 +229,8 @@ public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, KMType.UINT_TAG, KMType.USERID, - KMType.DATE_TAG, KMType.CREATION_DATETIME + KMType.DATE_TAG, KMType.CREATION_DATETIME, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY }; byte index = 0; short tagInd; @@ -298,7 +301,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) { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e06a0f71..9b897226 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -79,14 +79,15 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_BEGIN_KM_CMD = 0x00; // Instructions for Provision Commands. private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD + 1; //0x01 - private static final byte INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD + 2; //0x02 - private static final byte INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD + 3; //0x03 - private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 4; //0x04 - private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 5; //0x05 - private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 6; //0x06 - private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 7; //0x07 - private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 8; //0x08 - private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 9; //0x09 + private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = INS_BEGIN_KM_CMD + 2; //0x02 + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 3; //0x03 + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 4; //0x04 + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 5; //0x05 + private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 6; //0x06 + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 7; //0x07 + private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 8; //0x08 + private static final byte INS_SET_BOOT_ENDED_CMD = INS_BEGIN_KM_CMD + 9; //0x09 + // Top 32 commands are reserved for provisioning. private static final byte INS_END_KM_PROVISION_CMD = 0x20; @@ -118,12 +119,10 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // Provision reporting status private static final byte NOT_PROVISIONED = 0x00; private static final byte PROVISION_STATUS_ATTESTATION_KEY = 0x01; - private static final byte PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02; - 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_ATTESTATION_CERT_DATA = 0x02; + private static final byte PROVISION_STATUS_ATTEST_IDS = 0x04; + private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x08; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x10; // Data Dictionary items public static final byte DATA_ARRAY_SIZE = 30; @@ -189,6 +188,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static short[] tmpVariables; protected static short[] data; protected static byte provisionStatus = NOT_PROVISIONED; + protected static boolean isBootEnded = true; + protected static boolean isEarlyBootEnded = true; + /** * Registers this applet. @@ -349,15 +351,9 @@ public void process(APDU apdu) { sendError(apdu, KMError.OK); return; - case INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD: - processProvisionAttestationCertChainCmd(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_CHAIN; - sendError(apdu, KMError.OK); - return; - - case INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD: - processProvisionAttestationCertParams(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_PARAMS; + case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: + processProvisionAttestationCertDataCmd(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_DATA; sendError(apdu, KMError.OK); return; @@ -395,10 +391,21 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } processSetBootParamsCmd(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_BOOT_PARAM; - seProvider.clearDeviceBooted(false); + sendError(apdu, KMError.OK); return; + + case INS_SET_BOOT_ENDED_CMD: + if (seProvider.isBootSignalEventSupported() + && (keymasterState == KMKeymasterApplet.ACTIVE_STATE) + && (!seProvider.isDeviceRebooted())) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + //set the flag to mark boot ended + isBootEnded = true; + seProvider.clearDeviceBooted(false); + sendError(apdu, KMError.OK); + return; case INS_GET_PROVISION_STATUS_CMD: processGetProvisionStatusCmd(apdu); @@ -512,8 +519,7 @@ private void generateUniqueOperationHandle(byte[] buf, short offset, short len) private boolean isProvisioningComplete() { if ((0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_KEY)) - && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) - && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) + && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_DATA)) && (0 != (provisionStatus & PROVISION_STATUS_PRESHARED_SECRET))) { return true; } else { @@ -531,7 +537,7 @@ private void freeOperations() { } private void processEarlyBootEndedCmd(APDU apdu) { - KMException.throwIt(KMError.UNIMPLEMENTED); + isEarlyBootEnded = true; } private void processDeviceLockedCmd(APDU apdu) { @@ -709,81 +715,55 @@ private short getProvisionedCertificateData(byte dataType) { private void processGetCertChainCmd(APDU apdu) { // Make the response - tmpVariables[0] = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); + short certChainLen = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); short int32Ptr = buildErrorStatus(KMError.OK); - //Total Extra length - // Add arrayHeader and (PowerResetStatus + KMError.OK) - tmpVariables[2] = (short) (1 + encoder.getEncodedIntegerLength(int32Ptr)); - tmpVariables[0] += tmpVariables[2]; - tmpVariables[1] = KMByteBlob.instance(tmpVariables[0]); + short maxByteHeaderLen = 3; // Maximum possible ByteBlob header len. + short arrayHeaderLen = 1; + // Allocate maximum possible buffer. + // Add arrayHeader + (PowerResetStatus + KMError.OK) + Byte Header + short totalLen = (short) (arrayHeaderLen + encoder.getEncodedIntegerLength(int32Ptr) + maxByteHeaderLen + certChainLen); + tmpVariables[1] = KMByteBlob.instance(totalLen); bufferRef[0] = KMByteBlob.cast(tmpVariables[1]).getBuffer(); bufferProp[BUF_START_OFFSET] = KMByteBlob.cast(tmpVariables[1]).getStartOff(); bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[1]).length(); - // read the cert chain from non-volatile memory. Cert chain is already in - // CBOR format. + // copy the certificate chain to the end of the buffer. seProvider.readProvisionedData( KMSEProvider.CERTIFICATE_CHAIN, (byte[]) bufferRef[0], - (short) (bufferProp[BUF_START_OFFSET] + tmpVariables[2])); + (short) (bufferProp[BUF_START_OFFSET] + totalLen - certChainLen)); // Encode cert chain. - encoder.encodeCertChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET], int32Ptr); + encoder.encodeCertChain((byte[]) bufferRef[0], + bufferProp[BUF_START_OFFSET], + bufferProp[BUF_LEN_OFFSET], + int32Ptr, // uint32 ptr + (short) (bufferProp[BUF_START_OFFSET] + totalLen - certChainLen), // start pos of cert chain. + certChainLen); sendOutgoing(apdu); } - private void processProvisionAttestationCertParams(APDU apdu) { + private void processProvisionAttestationCertDataCmd(APDU apdu) { receiveIncoming(apdu); - // Arguments - short blob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 2); - KMArray.cast(argsProto).add((short) 0, blob); // Cert - DER encoded issuer - KMArray.cast(argsProto).add((short) 1, blob); // Cert - Expiry Time - // Decode the argument. - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // save issuer - DER Encoded - tmpVariables[0] = KMArray.cast(args).get((short) 0); - short len = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_ISSUER); - if (len != 0) { - // Clear issuer buffer. - seProvider.clearProvisionedData(KMSEProvider.CERTIFICATE_ISSUER); - } - seProvider.persistProvisionData( - KMSEProvider.CERTIFICATE_ISSUER, - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - - // save expiry time - UTC or General Time - YYMMDDhhmmssZ or YYYYMMDDhhmmssZ. - tmpVariables[0] = KMArray.cast(args).get((short) 1); - len = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_EXPIRY); - if (len != 0) { - // Clear issuer buffer. - seProvider.clearProvisionedData(KMSEProvider.CERTIFICATE_EXPIRY); - } + // Buffer holds the corresponding offsets and lengths of the certChain, certIssuer and certExpiry + // in the bufferRef[0] buffer. + short var = KMByteBlob.instance((short) 12); + // These variables point to the appropriate positions in the var buffer. + short certChainPos = KMByteBlob.cast(var).getStartOff(); + short certIssuerPos = (short) (KMByteBlob.cast(var).getStartOff() + 4); + short certExpiryPos = (short) (KMByteBlob.cast(var).getStartOff() + 8); + decoder.decodeCertificateData((short) 3, + (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET], + KMByteBlob.cast(var).getBuffer(), KMByteBlob.cast(var).getStartOff()); + // persist data seProvider.persistProvisionData( - KMSEProvider.CERTIFICATE_EXPIRY, - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - } - - private void processProvisionAttestationCertChainCmd(APDU apdu) { - tmpVariables[0] = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); - if (tmpVariables[0] != 0) { - //Clear the previous certificate chain. - seProvider.clearProvisionedData(KMSEProvider.CERTIFICATE_CHAIN); - } - receiveIncoming(apdu); - tmpVariables[1] = decoder.readCertificateChainLengthAndHeaderLen((byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - if (tmpVariables[1] != bufferProp[BUF_LEN_OFFSET]) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - seProvider.persistProvisionData(KMSEProvider.CERTIFICATE_CHAIN, (byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory + (byte[]) bufferRef[0], + Util.getShort(KMByteBlob.cast(var).getBuffer(), certChainPos), // offset + Util.getShort(KMByteBlob.cast(var).getBuffer(), (short) (certChainPos + 2)), // length + Util.getShort(KMByteBlob.cast(var).getBuffer(), certIssuerPos), // offset + Util.getShort(KMByteBlob.cast(var).getBuffer(), (short) (certIssuerPos + 2)), // length + Util.getShort(KMByteBlob.cast(var).getBuffer(), certExpiryPos), // offset + Util.getShort(KMByteBlob.cast(var).getBuffer(), (short) (certExpiryPos + 2))); // length + + // reclaim memory repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); } @@ -871,16 +851,8 @@ private void processProvisionAttestIdsCmd(APDU apdu) { repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); - repository.deleteAttIds(); // persist attestation Ids - if any is missing then exception occurs - saveAttId(KMType.ATTESTATION_ID_BRAND); - saveAttId(KMType.ATTESTATION_ID_DEVICE); - saveAttId(KMType.ATTESTATION_ID_PRODUCT); - saveAttId(KMType.ATTESTATION_ID_MANUFACTURER); - saveAttId(KMType.ATTESTATION_ID_MODEL); - saveAttId(KMType.ATTESTATION_ID_IMEI); - saveAttId(KMType.ATTESTATION_ID_MEID); - saveAttId(KMType.ATTESTATION_ID_SERIAL); + saveAttId(); } private void processProvisionSharedSecretCmd(APDU apdu) { @@ -916,17 +888,24 @@ private void processGetProvisionStatusCmd(APDU apdu) { sendOutgoing(apdu); } - private void saveAttId(short attTag) { - tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, attTag, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - repository.persistAttId( - mapToAttId(attTag), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); + private void saveAttId() { + // clear the attestation ids. + repository.deleteAttIds(); + + short attTag = KMType.ATTESTATION_ID_BRAND; + while (attTag <= KMType.ATTESTATION_ID_MODEL) { + tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, attTag, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); + repository.persistAttId( + mapToAttId(attTag), + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length()); + } else { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + attTag++; } } @@ -1999,7 +1978,7 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP } } - private void authorizeDeviceUnlock(short hwToken) { + private void authorizeDeviceUnlock(short hwToken, byte[] scratchPad) { // If device is locked and key characteristics requires unlocked device then check whether // HW auth token has correct timestamp. short ptr = @@ -2010,6 +1989,7 @@ private void authorizeDeviceUnlock(short hwToken) { if (hwToken == KMType.INVALID_VALUE) { KMException.throwIt(KMError.DEVICE_LOCKED); } + validateHwToken(data[HW_TOKEN], scratchPad); ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); // Check if the current auth time stamp is greater then device locked time stamp short ts = repository.getDeviceTimeStamp(); @@ -2531,10 +2511,12 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { KMException.throwIt(KMError.MISSING_MAC_LENGTH); } if (macLen % 8 != 0 - || macLen > 128 - || macLen - < KMIntegerTag.getShortValue( - KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + || macLen > 128) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } + if(macLen + < KMIntegerTag.getShortValue( + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { KMException.throwIt(KMError.INVALID_MAC_LENGTH); } op.setMacLength(macLen); @@ -2567,9 +2549,9 @@ 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])) { + } + if (macLen % 8 != 0 + || macLen > 256) { KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); } op.setMacLength(macLen); @@ -2588,7 +2570,22 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizePadding(op); authorizeBlockModeAndMacLength(op); authorizeUserSecureIdAuthTimeout(op, scratchPad); - authorizeDeviceUnlock(data[HW_TOKEN]); + authorizeDeviceUnlock(data[HW_TOKEN], scratchPad); + + //Validate bootloader only + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE && isBootEnded) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + + //Validate early boot + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE && isEarlyBootEnded) { + KMException.throwIt(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; @@ -3097,6 +3094,10 @@ private void importECKeys(byte[] scratchPad) { } } else { // add the key size to scratchPad + if (!(256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) + && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))){ + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } tmpVariables[5] = KMInteger.uint_16((short) 256); tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); @@ -3158,11 +3159,18 @@ private void importHmacKey(byte[] scratchPad) { KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { if (!(tmpVariables[2] >= 64 && tmpVariables[2] <= 512 && tmpVariables[2] % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (tmpVariables[2] != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) (KMByteBlob.cast(data[SECRET]).length() * 8)); + tmpVariables[6] = (short) (KMByteBlob.cast(data[SECRET]).length() * 8); + if (!(tmpVariables[6] >= 64 && tmpVariables[6] <= 512 && tmpVariables[6] % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + tmpVariables[5] = KMInteger.uint_16(tmpVariables[6]); tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; @@ -3199,12 +3207,17 @@ private void importTDESKey(byte[] scratchPad) { tmpVariables[2] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 168) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + if (tmpVariables[2] != 168 || + 192 != (short)( 8 * KMByteBlob.cast(data[SECRET]).length())) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { - // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) 168); + // add the key size to scratchPad + tmpVariables[6] = (short)( 8 * KMByteBlob.cast(data[SECRET]).length()); + if(tmpVariables[6] != 192) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + tmpVariables[5] = KMInteger.uint_16(tmpVariables[6]); tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; @@ -3249,6 +3262,9 @@ private void importAESKey(byte[] scratchPad) { KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keysize != KMType.INVALID_VALUE) { + if(keysize != (short)( 8 * KMByteBlob.cast(data[SECRET]).length())) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } validateAesKeySize(keysize); } else { // add the key size to scratchPad @@ -3323,7 +3339,11 @@ private void importRSAKey(byte[] scratchPad) { } } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) 2048); + tmpVariables[6] = (short) (KMByteBlob.cast(data[SECRET]).length() * 8); + if(tmpVariables[6] != 2048) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + tmpVariables[5] = KMInteger.uint_16((short) tmpVariables[6]); tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; @@ -3453,6 +3473,12 @@ private void processSetBootParamsCmd(APDU apdu) { // Hmac is cleared, so generate a new Hmac nonce. seProvider.newRandomNumber(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); repository.initHmacNonce(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); + + //flag to maintain the boot state + isBootEnded = false; + + //flag to maintain early boot ended state + isEarlyBootEnded = false; } private static void processGenerateKey(APDU apdu) { @@ -3474,7 +3500,7 @@ private static void processGenerateKey(APDU apdu) { // Check if EarlyBootEnded tag is present. tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { + if (tmpVariables[0] != KMType.INVALID_VALUE && isEarlyBootEnded) { KMException.throwIt(KMError.EARLY_BOOT_ENDED); } // Check if rollback resistance tag is present @@ -3483,16 +3509,11 @@ private static void processGenerateKey(APDU apdu) { if (tmpVariables[0] != KMType.INVALID_VALUE) { KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); } - // Bootloader only not supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } + // get algorithm tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); @@ -3608,7 +3629,7 @@ private static void validateAESKey() { tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } if ((tmpVariables[0] != 256) && (tmpVariables[0] != 128)) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); @@ -3633,7 +3654,7 @@ private static void validateAESKey() { || KMInteger.cast(tmpVariables[3]).getShort() > 128 || KMInteger.cast(tmpVariables[3]).getShort() < 96 || (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } } @@ -3692,7 +3713,7 @@ private static void validateTDESKey() { tmpVariables[1] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } if (tmpVariables[1] != 168 && tmpVariables[1] != 192) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); @@ -3732,7 +3753,7 @@ private static void validateHmacKey() { tmpVariables[1] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } if (((short) (tmpVariables[1] % 8) != 0) || (tmpVariables[1] < (short) 64) diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index dba79839..ef14c001 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -452,19 +452,16 @@ KMOperation initAsymmetricOperation( /** * This operation persists the provision data in the persistent memory. * - * @param dataType type of the provision data to read. - * @param buf buffer containing certificate chain. - * @param offset is the start of the buffer. - * @param len is the length of the buffer. - */ - void persistProvisionData(byte dataType, byte[] buf, short offset, short len); - - /** - * This operation clears the provisioned data from persistent memory. - * - * @param dataType type of the provision data to read. - */ - void clearProvisionedData(byte dataType); + * @param buf buffer which contains all the provision data. + * @param certChainOff is the start of the cert chain. + * @param certChainLen is the length of the cert chain. + * @param certIssuerOff is the start of the cert issuer. + * @param certIssuerLen is the length of the cert issuer. + * @param certExpiryOff is the start of the cert expiry. + * @param certExpiryLen is the length of the cert expiry. + */ + void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, + short certIssuerOff, short certIssuerLen, short certExpiryOff, short certExpiryLen); /** * The operation reads the provisioned data from persistent memory. diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index ffca0856..bc1ff735 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -97,19 +97,17 @@ enum class Instruction { INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD+20, INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD+21, INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD+22, - INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+8, - INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+9, + INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+7, + INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+8, }; enum ProvisionStatus { NOT_PROVISIONED = 0x00, PROVISION_STATUS_ATTESTATION_KEY = 0x01, - PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02, - PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, - PROVISION_STATUS_ATTEST_IDS = 0x08, - PROVISION_STATUS_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, + PROVISION_STATUS_ATTESTATION_CERT_DATA = 0x02, + PROVISION_STATUS_ATTEST_IDS = 0x04, + PROVISION_STATUS_PRESHARED_SECRET = 0x08, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x10, }; //Extended error codes diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h index 36f30bd8..ffc0011f 100644 --- a/ProvisioningTool/include/constants.h +++ b/ProvisioningTool/include/constants.h @@ -25,15 +25,12 @@ #define SUCCESS 0 #define FAILURE 1 -#define KEYMASTER_VERSION 4.1 -#define KEYMINT_VERSION 5 +#define KEYMASTER_VERSION_4_1 4.1 +#define KEYMASTER_VERSION_4_0 4 #define P1_40 0x40 #define P1_50 0x50 #define APDU_CLS 0x80 -#define APDU_P1(version) \ - (version == KEYMASTER_VERSION) ? \ - P1_40 : \ - P1_50 +#define APDU_P1 P1_40 #define APDU_P2 0x00 #define INS_BEGIN_KM_CMD 0x00 #define APDU_RESP_STATUS_OK 0x9000 @@ -79,7 +76,6 @@ constexpr uint64_t kKeyFormatRaw = 3; // json keys constexpr char kAttestKey[] = "attest_key"; constexpr char kAttestCertChain[] = "attest_cert_chain"; -constexpr char kAttestCertParams[] = "attest_cert_params"; constexpr char kSharedSecret[] = "shared_secret"; constexpr char kBootParams[] = "boot_params"; constexpr char kAttestationIds[] = "attestation_ids"; @@ -90,12 +86,10 @@ constexpr char kLockProvision[] = "lock_provision"; // Instruction constatnts constexpr int kAttestationKeyCmd = INS_BEGIN_KM_CMD + 1; -constexpr int kAttestCertChainCmd = INS_BEGIN_KM_CMD + 2; -constexpr int kAttestCertParamsCmd = INS_BEGIN_KM_CMD + 3; -constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 4; -constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 5; -constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 6; -constexpr int kLockProvisionCmd = INS_BEGIN_KM_CMD + 7; -constexpr int kGetProvisionStatusCmd = INS_BEGIN_KM_CMD + 8; -constexpr int kDeviceUniqueKeyCmd = INS_BEGIN_KM_CMD + 10; -constexpr int kAdditionalCertChainCmd = INS_BEGIN_KM_CMD + 11; \ No newline at end of file +constexpr int kAttestCertDataCmd = INS_BEGIN_KM_CMD + 2; +constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 3; +constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 4; +constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 5; +constexpr int kLockProvisionCmd = INS_BEGIN_KM_CMD + 6; +constexpr int kGetProvisionStatusCmd = INS_BEGIN_KM_CMD + 7; +constexpr int kSetVersionPatchLevelCmd = INS_BEGIN_KM_CMD + 8; diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp index d3498a96..fdad6be0 100644 --- a/ProvisioningTool/src/construct_apdus.cpp +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -47,12 +47,9 @@ using cppbor::Bstr; // static function declarations static int processInputFile(); static int processAttestationKey(); -static int processAttestationCertificateChain(); -static int processAttestationCertificateParams(); +static int processAttestationCertificateData(); static int processAttestationIds(); static int processSharedSecret(); -static int processDeviceUniqueKey(); -static int processAdditionalCertificateChain(); static int processSetBootParameters(); static int readDataFromFile(const char *fileName, std::vector& data); static int addApduHeader(const int ins, std::vector& inputData); @@ -70,7 +67,7 @@ void usage() { printf("construct_apdus [options]\n"); printf("Valid options are:\n"); printf("-h, --help show this help message and exit.\n"); - printf("-v, --km_version version \t Version of the keymaster (4.1 for keymaster; 5 for keymint) \n"); + printf("-v, --km_version version \t Version of the keymaster (4.1 for keymaster; 4 for keymaster4_0) \n"); printf("-i, --input jsonFile \t Input json file \n"); printf("-o, --output jsonFile \t Output json file \n"); } @@ -163,30 +160,18 @@ int getBootParameterBlobValue(Json::Value& bootParamsObj, const char* key, std:: // Parses the input json file. Prepares the apdu for each entry in the json // file and dump all the apdus into the output json file. int processInputFile() { - // Parse Json file if (0 != readJsonFile(root, inputFileName)) { return FAILURE; } - if (keymasterVersion == KEYMASTER_VERSION) { - printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); - if (0 != processAttestationKey() || - 0 != processAttestationCertificateChain() || - 0 != processAttestationCertificateParams()) { - return FAILURE; - } - } else { - printf("\n Selected keymint version(%f) for provisioning \n", keymasterVersion); - if ( 0 != processDeviceUniqueKey() || - 0 != processAttestationCertificateChain()) { - return FAILURE; - } - } - if (0 != processAttestationIds() || - 0 != processSharedSecret() || - 0 != processSetBootParameters()) { - return FAILURE; + printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); + if (0 != processAttestationKey() || + 0 != processAttestationCertificateData() || + 0 != processAttestationIds() || + 0 != processSharedSecret() || + 0 != processSetBootParameters()) { + return FAILURE; } if (SUCCESS != writeJsonFile(writerRoot, outputFileName)) { return FAILURE; @@ -240,12 +225,18 @@ int processAttestationKey() { return SUCCESS; } -static int processAttestationCertificateChain() { +static int processAttestationCertificateData() { Json::Value certChainFiles = root.get(kAttestCertChain, Json::Value::nullRef); if (!certChainFiles.isNull()) { std::vector certData; + std::vector subject; + std::vector notAfter; if(certChainFiles.isArray()) { + if (certChainFiles.size() == 0) { + printf("\n empty certificate.\n"); + return FAILURE; + } for (uint32_t i = 0; i < certChainFiles.size(); i++) { if(certChainFiles[i].isString()) { /* Read the certificates. */ @@ -253,6 +244,19 @@ static int processAttestationCertificateChain() { printf("\n Failed to read the Root certificate\n"); return FAILURE; } + if (i == 0) { // Leaf certificate + /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ + /* Get X509 certificate instance for the root certificate.*/ + X509_Ptr x509(parseDerCertificate(certData)); + if (!x509) { + return FAILURE; + } + + /* Get subject in DER */ + getDerSubjectName(x509.get(), subject); + /* Get Expirty Time */ + getNotAfter(x509.get(), notAfter); + } } else { printf("\n Fail: Only proper certificate paths as a string is allowed inside the json file. \n"); return FAILURE; @@ -263,8 +267,12 @@ static int processAttestationCertificateChain() { return FAILURE; } // Prepare cbor input - std::vector cborData = Bstr(certData).encode(); - if (SUCCESS != addApduHeader(kAttestCertChainCmd, cborData)) { + Array array; + array.add(certData); + array.add(subject); + array.add(notAfter); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kAttestCertDataCmd, cborData)) { return FAILURE; } // Write to json. @@ -277,59 +285,6 @@ static int processAttestationCertificateChain() { return SUCCESS; } -int processAttestationCertificateParams() { - Json::Value certChainFile = root.get(kAttestCertChain, Json::Value::nullRef); - if (!certChainFile.isNull()) { - std::vector> certData; - if (certChainFile.isArray()) { - if (certChainFile.size() == 0) { - printf("\n empty certificate.\n"); - return FAILURE; - } - std::vector leafCertificate; - if (SUCCESS != readDataFromFile(certChainFile[0].asString().data(), leafCertificate)) { - printf("\n Failed to read the Root certificate\n"); - return FAILURE; - } - // ---------------- - // Prepare cbor data. - Array array; - std::vector subject; - std::vector notAfter; - - /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ - /* Get X509 certificate instance for the root certificate.*/ - X509_Ptr x509(parseDerCertificate(leafCertificate)); - if (!x509) { - return FAILURE; - } - - /* Get subject in DER */ - getDerSubjectName(x509.get(), subject); - /* Get Expirty Time */ - getNotAfter(x509.get(), notAfter); - - array.add(subject); - array.add(notAfter); - std::vector cborData = array.encode(); - if (SUCCESS != addApduHeader(kAttestCertParamsCmd, cborData)) { - return FAILURE; - } - // Write to json. - writerRoot[kAttestCertParams] = getHexString(cborData); - //----------------- - } else { - printf("\n Fail: cert chain value should be an array inside the json file. \n"); - return FAILURE; - } - } else { - printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); - return FAILURE; - } - printf("\n Constructed attestation certificate params APDU successfully. \n"); - return SUCCESS; -} - int processAttestationIds() { //AttestIDParams params; Json::Value attestIds = root.get("attest_ids", Json::Value::nullRef); @@ -535,7 +490,7 @@ int addApduHeader(const int ins, std::vector& inputData) { } inputData.insert(inputData.begin(), static_cast(APDU_P2));//P2 - inputData.insert(inputData.begin(), static_cast(APDU_P1(keymasterVersion)));//P1 + inputData.insert(inputData.begin(), static_cast(APDU_P1));//P1 inputData.insert(inputData.begin(), static_cast(ins));//INS inputData.insert(inputData.begin(), static_cast(APDU_CLS));//CLS return SUCCESS; @@ -564,11 +519,6 @@ int readDataFromFile(const char *filename, std::vector& data) { return ret; } -// TODO -static int processDeviceUniqueKey() { return 0; } -static int processAdditionalCertificateChain() { return 0; } - - int main(int argc, char* argv[]) { int c; struct option longOpts[] = { @@ -624,7 +574,7 @@ int main(int argc, char* argv[]) { usage(); return FAILURE; } - if (keymasterVersion != KEYMASTER_VERSION && keymasterVersion != KEYMINT_VERSION) { + if (keymasterVersion != KEYMASTER_VERSION_4_1 && keymasterVersion != KEYMASTER_VERSION_4_0) { printf("\n Error unknown version."); return FAILURE; } diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index 997774e4..e06c389f 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -26,7 +26,6 @@ #include #include -#define SE_POWER_RESET_STATUS_FLAG (1 << 30) enum ProvisionStatus { NOT_PROVISIONED = 0x00, PROVISION_STATUS_ATTESTATION_KEY = 0x01, @@ -52,7 +51,8 @@ using cppbor::MajorType; // static function declarations static uint16_t getApduStatus(std::vector& inputData); static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response); -static int provisionData(std::shared_ptr& pSocket, const char* jsonKey, std::vector& response); +static int provisionData(std::shared_ptr& pSocket, const char* jsonKey); +static int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response); static int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t &value); @@ -183,7 +183,7 @@ int openConnection(std::shared_ptr& pSocket) { // Parses the input json file. Sends the apdus to JCServer. int processInputFile() { - if (keymasterVersion != KEYMASTER_VERSION && keymasterVersion != KEYMINT_VERSION) { + if (keymasterVersion != KEYMASTER_VERSION_4_1 && keymasterVersion != KEYMASTER_VERSION_4_0) { printf("\n Error unknown version.\n"); usage(); return FAILURE; @@ -199,23 +199,12 @@ int processInputFile() { } std::vector response; - if (keymasterVersion == KEYMASTER_VERSION) { - printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); - if (0 != provisionData(pSocket, kAttestKey) || - 0 != provisionData(pSocket, kAttestCertChain) || - 0 != provisionData(pSocket, kAttestCertParams)) { - return FAILURE; - } - } else { - printf("\n Selected keymint version(%f) for provisioning \n", keymasterVersion); - if ( 0 != provisionData(pSocket, kDeviceUniqueKey) || - 0 != provisionData(pSocket, kAdditionalCertChain)) { - return FAILURE; - } - } - if (0 != provisionData(pSocket, kAttestationIds) || - 0 != provisionData(pSocket, kSharedSecret) || - 0 != provisionData(pSocket, kBootParams)) { + printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); + if (0 != provisionData(pSocket, kAttestKey) || + 0 != provisionData(pSocket, kAttestCertChain) || + 0 != provisionData(pSocket, kAttestationIds) || + 0 != provisionData(pSocket, kSharedSecret) || + 0 != provisionData(pSocket, kBootParams)) { return FAILURE; } return SUCCESS; From c4d5f0cf3786b8d17300bf9085832eaaeb19cf36 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sun, 7 Nov 2021 22:51:33 +0000 Subject: [PATCH 13/33] Added cts keystore patch --- .../cts_tests_tests_keystore.patch | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 aosp_integration_patches/cts_tests_tests_keystore.patch diff --git a/aosp_integration_patches/cts_tests_tests_keystore.patch b/aosp_integration_patches/cts_tests_tests_keystore.patch new file mode 100644 index 00000000..62888a5b --- /dev/null +++ b/aosp_integration_patches/cts_tests_tests_keystore.patch @@ -0,0 +1,157 @@ +diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java +index ccbadf98a31..eca7b6c2abe 100644 +--- a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java ++++ b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java +@@ -744,17 +744,27 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + int blockSize = getBlockSize(); + if (isStreamCipher()) { + // Stream cipher -- one byte in, one byte out ++ int comparingPosition = 0; ++ //Stream cipher -- one byte in, one byte out (unless when Strongbox is used) + for (int plaintextIndex = 0; plaintextIndex < plaintext.length; plaintextIndex++) { + byte[] output = update(new byte[] {plaintext[plaintextIndex]}); +- assertEquals("plaintext index: " + plaintextIndex, 1, output.length); +- assertEquals("plaintext index: " + plaintextIndex, +- expectedCiphertext[plaintextIndex], output[0]); ++ if (!isStrongbox()) { ++ assertTrue(output != null); ++ assertEquals("plaintext index: " + plaintextIndex, 1, output.length); ++ } ++ if (output != null) { ++ for (int i = 0; i < output.length; ++i) { ++ assertEquals("ciphertext comparison position: " + comparingPosition, ++ expectedCiphertext[comparingPosition], output[i]); ++ comparingPosition += 1; ++ } ++ } + } + byte[] finalOutput = doFinal(); + byte[] expectedFinalOutput; +- if (isAuthenticatedCipher()) { ++ if (isAuthenticatedCipher() || (isStrongbox() && finalOutput.length != 0)) { + expectedFinalOutput = +- subarray(expectedCiphertext, plaintext.length, expectedCiphertext.length); ++ subarray(expectedCiphertext, comparingPosition, expectedCiphertext.length); + } else { + expectedFinalOutput = EmptyArray.BYTE; + } +@@ -814,15 +824,28 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + byte[] finalOutput = doFinal(); + assertEquals(expectedPlaintext, finalOutput); + } else if (isStreamCipher()) { +- // Unauthenticated stream cipher -- one byte in, one byte out ++ int comparingPosition = 0; ++ // Unauthenticated stream cipher -- one byte in, one byte out (unless when Strongbox is used) + for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) { + byte[] output = update(new byte[] {ciphertext[ciphertextIndex]}); +- assertEquals("ciphertext index: " + ciphertextIndex, 1, output.length); +- assertEquals("ciphertext index: " + ciphertextIndex, +- expectedPlaintext[ciphertextIndex], output[0]); ++ if (!isStrongbox()) { ++ assertTrue(output != null); ++ assertEquals("ciphertext index: " + ciphertextIndex, 1, output.length); ++ } ++ if (output != null) { ++ for (int i = 0; i < output.length; ++i) { ++ assertEquals("plaintext comparison position: " + comparingPosition, ++ expectedPlaintext[comparingPosition], output[i]); ++ comparingPosition += 1; ++ } ++ } + } + byte[] finalOutput = doFinal(); +- assertEquals(0, finalOutput.length); ++ int expectedPlainTextLength = 0; ++ if (isStrongbox()) { ++ expectedPlainTextLength = (expectedPlaintext.length - comparingPosition); ++ } ++ assertEquals(expectedPlainTextLength, finalOutput.length); + } else { + // Unauthenticated block cipher -- operates in full blocks only + +@@ -1187,6 +1210,8 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + throw new AssertionFailedError("Unsupported opmode: " + opmode); + } + ++ boolean allowZeroLengthOutput = expectedOutput.length == 0; ++ + int inputEndIndexInBuffer = inputOffsetInBuffer + input.length; + int outputEndIndexInBuffer = outputOffsetInBuffer + expectedOutput.length; + +@@ -1195,15 +1220,15 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length); + createCipher(); + initKat(opmode); +- String additionalInformation = ""; +- if (isStrongbox() && opmode == Cipher.ENCRYPT_MODE) { +- additionalInformation = "May fail due to b/194134359"; +- } +- assertEquals(additionalInformation, expectedOutput.length, +- update(buffer, inputOffsetInBuffer, input.length, +- buffer, outputOffsetInBuffer)); +- assertEquals(expectedOutput, +- subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer)); ++ int bytes = update(buffer, inputOffsetInBuffer, input.length, ++ buffer, outputOffsetInBuffer); ++ // We make little assumptions about the size of the output. But we make sure that at least ++ // one block was processed. ++ assertTrue(bytes >= blockSize || (allowZeroLengthOutput && bytes == 0)); ++ // Check that all that was processed was as expected. ++ assertEquals(subarray(expectedOutput, 0, bytes), ++ subarray(buffer, outputOffsetInBuffer, outputOffsetInBuffer + bytes)); ++ + + if (outputOffsetInBuffer == 0) { + // We can use the update variant which assumes that output offset is 0. +@@ -1211,10 +1236,10 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length); + createCipher(); + initKat(opmode); +- assertEquals(expectedOutput.length, +- update(buffer, inputOffsetInBuffer, input.length, buffer)); +- assertEquals(expectedOutput, +- subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer)); ++ bytes = update(buffer, inputOffsetInBuffer, input.length, buffer); ++ assertTrue(bytes >= blockSize || (allowZeroLengthOutput && bytes == 0)); ++ assertEquals(subarray(expectedOutput, 0, bytes), ++ subarray(buffer, outputOffsetInBuffer, outputOffsetInBuffer + bytes)); + } + + // Test the update(ByteBuffer, ByteBuffer) variant +@@ -1225,9 +1250,10 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + ByteBuffer.wrap(buffer, outputOffsetInBuffer, expectedOutput.length); + createCipher(); + initKat(opmode); +- assertEquals(expectedOutput.length, update(inputBuffer, outputBuffer)); +- assertEquals(expectedOutput, +- subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer)); ++ bytes = update(inputBuffer, outputBuffer); ++ assertTrue(bytes >= blockSize || (allowZeroLengthOutput && bytes == 0)); ++ assertEquals(subarray(expectedOutput, 0, bytes), ++ subarray(buffer, outputOffsetInBuffer, outputOffsetInBuffer + bytes)); + } + + public void testDoFinalCopySafe() throws Exception { +@@ -1485,16 +1511,15 @@ abstract class BlockCipherTestBase extends AndroidTestCase { + 0, outputLength); + return; + } ++ /* ++ * Strongbox implementations did not have the following restrictions. ++ */ ++ if (isStrongbox()) return; + + if (isStreamCipher()) { + if (outputLength != inputLength) { +- if (isStrongbox()) { +- fail("Output of update (" + outputLength + ") not same size as input (" +- + inputLength + ") b/194123581"); +- } else { +- fail("Output of update (" + outputLength + ") not same size as input (" +- + inputLength + ")"); +- } ++ fail("Output of update (" + outputLength + ") not same size as input (" ++ + inputLength + ")"); + } + } else { + if ((outputLength % getBlockSize()) != 0) { From 49f490caa51ceff99ff20db9ab530525116a6df4 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Tue, 9 Nov 2021 23:49:32 +0000 Subject: [PATCH 14/33] Added implementation for MAX_USES_PER_BOOT --- .../javacard/test/KMFunctionalTest.java | 195 +++++++++++++++--- .../android/javacard/keymaster/KMError.java | 1 + .../javacard/keymaster/KMKeyParameters.java | 4 +- .../javacard/keymaster/KMKeymasterApplet.java | 92 +++++++-- .../javacard/keymaster/KMRepository.java | 126 ++++++++++- 5 files changed, 365 insertions(+), 53 deletions(-) diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index 60fe778b..941d9b68 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -86,14 +86,13 @@ public class KMFunctionalTest { private static final byte INS_BEGIN_KM_CMD = 0x00; private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD + 1; //0x01 - private static final byte INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD + 2; //0x02 - private static final byte INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD + 3; //0x03 - private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 4; //0x04 - private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 5; //0x05 - private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 6; //0x06 - private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 7; //0x07 - private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 8; //0x08 - private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 9; //0x09 + private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = INS_BEGIN_KM_CMD + 2; //0x02 + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 3; //0x04 + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 4; //0x05 + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 5; //0x06 + private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 6; //0x07 + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 7; //0x08 + private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 8; //0x09 // Top 32 commands are reserved for provisioning. private static final byte INS_END_KM_PROVISION_CMD = 0x20; @@ -535,6 +534,8 @@ private void setBootParams(CardSimulator simulator, short bootPatchLevel) { } private void provisionSigningCertificate(CardSimulator simulator) { + short arrPtr = KMArray.instance((short) 3); + short byteBlobPtr = KMByteBlob.instance( (short) (kEcAttestCert.length + kEcAttestRootCert.length)); Util.arrayCopyNonAtomic(kEcAttestCert, (short) 0, @@ -546,8 +547,17 @@ private void provisionSigningCertificate(CardSimulator simulator) { (short) (KMByteBlob.cast(byteBlobPtr).getStartOff() + kEcAttestCert.length), (short) kEcAttestRootCert.length); + KMArray.cast(arrPtr).add((short) 0, byteBlobPtr); + + short byteBlob1 = KMByteBlob.instance(X509Issuer, (short) 0, + (short) X509Issuer.length); + KMArray.cast(arrPtr).add((short) 1, byteBlob1); + short byteBlob2 = KMByteBlob.instance(expiryTime, (short) 0, + (short) expiryTime.length); + KMArray.cast(arrPtr).add((short) 2, byteBlob2); + CommandAPDU apdu = encodeApdu( - (byte) INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD, byteBlobPtr); + (byte) INS_PROVISION_ATTESTATION_CERT_DATA_CMD, arrPtr); // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); Assert.assertEquals(0x9000, response.getSW()); @@ -594,23 +604,6 @@ private void provisionSigningKey(CardSimulator simulator) { Assert.assertEquals(0x9000, response.getSW()); } - private void provisionCertificateParams(CardSimulator simulator) { - - short arrPtr = KMArray.instance((short) 2); - short byteBlob1 = KMByteBlob.instance(X509Issuer, (short) 0, - (short) X509Issuer.length); - KMArray.cast(arrPtr).add((short) 0, byteBlob1); - short byteBlob2 = KMByteBlob.instance(expiryTime, (short) 0, - (short) expiryTime.length); - KMArray.cast(arrPtr).add((short) 1, byteBlob2); - - CommandAPDU apdu = encodeApdu( - (byte) INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - } - private void provisionSharedSecret(CardSimulator simulator) { byte[] sharedKeySecret = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -677,7 +670,6 @@ private void provisionLocked(CardSimulator simulator) { private void provisionCmd(CardSimulator simulator) { provisionSigningKey(simulator); provisionSigningCertificate(simulator); - provisionCertificateParams(simulator); provisionSharedSecret(simulator); provisionAttestIds(simulator); // set bootup parameters @@ -1209,6 +1201,142 @@ private short extractKeyBlobArray(short keyBlob) { .cast(keyBlob).getStartOff(), KMByteBlob.cast(keyBlob).length()); } + @Test + public void testRateLimitExceptsMaxOpsExceeded() { + init(); + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 2)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + + keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); + Util.arrayCopyNonAtomic(keyBlob, (short) 0, + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + (short) keyBlob.length); + inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + + keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); + Util.arrayCopyNonAtomic(keyBlob, (short) 0, + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + (short) keyBlob.length); + inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + Assert.assertEquals(KMError.KEY_MAX_OPS_EXCEEDED, beginResp); + cleanUp(); + } + + @Test + public void testRateLimitExceptsTooManyOperations() { + init(); + byte[] plainData = "Hello World 123!".getBytes(); + for (int i = 0; i <= 8; i++) { + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 1)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + if (i == 8) { + // Only 8 keys are allowed for MAX_USES_PER_BOOT + Assert.assertEquals(KMError.TOO_MANY_OPERATIONS, beginResp); + return; + } + short opHandle = KMArray.cast(beginResp).get((short) 2); + finish(opHandle, + KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, + (short) 0, (short) 0, (short) 0, KMError.OK, false); + } + cleanUp(); + } + + @Test + public void testRateLimitClearBufferAfterReboot() { + init(); + byte[] plainData = "Hello World 123!".getBytes(); + for (int i = 0; i <= 32; i++) { + if (i % 8 == 0) { + // Simulate reboot using set boot parameters. + // Clear the rate limited keys from the flash memory + setBootParams(simulator, (short) BOOT_PATCH_LEVEL); + } + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 1)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + short opHandle = KMArray.cast(beginResp).get((short) 2); + // Finish + finish(opHandle, + KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, + (short) 0, (short) 0, (short) 0, KMError.OK, false); + } + cleanUp(); + } + + @Test + public void testRateLimitWithHugeCount() { + init(); + short maxUsesPerBoot = 1000; + byte[] plainData = "Hello World 123!".getBytes(); + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_16(maxUsesPerBoot)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + + for (int i = 0; i <= maxUsesPerBoot; i++) { + // Cache keyblob + keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); + Util.arrayCopyNonAtomic(keyBlob, (short) 0, + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + (short) keyBlob.length); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + if (i == maxUsesPerBoot) { + Assert.assertEquals(KMError.KEY_MAX_OPS_EXCEEDED, beginResp); + return; + } + short opHandle = KMArray.cast(beginResp).get((short) 2); + // Finish + finish(opHandle, + KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, + (short) 0, (short) 0, (short) 0, KMError.OK, false); + } + cleanUp(); + } + @Test public void testRsaGenerateKeySuccess() { init(); @@ -1236,7 +1364,7 @@ public void testRsaGenerateKeySuccess() { cleanUp(); } - private short generateRsaKey(byte[] clientId, byte[] appData) { + private short generateRsaKey(byte[] clientId, byte[] appData, short keyUsageLimitPtr) { byte[] activeAndCreationDateTime = {0, 0, 0x01, 0x73, 0x51, 0x7C, (byte) 0xCC, 0x00}; short tagCount = 11; if (clientId != null) { @@ -1245,6 +1373,9 @@ private short generateRsaKey(byte[] clientId, byte[] appData) { if (appData != null) { tagCount++; } + if (keyUsageLimitPtr != KMType.INVALID_VALUE) { + tagCount++; + } short arrPtr = KMArray.instance(tagCount); short keySize = KMIntegerTag .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 2048)); @@ -1297,6 +1428,10 @@ private short generateRsaKey(byte[] clientId, byte[] appData) { KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData, (short) 0, (short) appData.length))); } + if (keyUsageLimitPtr != KMType.INVALID_VALUE) { + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag + .instance(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, keyUsageLimitPtr)); + } short keyParams = KMKeyParameters.instance(arrPtr); arrPtr = KMArray.instance((short) 1); KMArray arg = KMArray.cast(arrPtr); @@ -1316,6 +1451,10 @@ private short generateRsaKey(byte[] clientId, byte[] appData) { return ret; } + private short generateRsaKey(byte[] clientId, byte[] appData) { + return generateRsaKey(clientId, appData, KMType.INVALID_VALUE); + } + private short generateAttestationKey() { // 15th July 2020 00.00.00 byte[] activeAndCreationDateTime = {0, 0, 0x01, 0x73, 0x51, 0x7C, (byte) 0xCC, 0x00}; diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 0b4373d3..c79fd4b8 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; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 17b3311d..fd3f1143 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -116,8 +116,7 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { 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; @@ -171,6 +170,7 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, }; byte index = 0; short tagInd; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 9b897226..213bea45 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1974,11 +1974,58 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad); + if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } } } - private void authorizeDeviceUnlock(short hwToken, byte[] scratchPad) { + 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 = @@ -1986,11 +2033,10 @@ private void authorizeDeviceUnlock(short hwToken, byte[] scratchPad) { 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); } - validateHwToken(data[HW_TOKEN], scratchPad); - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(); // Check if the current auth time stamp is greater then device locked time stamp short ts = repository.getDeviceTimeStamp(); if (KMInteger.compare(ptr, ts) <= 0) { @@ -1999,7 +2045,7 @@ private void authorizeDeviceUnlock(short hwToken, byte[] scratchPad) { // 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); @@ -2570,7 +2616,8 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizePadding(op); authorizeBlockModeAndMacLength(op); authorizeUserSecureIdAuthTimeout(op, scratchPad); - authorizeDeviceUnlock(data[HW_TOKEN], scratchPad); + authorizeDeviceUnlock(scratchPad); + authorizeKeyUsageForCount(scratchPad); //Validate bootloader only tmpVariables[0] = @@ -2805,7 +2852,9 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); if (authTimeoutTagPtr != KMType.INVALID_VALUE) { // authenticate user - authTokenMatches(userSecureIdPtr, authType, scratchPad); + 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]); @@ -2851,18 +2900,21 @@ private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, return false; } - private void authTokenMatches(short userSecureIdsPtr, short authType, + private boolean authTokenMatches(short userSecureIdsPtr, short authType, byte[] scratchPad) { - validateHwToken(data[HW_TOKEN], scratchPad); + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { + return false; + } if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + 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) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + return false; } + return true; } private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, byte[] scratchPad) { @@ -2955,23 +3007,18 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, short key, byte[] KMByteBlob.cast(ptr).length()); } - private void validateHwToken(short hwToken, byte[] scratchPad) { + 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) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); + return false; } short key = repository.getComputedHmacKey(); - boolean verify; if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { - verify = verifyHwTokenMacInLittleEndian(hwToken, key, scratchPad); + return verifyHwTokenMacInLittleEndian(hwToken, key, scratchPad); } else { - verify = verifyHwTokenMacInBigEndian(hwToken, key, scratchPad); - } - if (!verify) { - // Throw Exception if none of the combination works. - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + return verifyHwTokenMacInBigEndian(hwToken, key, scratchPad); } } @@ -3479,6 +3526,9 @@ private void processSetBootParamsCmd(APDU apdu) { //flag to maintain early boot ended state isEarlyBootEnded = false; + + // Clear all the auth tags + repository.removeAllAuthTags(); } private static void processGenerateKey(APDU apdu) { diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index e86fca68..0d8ede80 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -44,9 +44,9 @@ public class KMRepository implements KMUpgradable { public static final byte ATT_ID_MODEL = 7; // Data table configuration other non provisioned parameters. - public static final short DATA_INDEX_SIZE = 13; + public static final short DATA_INDEX_SIZE = 21; public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_MEM_SIZE = 300; + public static final short DATA_MEM_SIZE = 500; public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -72,6 +72,14 @@ public class KMRepository implements KMUpgradable { public static final byte DEVICE_LOCKED_TIME = 10; public static final byte DEVICE_LOCKED = 11; public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 12; + public static final byte AUTH_TAG_1 = 13; + public static final byte AUTH_TAG_2 = 14; + public static final byte AUTH_TAG_3 = 15; + public static final byte AUTH_TAG_4 = 16; + public static final byte AUTH_TAG_5 = 17; + public static final byte AUTH_TAG_6 = 18; + public static final byte AUTH_TAG_7 = 19; + public static final byte AUTH_TAG_8 = 20; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -88,6 +96,10 @@ public class KMRepository implements KMUpgradable { public static final short MAX_OPS = 4; public static final byte BOOT_KEY_MAX_SIZE = 32; public static final byte BOOT_HASH_MAX_SIZE = 32; + public static final 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); private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -800,6 +812,116 @@ public void setBootState(byte state) { writeDataEntry(BOOT_VERIFIED_BOOT_STATE, getHeap(), start, BOOT_STATE_SIZE); } + 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); From 62066b550a5f87c8a108f870969928d78ed8190d Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sun, 14 Nov 2021 16:11:42 +0000 Subject: [PATCH 15/33] No Buffering for Block ciphers except for AES PKCS7 and AES GCM Decryption --- .../javacard/keymaster/KMByteBlob.java | 4 + .../javacard/keymaster/KMKeymasterApplet.java | 154 ++++++------------ .../4.1/JavacardKeymaster4Device.cpp | 19 +-- .../4.1/JavacardOperationContext.cpp | 52 +++--- .../include/JavacardOperationContext.h | 2 +- 5 files changed, 87 insertions(+), 144 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index a6f4c529..d980bd9a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -130,4 +130,8 @@ public void decrementLength(short len) { length = (short) (length - len); Util.setShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1), length); } + + public void setLength(short len) { + Util.setShort(heap, (short)(instanceTable[KM_BYTE_BLOB_OFFSET] + 1), len); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 213bea45..db9a55ba 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1696,7 +1696,7 @@ private void processFinishOperationCmd(APDU apdu) { finishSigningVerifyingOperation(op, scratchPad); break; case KMType.ENCRYPT: - finishEncryptOperation(op, scratchPad); + finishEncryptOperation(op); break; case KMType.DECRYPT: finishDecryptOperation(op, scratchPad); @@ -1721,55 +1721,58 @@ private void processFinishOperationCmd(APDU apdu) { sendOutgoing(apdu); } - private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { + private void finishEncryptOperation(KMOperationState op) { + if(op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES){ + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + finishAesDesOperation(op); + } + + private void finishAesDesOperation(KMOperationState op) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - short blockSize; - switch (op.getAlgorithm()) { - case KMType.AES: - case KMType.DES: - if (op.getAlgorithm() == KMType.AES) { - blockSize = AES_BLOCK_SIZE; - } else { - blockSize = DES_BLOCK_SIZE; - } - // If no padding then data length must be block aligned - if ((op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) - && op.getPadding() == KMType.PADDING_NONE - && ((short) (len % blockSize) != 0)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } else if (op.getBlockMode() == KMType.GCM) { - // update aad if there is any - updateAAD(op, (byte) 0x01); - // 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 - tmpVariables[0] = 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(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff()); + short blockSize = DES_BLOCK_SIZE; + if (op.getAlgorithm() == KMType.AES) { + blockSize = AES_BLOCK_SIZE; + } - data[OUTPUT_DATA] = - KMByteBlob.instance( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - len); - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - break; + if((op.getPurpose() == KMType.DECRYPT) && (op.getPadding() == KMType.PKCS7) + && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) + && ((short) (len % blockSize) != 0)){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } + + if (op.getBlockMode() == KMType.GCM) { + if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // update aad if there is any + updateAAD(op, (byte) 0x01); + // 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 + 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); + } + } + + // Update the length of the output + KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len); } private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - short blockSize; switch (op.getAlgorithm()) { case KMType.RSA: // Fill the scratch pad with zero @@ -1790,38 +1793,7 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { break; case KMType.AES: case KMType.DES: - if (op.getAlgorithm() == KMType.AES) { - blockSize = AES_BLOCK_SIZE; - } else { - blockSize = DES_BLOCK_SIZE; - } - if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB) - && len > 0 - && (len % blockSize) != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } else if (op.getBlockMode() == KMType.GCM) { - // update aad if there is any - updateAAD(op, (byte) 0x01); - // Check if there is at least MAC Length bytes of input data - if ((len < (short) (op.getMacLength() / 8))) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // Get the output size - in case of JCardSim this might be more than input size - len = - op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); - } - tmpVariables[1] = repository.alloc((short) (len + blockSize)); - byte[] heap = repository.getHeap(); - len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - heap, - tmpVariables[1]); - - data[OUTPUT_DATA] = KMByteBlob.instance(heap, tmpVariables[1], len); + finishAesDesOperation(op); break; } } @@ -2210,16 +2182,9 @@ private void processUpdateOperationCmd(APDU apdu) { KMException.throwIt(KMError.OPERATION_CANCELLED); } short inputLen = KMByteBlob.cast(data[INPUT_DATA]).length(); - short additionalExpOutLen = 0; + short blockSize = DES_BLOCK_SIZE; if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB) { - // input data must be block aligned. - // 128 bit block size - HAL must send block aligned data - if (inputLen % AES_BLOCK_SIZE != 0 || inputLen <= 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } else { /* CTR or GCM Modes */ - additionalExpOutLen = AES_BLOCK_SIZE; + blockSize = AES_BLOCK_SIZE; if (op.getBlockMode() == KMType.GCM) { updateAAD(op, (byte) 0x00); // if input data present @@ -2230,15 +2195,9 @@ private void processUpdateOperationCmd(APDU apdu) { } } } - } - } else if (op.getAlgorithm() == KMType.DES) { - // 64 bit block size - HAL must send block aligned data - if (inputLen % 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) (inputLen + additionalExpOutLen)); + data[OUTPUT_DATA] = KMByteBlob.instance((short) (inputLen + 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. @@ -2257,16 +2216,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 (tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()) { - data[INPUT_DATA] = data[OUTPUT_DATA]; - data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); - 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(), - tmpVariables[0]); - } + KMByteBlob.cast(data[OUTPUT_DATA]).setLength(tmpVariables[0]); } // Persist if there are any updates. op.persist(); diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index bc1ff735..e2467460 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -1503,11 +1503,10 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device cppbor::Array array; std::unique_ptr item; std::vector cborOutData; - ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; + V41ErrorCode errorCode = V41ErrorCode::UNKNOWN_ERROR; std::vector asn1ParamsVerified; - ErrorCode ret = ErrorCode::UNKNOWN_ERROR; - if(ErrorCode::OK != (ret = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { + if(V41ErrorCode::OK != (errorCode = static_cast(encodeParametersVerified(verificationToken, asn1ParamsVerified)))) { LOG(DEBUG) << "INS_DEVICE_LOCKED_CMD: Error in encodeParametersVerified, status: " << (int32_t) errorCode; return errorCode; } @@ -1517,11 +1516,11 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); std::vector cborData = array.encode(); - ret = sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData); + errorCode = static_cast(sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData)); - if(ret == ErrorCode::OK) { + if(errorCode == V41ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( + std::tie(item, errorCode) = decodeData( cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); } return errorCode; @@ -1532,13 +1531,13 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device std::string message; std::vector cborOutData; std::vector cborInput; - ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; + V41ErrorCode errorCode = V41ErrorCode::UNKNOWN_ERROR; - ErrorCode ret = sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData); + errorCode = static_cast(sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData)); - if(ret == ErrorCode::OK) { + if(errorCode == V41ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( + std::tie(item, errorCode) = decodeData( cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); } return errorCode; diff --git a/HAL/keymaster/4.1/JavacardOperationContext.cpp b/HAL/keymaster/4.1/JavacardOperationContext.cpp index 46816cfc..64c13c71 100644 --- a/HAL/keymaster/4.1/JavacardOperationContext.cpp +++ b/HAL/keymaster/4.1/JavacardOperationContext.cpp @@ -215,18 +215,17 @@ ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector& input, +ErrorCode OperationContext::bufferData(uint64_t operHandle, std::vector& input, Operation opr, std::vector& out) { BufferedData& data = operationTable[operHandle].data; int dataToSELen = 0;/*Length of the data to be send to the Applet.*/ @@ -250,25 +249,20 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, std::vector } else { /*Update */ //Calculate the block sized length on combined input of both buffered data and input data. - // AES/TDES, ECB and CBC Modes: - // Buffer till (blockSize - 1) bytes of data is received. - // AES GCM(Encrypt) or CTR Modes: - // No Buffering. - // AES/TDES, Decrypt PKCS7 Padding: + // AES/TDES, Encrypt/Decrypt PKCS7 Padding: // Buffer till blockSize of data is received. // AES GCM Decrypt: // Buffer tag length bytes of data. - //For symmetric ciphers, decryption operation and PKCS7 padding mode or AES GCM operation save the last - // blockSize/tagSize bytes of data and send this block in finish operation. This is done to make sure - // that there will be always a blockSize/tagSize bytes of data left for finish operation so that javacard - // Applet may remove PKCS7 padding if any or get the tag data for AES GCM operation for authentication purpose. - if (operationTable[operHandle].info.pad == PaddingMode::PKCS7 && - operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { - /* Buffer till we receive more than blockSize of data of atleast one byte*/ - dataToSELen = ((data.buf_len + input.size())/blockSize) * blockSize; - int remaining = ((data.buf_len + input.size()) % blockSize); - if (dataToSELen >= blockSize && remaining == 0) { - dataToSELen -= blockSize; + if (operationTable[operHandle].info.pad == PaddingMode::PKCS7) { + if (operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { + /* Buffer till we receive more than blockSize of data of atleast one byte*/ + dataToSELen = ((data.buf_len + input.size()) / blockSize) * blockSize; + int remaining = ((data.buf_len + input.size()) % blockSize); + if (dataToSELen >= blockSize && remaining == 0) { + dataToSELen -= blockSize; + } + } else { // Encrypt + dataToSELen = ((data.buf_len + input.size()) / blockSize) * blockSize; } } else if (operationTable[operHandle].info.mode == BlockMode::GCM && operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { @@ -277,13 +271,9 @@ ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, std::vector if ((data.buf_len + input.size()) > operationTable[operHandle].info.macLength) { dataToSELen = (data.buf_len + input.size()) - operationTable[operHandle].info.macLength; } - } else if (operationTable[operHandle].info.mode == BlockMode::GCM || - operationTable[operHandle].info.mode == BlockMode::CTR) { + } else { /* No Buffering */ dataToSELen = input.size(); - } else { - /* Buffer (BlockSize - 1) bytes */ - dataToSELen = ((data.buf_len + input.size())/blockSize) * blockSize; } //Copy data to be send to SE from buffer, only if atleast a minimum block aligned size is available. if(dataToSELen > 0) { @@ -332,7 +322,7 @@ ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, std::vecto if(Algorithm::AES == operationTable[operHandle].info.alg || Algorithm::TRIPLE_DES == operationTable[operHandle].info.alg) { /*Symmetric */ - if(ErrorCode::OK != (errorCode = getBlockAlignedData(operHandle, data, + if(ErrorCode::OK != (errorCode = bufferData(operHandle, data, opr, out))) { return errorCode; } diff --git a/HAL/keymaster/include/JavacardOperationContext.h b/HAL/keymaster/include/JavacardOperationContext.h index ff12dbb1..0d452c67 100644 --- a/HAL/keymaster/include/JavacardOperationContext.h +++ b/HAL/keymaster/include/JavacardOperationContext.h @@ -137,7 +137,7 @@ class OperationContext { * reamining data for update calls only. For finish calls it extracts all the buffered data combines it with * input data. */ - ErrorCode getBlockAlignedData(uint64_t operHandle, std::vector& input, + ErrorCode bufferData(uint64_t operHandle, std::vector& input, Operation opr, std::vector& out); /** * This function sends the data back to the caller using callback functions. It does some processing on input data From 9a103defebdf3a3465fea438df338a536ec70626 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Mon, 15 Nov 2021 23:14:36 +0000 Subject: [PATCH 16/33] Store ComputedHmac Key inside a KeyObject --- .../keymaster/KMAndroidSEProvider.java | 38 +++++--- .../android/javacard/keymaster/KMHmacKey.java | 6 +- .../javacard/keymaster/KMConfigurations.java | 1 + .../android/javacard/keymaster/KMHmacKey.java | 6 +- .../javacard/keymaster/KMJCardSimulator.java | 43 ++++++--- .../javacard/test/KMFunctionalTest.java | 56 +++++------- .../javacard/keymaster/KMKeymasterApplet.java | 39 ++++---- .../javacard/keymaster/KMRepository.java | 90 +++++++++---------- .../javacard/keymaster/KMSEProvider.java | 25 ++++-- 9 files changed, 169 insertions(+), 135 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 617f9ff3..9ac63267 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -117,6 +117,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; private static final short MAX_OPERATIONS = 4; + private static final short COMPUTED_HMAC_KEY_SIZE = 32; private static final short CERT_CHAIN_OFFSET = 0; private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; @@ -177,6 +178,7 @@ public class KMAndroidSEProvider implements KMSEProvider { private KMAESKey masterKey; private KMECPrivateKey attestationKey; private KMHmacKey preSharedKey; + private KMHmacKey computedHmacKey; private static KMAndroidSEProvider androidSEProvider = null; @@ -728,9 +730,11 @@ 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, + @Override + public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - hmacSignature.init(key, Signature.MODE_VERIFY); + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); } @@ -757,15 +761,6 @@ 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); - } - @Override public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength, @@ -1179,6 +1174,7 @@ public void onSave(Element element) { KMAESKey.onSave(element, masterKey); KMECPrivateKey.onSave(element, attestationKey); KMHmacKey.onSave(element, preSharedKey); + KMHmacKey.onSave(element, computedHmacKey); } @Override @@ -1187,6 +1183,7 @@ public void onRestore(Element element) { masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); + computedHmacKey = KMHmacKey.onRestore(element); } @Override @@ -1242,6 +1239,20 @@ public KMAttestationKey createAttestationKey(byte[] keyData, short offset, attestationKey.setS(keyData, offset, length); return (KMAttestationKey) attestationKey; } + + @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 KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length) { @@ -1281,4 +1292,9 @@ public void releaseAllOperations() { index++; } } + + @Override + public KMComputedHmacKey getComputedHmacKey() { + return computedHmacKey; + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java index b2a38b24..98f623b2 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java @@ -21,7 +21,7 @@ import javacard.security.HMACKey; -public class KMHmacKey implements KMPreSharedKey { +public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { private HMACKey hmacKey; @@ -36,6 +36,10 @@ 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/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java index fd122b16..6e5090a1 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -25,4 +25,5 @@ public class KMConfigurations { public static final short CERT_CHAIN_MAX_SIZE = 2500; public static final short CERT_ISSUER_MAX_SIZE = 250; public static final short CERT_EXPIRY_MAX_SIZE = 20; + public static final short TOTAL_ATTEST_IDS_SIZE = 300; } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java index 65f1d02a..64837ace 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java @@ -17,7 +17,7 @@ import javacard.security.HMACKey; -public class KMHmacKey implements KMPreSharedKey { +public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { private HMACKey hmacKey; @@ -33,6 +33,10 @@ 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/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index 749264cc..d870f82a 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -77,6 +77,7 @@ public class KMJCardSimulator implements KMSEProvider { private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; private static final short CERT_EXPIRY_OFFSET = (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); + private static final short COMPUTED_HMAC_KEY_SIZE = 32; public static boolean jcardSim = false; private static Signature kdf; @@ -91,6 +92,7 @@ public class KMJCardSimulator implements KMSEProvider { private KMAESKey masterKey; private KMECPrivateKey attestationKey; private KMHmacKey preSharedKey; + private KMHmacKey computedHmacKey; private static KMJCardSimulator jCardSimulator = null; @@ -544,12 +546,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) { @@ -562,17 +558,19 @@ public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, } @Override - public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, - short dataStart, short dataLength, byte[] mac, short macStart) { - HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacSign(key, data, dataStart, dataLength, mac, macStart); + 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 - public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, - short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { + public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, + short dataStart, short dataLength, byte[] mac, short macStart) { HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacVerify(key, data, dataStart, dataLength, mac, macStart, macLength); + return hmacSign(key, data, dataStart, dataLength, mac, macStart); } @Override @@ -1338,6 +1336,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; @@ -1353,6 +1365,11 @@ public KMPreSharedKey getPresharedKey() { return (KMPreSharedKey) preSharedKey; } + @Override + public KMComputedHmacKey getComputedHmacKey() { + return (KMComputedHmacKey) computedHmacKey; + } + @Override public void releaseAllOperations() { //Do nothing. diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index 941d9b68..bbba28ab 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -20,7 +20,9 @@ import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMComputedHmacKey; import com.android.javacard.keymaster.KMConfigurations; +import com.android.javacard.keymaster.KMHmacKey; import com.android.javacard.keymaster.KMJCardSimApplet; import com.android.javacard.keymaster.KMJCardSimulator; import com.android.javacard.keymaster.KMSEProvider; @@ -919,7 +921,7 @@ public void testDeviceLocked() { init(); byte[] hmacKey = new byte[32]; cryptoProvider.newRandomNumber(hmacKey, (short) 0, (short) 32); - KMRepository.instance().initComputedHmac(hmacKey, (short) 0, (short) 32); + cryptoProvider.createComputedHmacKey(hmacKey, (short) 0, (short) 32); // generate aes key with unlocked_device_required short aesKey = generateAesDesKey(KMType.AES, (short) 128, null, null, true); short keyBlobPtr = KMArray.cast(aesKey).get((short) 1); @@ -946,26 +948,7 @@ public void testDeviceLocked() { KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short) 1)); verToken = signVerificationToken(verToken, KMConfigurations.TEE_MACHINE_TYPE); // device locked request - deviceLock(verToken); - // decrypt should fail - inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); - short beginResp = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(inParams), (short) 0, false); - Assert.assertEquals(beginResp, KMError.DEVICE_LOCKED); - short hwToken = KMHardwareAuthToken.instance(); - KMHardwareAuthToken.cast(hwToken).setTimestamp(KMInteger.uint_16((byte) 2)); - KMHardwareAuthToken.cast(hwToken) - .setHwAuthenticatorType(KMEnum.instance(KMType.USER_AUTH_TYPE, (byte) KMType.PASSWORD)); - inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); - hwToken = signHwToken(hwToken); - ret = processMessage(cipherData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.DECRYPT, - KMKeyParameters.instance(inParams), hwToken, null, false, false - ); - ret = KMArray.cast(ret).get((short) 0); - Assert.assertEquals(KMInteger.cast(ret).getShort(), KMError.OK); + deviceLock(verToken, KMError.VERIFICATION_FAILED); cleanUp(); } @@ -1008,13 +991,9 @@ private short signHwToken(short hwToken) { */ byte[] mac = new byte[32]; - /* - len = - cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, - mac, - (short)0); - */ - short key = KMRepository.instance().getComputedHmacKey(); + short key = KMByteBlob.instance((short) 32); + KMHmacKey computedHmacKey = (KMHmacKey) cryptoProvider.getComputedHmacKey(); + computedHmacKey.getKey(KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff()); cryptoProvider.hmacSign( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), @@ -1027,16 +1006,25 @@ private short signHwToken(short hwToken) { return hwToken; } - private void deviceLock(short verToken) { + private void deviceLock(short verToken, short expectedError) { short req = KMArray.instance((short) 2); KMArray.cast(req).add((short) 0, KMInteger.uint_8((byte) 1)); KMArray.cast(req).add((short) 1, verToken); CommandAPDU apdu = encodeApdu((byte) INS_DEVICE_LOCKED_CMD, req); ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 1); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); byte[] respBuf = response.getBytes(); - Assert.assertEquals(respBuf[0], KMError.OK); + short len = (short) respBuf.length; + byte majorType = readMajorType(respBuf); + short retError; + if (majorType == CBOR_ARRAY_MAJOR_TYPE) { + short ret = KMArray.instance((short) 1); + ret = decoder.decode(ret, respBuf, (short) 0, len); + retError = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + } else {//Major type UINT. + short ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, len); + retError = KMInteger.cast(ret).getShort(); + } + Assert.assertEquals(retError, expectedError); } private short signVerificationToken(short verToken, byte machineType) { @@ -1095,7 +1083,9 @@ private short signVerificationToken(short verToken, byte machineType) { mac, (short)0); */ - short key = KMRepository.instance().getComputedHmacKey(); + short key = KMByteBlob.instance((short) 32); + KMHmacKey computedHmacKey = (KMHmacKey) cryptoProvider.getComputedHmacKey(); + computedHmacKey.getKey(KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff()); cryptoProvider.hmacSign(KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), KMByteBlob.cast(key).length(), diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index db9a55ba..41bf66f6 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1131,7 +1131,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { scratchPad, (short) 0); // persist the computed hmac key. - repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[6]); + seProvider.createComputedHmacKey(scratchPad, (short) 0, tmpVariables[6]); // Generate sharingKey verification signature and store that in scratch pad. tmpVariables[5] = @@ -2030,7 +2030,7 @@ private void authorizeDeviceUnlock(byte[] scratchPad) { } } - private boolean verifyVerificationTokenMacInBigEndian(short verToken, short key, byte[] scratchPad) { + private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) { // concatenation length will be 37 + length of verified parameters list - which // is typically // empty @@ -2059,9 +2059,7 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, short key, ptr = KMVerificationToken.cast(verToken).getMac(); return seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), + seProvider.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -2070,7 +2068,7 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, short key, KMByteBlob.cast(ptr).length()); } - private boolean verifyVerificationTokenMacInLittleEndian(short verToken, short key, byte[] scratchPad) { + private boolean verifyVerificationTokenMacInLittleEndian(short verToken, byte[] scratchPad) { // concatenation length will be 37 + length of verified parameters list - which // is typically // empty @@ -2099,9 +2097,7 @@ private boolean verifyVerificationTokenMacInLittleEndian(short verToken, short k ptr = KMVerificationToken.cast(verToken).getMac(); return seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), + seProvider.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -2116,12 +2112,11 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { if (KMByteBlob.cast(ptr).length() == 0) { KMException.throwIt(KMError.INVALID_MAC_LENGTH); } - short key = repository.getComputedHmacKey(); boolean verify; if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { - verify = verifyVerificationTokenMacInLittleEndian(verToken, key, scratchPad); + verify = verifyVerificationTokenMacInLittleEndian(verToken, scratchPad); } else { - verify = verifyVerificationTokenMacInBigEndian(verToken, key, scratchPad); + verify = verifyVerificationTokenMacInBigEndian(verToken, scratchPad); } if (!verify) { // Throw Exception if none of the combination works. @@ -2867,7 +2862,7 @@ private boolean authTokenMatches(short userSecureIdsPtr, short authType, return true; } - private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, byte[] scratchPad) { + private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { // The challenge, userId and authenticatorId, authenticatorType and timestamp // are in network order (big-endian). short len = 0; @@ -2902,9 +2897,7 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, byte[] scr 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, @@ -2914,7 +2907,7 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, short key, byte[] scr } - private boolean verifyHwTokenMacInLittleEndian(short hwToken, short key, byte[] scratchPad) { + 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; @@ -2946,9 +2939,7 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, short key, byte[] 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, @@ -2964,11 +2955,10 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { if (KMByteBlob.cast(ptr).length() == 0) { return false; } - short key = repository.getComputedHmacKey(); if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { - return verifyHwTokenMacInLittleEndian(hwToken, key, scratchPad); + return verifyHwTokenMacInLittleEndian(hwToken, scratchPad); } else { - return verifyHwTokenMacInBigEndian(hwToken, key, scratchPad); + return verifyHwTokenMacInBigEndian(hwToken, scratchPad); } } @@ -3461,7 +3451,8 @@ private void processSetBootParamsCmd(APDU apdu) { repository.clearAndroidSystemProperties(); // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - repository.clearComputedHmac(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE, (byte) 0); + seProvider.createComputedHmacKey(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE); repository.clearHmacNonce(); //Clear all the operation state. diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 0d8ede80..e66961b5 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -31,9 +31,12 @@ public class KMRepository implements KMUpgradable { public static final byte DEFAULT_TABLE_TABLE = 0; public static final byte ATTEST_IDS_DATA_TABLE = 1; + public static final short DATA_INDEX_ENTRY_SIZE = 4; // Data table configuration for attestation ids public static final short ATTEST_IDS_DATA_INDEX_SIZE = 8; - public static final short ATTEST_IDS_DATA_TABLE_SIZE = 300; + public static final short ATTEST_IDS_DATA_TABLE_SIZE = + (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE) + + KMConfigurations.TOTAL_ATTEST_IDS_SIZE; public static final byte ATT_ID_BRAND = 0; public static final byte ATT_ID_DEVICE = 1; public static final byte ATT_ID_PRODUCT = 2; @@ -44,9 +47,7 @@ public class KMRepository implements KMUpgradable { public static final byte ATT_ID_MODEL = 7; // Data table configuration other non provisioned parameters. - public static final short DATA_INDEX_SIZE = 21; - public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_MEM_SIZE = 500; + public static final short DATA_INDEX_SIZE = 20; public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -59,27 +60,22 @@ public class KMRepository implements KMUpgradable { private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; // Data table offsets - 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_LEVEL = 3; - public static final byte VENDOR_PATCH_LEVEL = 4; - public static final byte BOOT_PATCH_LEVEL = 5; - public static final byte BOOT_VERIFIED_BOOT_KEY = 6; - public static final byte BOOT_VERIFIED_BOOT_HASH = 7; - public static final byte BOOT_VERIFIED_BOOT_STATE = 8; - public static final byte BOOT_DEVICE_LOCKED_STATUS = 9; - public static final byte DEVICE_LOCKED_TIME = 10; - public static final byte DEVICE_LOCKED = 11; - public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 12; - public static final byte AUTH_TAG_1 = 13; - public static final byte AUTH_TAG_2 = 14; - public static final byte AUTH_TAG_3 = 15; - public static final byte AUTH_TAG_4 = 16; - public static final byte AUTH_TAG_5 = 17; - public static final byte AUTH_TAG_6 = 18; - public static final byte AUTH_TAG_7 = 19; - public static final byte AUTH_TAG_8 = 20; + public static final byte BEGIN_OFFSET = 0; + public static final byte HMAC_NONCE = BEGIN_OFFSET + 0; + public static final byte BOOT_OS_VERSION = BEGIN_OFFSET + 1; + public static final byte BOOT_OS_PATCH_LEVEL = BEGIN_OFFSET + 2; + public static final byte VENDOR_PATCH_LEVEL = BEGIN_OFFSET + 3; + public static final byte BOOT_PATCH_LEVEL = BEGIN_OFFSET + 4; + public static final byte BOOT_VERIFIED_BOOT_KEY = BEGIN_OFFSET + 5; + public static final byte BOOT_VERIFIED_BOOT_HASH = BEGIN_OFFSET + 6; + public static final byte BOOT_VERIFIED_BOOT_STATE = BEGIN_OFFSET + 7; + public static final byte BOOT_DEVICE_LOCKED_STATUS = BEGIN_OFFSET + 8; + public static final byte DEVICE_LOCKED_TIME = BEGIN_OFFSET + 9; + public static final byte DEVICE_LOCKED = BEGIN_OFFSET + 10; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = BEGIN_OFFSET + 11; + // Total 8 Auth Tags start offset 12 and end offset 19. + public static final byte AUTH_TAG_1 = BEGIN_OFFSET + 12; + public static final byte END_OFFSET = 20; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -91,7 +87,9 @@ public class KMRepository implements KMUpgradable { public static final short VENDOR_PATCH_SIZE = 4; public static final short BOOT_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 BOOT_DEVICE_LOCK_FLAG_SIZE = 1; + public static final short DEVICE_LOCKED_FLAG_SIZE = 1; + public static final short DEVICE_LOCKED_PASSWORD_ONLY_SIZE = 1; public static final short BOOT_STATE_SIZE = 1; public static final short MAX_OPS = 4; public static final byte BOOT_KEY_MAX_SIZE = 32; @@ -102,6 +100,20 @@ public class KMRepository implements KMUpgradable { public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; + private static final short DATA_MEM_SIZE = (DATA_INDEX_ENTRY_SIZE * DATA_INDEX_SIZE) + + HMAC_SEED_NONCE_SIZE + + OS_VERSION_SIZE + + OS_PATCH_SIZE + + VENDOR_PATCH_SIZE + + BOOT_PATCH_SIZE + + BOOT_KEY_MAX_SIZE + + BOOT_HASH_MAX_SIZE + + BOOT_STATE_SIZE + + BOOT_DEVICE_LOCK_FLAG_SIZE + + DEVICE_LOCK_TS_SIZE + + DEVICE_LOCKED_FLAG_SIZE + + DEVICE_LOCKED_PASSWORD_ONLY_SIZE + + (8 * AUTH_TAG_ENTRY_SIZE); // Class Attributes private Object[] operationStateTable; @@ -329,13 +341,6 @@ public void releaseAllOperations() { } } - 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); @@ -347,10 +352,6 @@ public void clearHmacNonce() { clearDataEntry(HMAC_NONCE); } - public void clearComputedHmac() { - clearDataEntry(COMPUTED_HMAC_KEY); - } - public void onUninstall() { // Javacard Runtime environment cleans up the data. @@ -552,9 +553,6 @@ public short getHmacNonce() { return readData(HMAC_NONCE); } - public short getComputedHmacKey() { - return readData(COMPUTED_HMAC_KEY); - } public void persistAttId(byte id, byte[] buf, short start, short len) { writeDataEntry(ATTEST_IDS_DATA_TABLE, id, buf, start, len); @@ -744,33 +742,33 @@ public void clearAndroidSystemProperties() { } public void setBootloaderLocked(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); + short start = alloc(BOOT_DEVICE_LOCK_FLAG_SIZE); if (flag) { (getHeap())[start] = (byte) 0x01; } else { (getHeap())[start] = (byte) 0x00; } - writeDataEntry(BOOT_DEVICE_LOCKED_STATUS, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(BOOT_DEVICE_LOCKED_STATUS, getHeap(), start, BOOT_DEVICE_LOCK_FLAG_SIZE); } public void setDeviceLock(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); + short start = alloc(DEVICE_LOCKED_FLAG_SIZE); if (flag) { (getHeap())[start] = (byte) 0x01; } else { (getHeap())[start] = (byte) 0x00; } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCKED_FLAG_SIZE); } public void setDeviceLockPasswordOnly(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); + short start = alloc(DEVICE_LOCKED_PASSWORD_ONLY_SIZE); if (flag) { (getHeap())[start] = (byte) 0x01; } else { (getHeap())[start] = (byte) 0x00; } - writeDataEntry(DEVICE_LOCKED_PASSWORD_ONLY, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(DEVICE_LOCKED_PASSWORD_ONLY, getHeap(), start, DEVICE_LOCKED_PASSWORD_ONLY_SIZE); } public void setDeviceLockTimestamp(byte[] buf, short start, short len) { diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index ef14c001..982fb8d4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -296,9 +296,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 instance of KMComputedHmacKey. * @param data is the buffer containing data. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -308,9 +306,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, @@ -544,6 +540,16 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, */ KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length); + /** + * 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. * @@ -564,6 +570,13 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, * @return Instance of the KMPreSharedKey. */ KMPreSharedKey getPresharedKey(); + + /** + * Returns the computed Hmac key. + * + * @return Instance of the computed hmac key. + */ + KMComputedHmacKey getComputedHmacKey(); /** * Releases all the instance back to pool. Generally this is used when card is reset. From d9994339564fc05806d10959c06bac927146c268 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Tue, 16 Nov 2021 06:42:26 +0000 Subject: [PATCH 17/33] Added KMComputedHmacKey --- .../com/android/javacard/keymaster/KMComputedHmacKey.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java diff --git a/Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java b/Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java new file mode 100644 index 00000000..9621b417 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java @@ -0,0 +1,5 @@ +package com.android.javacard.keymaster; + + +public interface KMComputedHmacKey { +} From ab85ff655afe5d766f639843318c1b25e4b95b72 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Tue, 16 Nov 2021 22:44:16 +0000 Subject: [PATCH 18/33] Added trusted confirmation changes --- .../keymaster/KMAndroidSEProvider.java | 65 +++++++++++++-- .../javacard/keymaster/KMConfigurations.java | 1 + .../javacard/keymaster/KMOperationImpl.java | 20 ++++- .../javacard/keymaster/KMJCardSimulator.java | 21 +++-- .../android/javacard/keymaster/KMError.java | 4 +- .../javacard/keymaster/KMKeyParameters.java | 2 +- .../javacard/keymaster/KMKeymasterApplet.java | 79 ++++++++++++++++--- .../javacard/keymaster/KMOperationState.java | 30 ++++++- .../javacard/keymaster/KMRepository.java | 69 ++++++++++++++-- .../javacard/keymaster/KMSEProvider.java | 8 ++ 10 files changed, 260 insertions(+), 39 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 9ac63267..985d87fb 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -117,6 +117,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; private static final short MAX_OPERATIONS = 4; + private static final short HMAC_MAX_OPERATIONS = 8; private static final short COMPUTED_HMAC_KEY_SIZE = 32; private static final short CERT_CHAIN_OFFSET = 0; @@ -164,7 +165,9 @@ public class KMAndroidSEProvider implements KMSEProvider { private Object[] sigPool; // KMOperationImpl pool private Object[] operationPool; - + // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. + private Object[] hmacSignOperationPool; + private Signature kdf; private Signature hmacSignature; @@ -203,13 +206,18 @@ public KMAndroidSEProvider() { // Re-usable cipher and signature instances cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; - sigPool = new Object[(short) (SIG_ALGS.length * 4)]; + // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. + sigPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; operationPool = new Object[4]; + + //maintain seperate operation pool for hmac signer used to support trusted confirmation + hmacSignOperationPool = new Object[4]; // Creates an instance of each cipher algorithm once. initializeCipherPool(); // Creates an instance of each signature algorithm once. initializeSigPool(); initializeOperationPool(); + initializeHmacSignOperationPool(); //RsaOAEP Decipher rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); @@ -288,6 +296,14 @@ private void initializeOperationPool() { index++; } } + + private void initializeHmacSignOperationPool() { + short index = 0; + while (index < 4) { + hmacSignOperationPool[index] = new KMOperationImpl(); + index++; + } + } // Create a signature instance of each algorithm once. private void initializeSigPool() { @@ -339,6 +355,20 @@ private KMOperationImpl getOperationInstanceFromPool() { } return null; } + + private KMOperationImpl getHmacSignOperationInstanceFromPool() { + short index = 0; + KMOperationImpl impl; + while (index < hmacSignOperationPool.length) { + impl = (KMOperationImpl) hmacSignOperationPool[index]; + // Mode is always set. so compare using mode value. + if (impl.getMode() == KMType.INVALID_VALUE) { + return impl; + } + index++; + } + return null; + } private Signature getSignatureInstanceFromPool(byte alg) { return (Signature) getInstanceFromPool(sigPool, alg); @@ -351,7 +381,8 @@ private Cipher getCipherInstanceFromPool(byte alg) { private boolean isResourceBusy(Object obj) { short index = 0; while (index < MAX_OPERATIONS) { - if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj)) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj) + || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj)) { return true; } index++; @@ -372,8 +403,12 @@ private Object getInstanceFromPool(Object[] pool, byte alg) { short instanceCount = 0; boolean isCipher = isCipherAlgorithm(alg); boolean isSigner = isSignerAlgorithm(alg); + short maxOperations = MAX_OPERATIONS; + if (Signature.ALG_HMAC_SHA_256 == alg) { + maxOperations = HMAC_MAX_OPERATIONS; + } while (index < (short) pool.length) { - if (instanceCount >= MAX_OPERATIONS) { + if (instanceCount >= maxOperations) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); break; } @@ -925,14 +960,18 @@ public Cipher createSymmetricCipher(short alg, short purpose, return symmCipher; } - public Signature createHmacSignerVerifier(short purpose, short digest, + private Signature createHmacSignerVerifier(short purpose, short digest, byte[] secret, short secretStart, short secretLength) { + HMACKey key = createHMACKey(secret, secretStart, secretLength); + return createHmacSignerVerifier(purpose, digest, key); + } + + 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 = getSignatureInstanceFromPool(alg); - HMACKey key = createHMACKey(secret, secretStart, secretLength); hmacSignerVerifier.init(key, (byte) mapPurpose(purpose)); return hmacSignerVerifier; } @@ -972,6 +1011,17 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, return opr; } + @Override + public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { + KMOperationImpl opr = null; + KMHmacKey key = (KMHmacKey) computedHmacKey; + Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); + opr = getHmacSignOperationInstanceFromPool(); + opr.setMode(KMType.VERIFY); + opr.setSignature(signerVerifier); + return opr; + } + public Signature createRsaSigner(short digest, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { @@ -1191,6 +1241,7 @@ public short getBackupPrimitiveByteCount() { short count = (short) (KMAESKey.getBackupPrimitiveByteCount() + KMECPrivateKey.getBackupPrimitiveByteCount() + + KMHmacKey.getBackupPrimitiveByteCount() + KMHmacKey.getBackupPrimitiveByteCount()); return count; } @@ -1201,6 +1252,7 @@ public short getBackupObjectCount() { (short) (1 + /* provisionData buffer */ KMAESKey.getBackupObjectCount() + KMECPrivateKey.getBackupObjectCount() + + KMHmacKey.getBackupObjectCount() + KMHmacKey.getBackupObjectCount()); return count; } @@ -1289,6 +1341,7 @@ public void releaseAllOperations() { short index = 0; while (index < operationPool.length) { ((KMOperationImpl) operationPool[index]).abort(); + ((KMOperationImpl) hmacSignOperationPool[index]).abort(); index++; } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java index fd122b16..6e5090a1 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -25,4 +25,5 @@ public class KMConfigurations { public static final short CERT_CHAIN_MAX_SIZE = 2500; public static final short CERT_ISSUER_MAX_SIZE = 250; public static final short CERT_EXPIRY_MAX_SIZE = 20; + public static final short TOTAL_ATTEST_IDS_SIZE = 300; } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 3fd3cbe2..8bed402d 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -92,7 +92,6 @@ public boolean isResourceMatches(Object object) { return operationInst[0] == object; } - private void reset() { operationInst[0] = null; parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; @@ -234,6 +233,25 @@ 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[OPER_MODE_OFFSET] == KMType.SIGN || parameters[OPER_MODE_OFFSET] == KMType.VERIFY) && + (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { + byte[] empty = {}; + Signature signer = (Signature) operationInst[0]; + try { + if (parameters[OPER_MODE_OFFSET] == KMType.SIGN) { + signer.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/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index d870f82a..e0d2b546 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -613,6 +613,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[] privKeyBuf, short privKeyStart, short privKeyLength, @@ -994,17 +1002,18 @@ private KMCipher createAesCtrCipherNoPad(short mode, byte[] secret, short secret return ret; } + private Signature createHmacSignerVerifier(short purpose, short digest, + byte[] secret, short secretStart, short secretLength) { + HMACKey key = createHMACKey(secret, secretStart, secretLength); + return createHmacSignerVerifier(purpose, digest, key); + } - public Signature createHmacSignerVerifier(short purpose, short digest, byte[] secret, - short secretStart, short secretLength) { - short alg = Signature.ALG_HMAC_SHA_256; + 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); - HMACKey key = (HMACKey) KeyBuilder - .buildKey(KeyBuilder.TYPE_HMAC, (short) (secretLength * 8), false); - key.setKey(secret, secretStart, secretLength); hmacSignerVerifier.init(key, (byte) purpose); return hmacSignerVerifier; } diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index c79fd4b8..e2c74dcb 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -68,8 +68,8 @@ public class KMError { public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; public static final short CANNOT_ATTEST_IDS = 66; - public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; - + 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 UNIMPLEMENTED = 100; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index fd3f1143..3592e32b 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -113,7 +113,6 @@ 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 @@ -171,6 +170,7 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED }; byte index = 0; short tagInd; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 41bf66f6..e15ec737 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -188,8 +188,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static short[] tmpVariables; protected static short[] data; protected static byte provisionStatus = NOT_PROVISIONED; - protected static boolean isBootEnded = true; - protected static boolean isEarlyBootEnded = true; /** @@ -402,7 +400,8 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } //set the flag to mark boot ended - isBootEnded = true; + repository.setBootEndedStatus(true); + seProvider.clearDeviceBooted(false); sendError(apdu, KMError.OK); return; @@ -537,7 +536,7 @@ private void freeOperations() { } private void processEarlyBootEndedCmd(APDU apdu) { - isEarlyBootEnded = true; + repository.setEarlyBootEndedStatus(true); } private void processDeviceLockedCmd(APDU apdu) { @@ -1692,6 +1691,7 @@ private void processFinishOperationCmd(APDU apdu) { authorizeUpdateFinishOperation(op, scratchPad); switch (op.getPurpose()) { case KMType.SIGN: + finishTrustedConfirmationOperation(op); case KMType.VERIFY: finishSigningVerifyingOperation(op, scratchPad); break; @@ -2169,6 +2169,9 @@ private void processUpdateOperationCmd(APDU apdu) { 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 @@ -2292,6 +2295,7 @@ private void processBeginOperationCmd(APDU apdu) { authorizeAndBeginOperation(op, scratchPad); switch (op.getPurpose()) { case KMType.SIGN: + beginTrustedConfirmationOperation(op); case KMType.VERIFY: beginSignVerifyOperation(op); break; @@ -2567,14 +2571,14 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) //Validate bootloader only tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && isBootEnded) { + if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getBootEndedStatus()) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } //Validate early boot tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && isEarlyBootEnded) { + if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } @@ -2688,6 +2692,21 @@ 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()) { @@ -2990,12 +3009,13 @@ private void processImportKeyCmd(APDU apdu) { } private void importKey(APDU apdu, byte[] scratchPad) { - // Bootloader only not supported + tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { + KMException.throwIt(KMError.EARLY_BOOT_ENDED); } + // Rollback protection not supported tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); @@ -3463,10 +3483,10 @@ private void processSetBootParamsCmd(APDU apdu) { repository.initHmacNonce(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); //flag to maintain the boot state - isBootEnded = false; + repository.setBootEndedStatus(false); //flag to maintain early boot ended state - isEarlyBootEnded = false; + repository.setEarlyBootEndedStatus(false); // Clear all the auth tags repository.removeAllAuthTags(); @@ -3491,7 +3511,7 @@ private static void processGenerateKey(APDU apdu) { // Check if EarlyBootEnded tag is present. tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && isEarlyBootEnded) { + if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { KMException.throwIt(KMError.EARLY_BOOT_ENDED); } // Check if rollback resistance tag is present @@ -4115,4 +4135,37 @@ private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) { KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); return KMInteger.uint_64(scratchPad, (short) 16); } + + 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()) { + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); + 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(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).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 85a346e6..7d2a7f4d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -31,6 +31,7 @@ public class KMOperationState { public static final byte MAX_DATA = 63; private static final byte OPERATION = 0; + private static final byte HMAC_SIGNER_OPERATION = 1; private static final byte TRUE = 1; private static final byte FALSE = 0; // byte type @@ -66,7 +67,7 @@ public class KMOperationState { private KMOperationState() { data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); - objRefs = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + objRefs = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); isDataUpdated = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); } @@ -84,11 +85,12 @@ public static KMOperationState instance(short opHandle) { return opState; } - public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr) { + public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr, Object hmacSignerOpr) { KMOperationState opState = proto(); opState.reset(); Util.arrayCopy(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); prototype.objRefs[OPERATION] = opr; + prototype.objRefs[HMAC_SIGNER_OPERATION] = hmacSignerOpr; Util.setShort(prototype.data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); return opState; } @@ -99,7 +101,8 @@ public void persist() { } KMRepository.instance().persistOperation(data, Util.getShort(data, OP_HANDLE), - (KMOperation) objRefs[OPERATION]); + (KMOperation) objRefs[OPERATION], + (KMOperation) objRefs[HMAC_SIGNER_OPERATION]); isDataUpdated[0] = FALSE; } @@ -114,6 +117,7 @@ public short getKeySize() { public void reset() { isDataUpdated[0] = FALSE; objRefs[OPERATION] = null; + objRefs[HMAC_SIGNER_OPERATION] = null; Util.arrayFillNonAtomic( data, (short) 0, (short) data.length, (byte) 0); } @@ -123,8 +127,12 @@ private void dataUpdated() { } public void release() { - if (objRefs[OPERATION] != null) + if (objRefs[OPERATION] != null) { ((KMOperation) objRefs[OPERATION]).abort(); + } + if (objRefs[HMAC_SIGNER_OPERATION] != null) { + ((KMOperation) objRefs[HMAC_SIGNER_OPERATION]).abort(); + } reset(); } @@ -309,4 +317,18 @@ public void setMacLength(short length) { public short getMacLength() { return Util.getShort(data, MAC_LENGTH); } + + public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) { + objRefs[HMAC_SIGNER_OPERATION] = hmacSignerOp; + dataUpdated(); + } + + public KMOperation getTrustedConfirmationSigner() { + return (KMOperation)objRefs[HMAC_SIGNER_OPERATION]; + } + + public boolean isTrustedConfirmationRequired() { + return objRefs[HMAC_SIGNER_OPERATION] != null; + } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index e66961b5..d8646130 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -47,7 +47,7 @@ public class KMRepository implements KMUpgradable { public static final byte ATT_ID_MODEL = 7; // Data table configuration other non provisioned parameters. - public static final short DATA_INDEX_SIZE = 20; + public static final short DATA_INDEX_SIZE = 22; public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -75,7 +75,9 @@ public class KMRepository implements KMUpgradable { public static final byte DEVICE_LOCKED_PASSWORD_ONLY = BEGIN_OFFSET + 11; // Total 8 Auth Tags start offset 12 and end offset 19. public static final byte AUTH_TAG_1 = BEGIN_OFFSET + 12; - public static final byte END_OFFSET = 20; + public static final byte BOOT_ENDED_STATUS = BEGIN_OFFSET + 20; + public static final byte EARLY_BOOT_ENDED_STATUS = BEGIN_OFFSET + 21; + public static final byte END_OFFSET = 22; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -98,6 +100,8 @@ public class KMRepository implements KMUpgradable { 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); + public static final short BOOT_ENDED_FLAG_SIZE = 1; + public static final short EARLY_BOOT_ENDED_FLAG_SIZE = 1; private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; private static final short DATA_MEM_SIZE = (DATA_INDEX_ENTRY_SIZE * DATA_INDEX_SIZE) @@ -113,7 +117,9 @@ public class KMRepository implements KMUpgradable { + DEVICE_LOCK_TS_SIZE + DEVICE_LOCKED_FLAG_SIZE + DEVICE_LOCKED_PASSWORD_ONLY_SIZE - + (8 * AUTH_TAG_ENTRY_SIZE); + + (8 * AUTH_TAG_ENTRY_SIZE) + + BOOT_ENDED_FLAG_SIZE + + EARLY_BOOT_ENDED_FLAG_SIZE; // Class Attributes private Object[] operationStateTable; @@ -132,6 +138,7 @@ public class KMRepository implements KMUpgradable { // Operation table. private static final short OPER_TABLE_DATA_OFFSET = 0; private static final short OPER_TABLE_OPR_OFFSET = 1; + private static final short OPER_TABLE_HMAC_SIGNER_OPR_OFFSET = 2; private static final short OPER_DATA_LEN = OPERATION_HANDLE_ENTRY_SIZE + KMOperationState.MAX_DATA; private static final short DATA_ARRAY_LENGTH = MAX_OPS * OPER_DATA_LEN; @@ -153,9 +160,10 @@ public KMRepository(boolean isUpgrading) { powerResetStatus[0] = POWER_RESET_STATUS_FLAG; newDataTable(isUpgrading); - operationStateTable = new Object[2]; + operationStateTable = new Object[3]; operationStateTable[0] = JCSystem.makeTransientByteArray(DATA_ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); operationStateTable[1] = JCSystem.makeTransientObjectArray(MAX_OPS, JCSystem.CLEAR_ON_RESET); + operationStateTable[2] = JCSystem.makeTransientObjectArray(MAX_OPS, JCSystem.CLEAR_ON_RESET); //Initialize the device locked status if (!isUpgrading) { @@ -203,12 +211,13 @@ public KMOperationState findOperation(byte[] buf, short off, short len) { short offset = 0; oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; while (index < MAX_OPS) { offset = (short) (index * OPER_DATA_LEN); if (0 == Util.arrayCompare(buf, off, oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), len)) { return KMOperationState.read(oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - operations[index]); + operations[index], hmacSignerOprs[index]); } index++; } @@ -245,10 +254,11 @@ public KMOperationState reserveOperation(short opHandle) { return null; } - public void persistOperation(byte[] data, short opHandle, KMOperation op) { + public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOperation hmacSignerOp) { short index = 0; byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; short offset = 0; short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); getOperationHandle( @@ -269,6 +279,7 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op) { Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), KMOperationState.MAX_DATA); operations[index] = op; + hmacSignerOprs[index] = hmacSignerOp; return; } index++; @@ -289,6 +300,7 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op) { Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), KMOperationState.MAX_DATA); operations[index] = op; + hmacSignerOprs[index] = hmacSignerOp; break; } index++; @@ -299,6 +311,7 @@ public void releaseOperation(KMOperationState op) { short index = 0; byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; short offset = 0; short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); getOperationHandle( @@ -317,6 +330,7 @@ public void releaseOperation(KMOperationState op) { Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); op.release(); operations[index] = null; + hmacSignerOprs[index] = null; break; } index++; @@ -327,6 +341,8 @@ public void releaseAllOperations() { short index = 0; byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; + Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; + short offset = 0; while (index < MAX_OPS) { offset = (short) (index * OPER_DATA_LEN); @@ -336,6 +352,10 @@ public void releaseAllOperations() { ((KMOperation) operations[index]).abort(); operations[index] = null; } + if (hmacSignerOprs[index] != null) { + ((KMOperation) hmacSignerOprs[index]).abort(); + hmacSignerOprs[index] = null; + } } index++; } @@ -947,4 +967,41 @@ public short getBackupObjectCount() { // dataTable return (short) 2; } + + public boolean getBootEndedStatus() { + short blob = readData(BOOT_ENDED_STATUS); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; + } + + public void setBootEndedStatus(boolean flag) { + short start = alloc(BOOT_ENDED_STATUS); + if (flag) { + (getHeap())[start] = (byte) 0x01; + } else { + (getHeap())[start] = (byte) 0x00; + } + writeDataEntry(BOOT_ENDED_STATUS, getHeap(), start, BOOT_ENDED_FLAG_SIZE); + } + + public boolean getEarlyBootEndedStatus() { + short blob = readData(EARLY_BOOT_ENDED_STATUS); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; + } + + public void setEarlyBootEndedStatus(boolean flag) { + short start = alloc(EARLY_BOOT_ENDED_STATUS); + if (flag) { + (getHeap())[start] = (byte) 0x01; + } else { + (getHeap())[start] = (byte) 0x00; + } + writeDataEntry(EARLY_BOOT_ENDED_STATUS, getHeap(), start, EARLY_BOOT_ENDED_FLAG_SIZE); + } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 982fb8d4..da57e3ee 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -401,6 +401,14 @@ KMOperation initSymmetricOperation( short ivLength, short macLength); + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * This creates a persistent operation for signing, verify, encryption and decryption using RSA * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public From c48ba442433c963915dcc4a6a81b10661f6c923d Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Wed, 17 Nov 2021 18:06:13 +0000 Subject: [PATCH 19/33] Hmac signer reset --- .../src/com/android/javacard/keymaster/KMOperationImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 8bed402d..81641ee7 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -243,9 +243,9 @@ public void abort() { Signature signer = (Signature) operationInst[0]; try { if (parameters[OPER_MODE_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); + } else { + signer.sign(empty, (short) 0, (short) 0, empty, (short) 0); } } catch(Exception e) { // Ignore. From 120c3a6f035eef03df4657253715eeff6ae69019 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Thu, 18 Nov 2021 06:23:31 +0000 Subject: [PATCH 20/33] Added VTS and CTS patches --- .../cts_tests_tests_keystore.patch | 19 ++ .../hardware_interfaces_keymaster.patch | 263 ++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 aosp_integration_patches/hardware_interfaces_keymaster.patch diff --git a/aosp_integration_patches/cts_tests_tests_keystore.patch b/aosp_integration_patches/cts_tests_tests_keystore.patch index 62888a5b..85ffb05c 100644 --- a/aosp_integration_patches/cts_tests_tests_keystore.patch +++ b/aosp_integration_patches/cts_tests_tests_keystore.patch @@ -1,3 +1,22 @@ +diff --git a/tests/tests/keystore/src/android/keystore/cts/AttestKeyTest.java b/tests/tests/keystore/src/android/keystore/cts/AttestKeyTest.java +index 0f064b645fd..452c034fcb0 100644 +--- a/tests/tests/keystore/src/android/keystore/cts/AttestKeyTest.java ++++ b/tests/tests/keystore/src/android/keystore/cts/AttestKeyTest.java +@@ -144,7 +144,13 @@ public class AttestKeyTest { + @Test + public void testAttestKeySecurityLevelMismatch() throws Exception { + TestUtils.assumeStrongBox(); +- ++ int keyStoreFeatureVersionStrongBox = ++ TestUtils.getFeatureVersionKeystoreStrongBox(InstrumentationRegistry.getInstrumentation().getTargetContext()); ++ if(Attestation.KM_VERSION_KEYMASTER_4 == keyStoreFeatureVersionStrongBox ++ || Attestation.KM_VERSION_KEYMASTER_4_1 == keyStoreFeatureVersionStrongBox) { ++ return; ++ } ++ + final String strongBoxAttestKeyAlias = "nonAttestKey"; + final String attestedKeyAlias = "attestedKey"; + generateKeyPair(KEY_ALGORITHM_EC, diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java index ccbadf98a31..eca7b6c2abe 100644 --- a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java diff --git a/aosp_integration_patches/hardware_interfaces_keymaster.patch b/aosp_integration_patches/hardware_interfaces_keymaster.patch new file mode 100644 index 00000000..f18f6ed7 --- /dev/null +++ b/aosp_integration_patches/hardware_interfaces_keymaster.patch @@ -0,0 +1,263 @@ +diff --git a/keymaster/4.0/vts/functional/Android.bp b/keymaster/4.0/vts/functional/Android.bp +index a7be660c4..dd91e9089 100644 +--- a/keymaster/4.0/vts/functional/Android.bp ++++ b/keymaster/4.0/vts/functional/Android.bp +@@ -31,9 +31,11 @@ cc_test { + "VerificationTokenTest.cpp", + "keymaster_hidl_hal_test.cpp", + ], ++ shared_libs: [ ++ "libcrypto", ++ ], + static_libs: [ + "android.hardware.keymaster@4.0", +- "libcrypto_static", + "libkeymaster4support", + "libkeymaster4vtstest", + ], +diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp +index 476eed8b1..3b2cdb641 100644 +--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp ++++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp +@@ -29,6 +29,9 @@ + + #include + ++#include ++#include ++ + #include + #include + #include +@@ -1079,20 +1082,229 @@ TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) { + * presented. + */ + TEST_P(SigningOperationsTest, NoUserConfirmation) { +- if (SecLevel() == SecurityLevel::STRONGBOX) return; + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() +- .RsaSigningKey(1024, 65537) ++ .RsaSigningKey(2048, 65537) ++ .Digest(Digest::NONE) ++ .Padding(PaddingMode::NONE) ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))); ++ ++ const string message = "12345678901234567890123456789012"; ++ EXPECT_EQ(ErrorCode::OK, ++ Begin(KeyPurpose::SIGN, ++ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); ++ string signature; ++ EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature)); ++} ++ ++/* ++ * SigningOperationsTest.TrustedConfirmationRequired ++ * ++ * Verifies that keymaster performs signing operations for keys with ++ * TRUSTED_CONFIRMATION_REQUIRED and a valid confirmation token ++ * presented. ++ */ ++TEST_P(SigningOperationsTest, TrustedConfirmationRequired) { ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .RsaSigningKey(2048, 65537) ++ .Digest(Digest::NONE) ++ .Padding(PaddingMode::NONE) ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))); ++ ++ const string conf_token = "confirmation token"; ++ const string message = "12345678901234567890123456789012"; ++ string signature; ++ ++ std::vector input; ++ input.insert(input.end(), conf_token.begin(), conf_token.end()); ++ input.insert(input.end(), message.begin(), message.end()); ++ ++ std::vector macKey(32, 0); ++ std::array hash; ++ unsigned int hashLen = 0; ++ ++ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); ++ EXPECT_TRUE(result != nullptr); ++ ++ std::vector confToken(hash.begin(), hash.end()); ++ ++ EXPECT_EQ(ErrorCode::OK, ++ Begin(KeyPurpose::SIGN, ++ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); ++ ++ AuthorizationSet out_params; ++ string output; ++ string retText; ++ ++ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), message, signature, ++ &out_params, &output)); ++ CheckedDeleteKey(); ++} ++ ++/* ++ * SigningOperationsTest.NoUserConfirmation2_FailureSuccess ++ * ++ * Verifies that keymaster validates failure and success signing operations for key with ++ * TRUSTED_CONFIRMATION_REQUIRED and a valid confirmation token ++ * presented. ++ */ ++ ++TEST_P(SigningOperationsTest, NoUserConfirmation2_FailureSuccess) { ++ ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))); + + const string message = "12345678901234567890123456789012"; ++ const string conf_token = "confirmation token"; ++ + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::SIGN, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); + string signature; + EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature)); ++ ++ std::vector input; ++ input.insert(input.end(), conf_token.begin(), conf_token.end()); ++ input.insert(input.end(), message.begin(), message.end()); ++ ++ std::vector macKey(32, 0); ++ std::array hash; ++ unsigned int hashLen = 0; ++ ++ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); ++ EXPECT_TRUE(result != nullptr); ++ std::vector confToken(hash.begin(), hash.end()); ++ ++ EXPECT_EQ(ErrorCode::OK, ++ Begin(KeyPurpose::SIGN, ++ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); ++ ++ AuthorizationSet out_params; ++ string output; ++ string retText; ++ ++ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), message, signature, ++ &out_params, &output)); ++ CheckedDeleteKey(); ++} ++ ++/* ++ * SigningOperationsTest.TrustedConfirmationRequired_HMAC ++ * ++ * Verifies that keymaster rejects signing operations for keys with ++ * TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token ++ * presented. ++ */ ++ ++TEST_P(SigningOperationsTest, TrustedConfirmationRequired_HMAC) { ++ ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .HmacKey(128) ++ .Digest(Digest::SHA_2_256) ++ .Padding(PaddingMode::NONE) ++ .Authorization(TAG_MIN_MAC_LENGTH, 160) ++ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) ++ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))) ++ << "Failed to create HMAC key with digest " ; ++ ++ const string conf_token = "confirmation token"; ++ const string message = "12345678901234567890123456789012"; ++ string signature; ++ ++ std::vector input; ++ input.insert(input.end(), conf_token.begin(), conf_token.end()); ++ input.insert(input.end(), message.begin(), message.end()); ++ ++ std::vector macKey(32, 0); ++ std::array hash; ++ unsigned int hashLen = 0; ++ ++ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); ++ EXPECT_TRUE(result != nullptr); ++ std::vector confToken(hash.begin(), hash.end()); ++ ++ EXPECT_EQ(ErrorCode::OK, ++ Begin(KeyPurpose::SIGN, ++ AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160))); ++ ++ AuthorizationSet out_params; ++ string output; ++ string retText; ++ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), message, signature, ++ &out_params, &output)); ++ ++ CheckedDeleteKey(); ++ ++} ++ ++TEST_P(SigningOperationsTest, TrustedConfirmationRequired_TomanyOperations) { ++ ++ ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .HmacKey(128) ++ .Digest(Digest::SHA_2_256) ++ .Padding(PaddingMode::NONE) ++ .Authorization(TAG_MIN_MAC_LENGTH, 160) ++ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) ++ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))) ++ << "Failed to create HMAC key with digest " ; ++ ++ const string conf_token = "confirmation token"; ++ const string message = "12345678901234567890123456789012"; ++ string signature; ++ ++ std::vector input; ++ input.insert(input.end(), conf_token.begin(), conf_token.end()); ++ input.insert(input.end(), message.begin(), message.end()); ++ ++ std::vector macKey(32, 0); ++ std::array hash; ++ unsigned int hashLen = 0; ++ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); ++ EXPECT_TRUE(result != nullptr); ++ std::vector confToken(hash.begin(), hash.end()); ++ AuthorizationSet out_params; ++ string output; ++ string retText; ++ int max_operations = SecLevel() == SecurityLevel::STRONGBOX ? 4 : 16; ++ OperationHandle op_handles[max_operations]; ++ ++ ++ for(int i=0; i< max_operations; i++) { ++ EXPECT_EQ(ErrorCode::OK, ++ Begin(KeyPurpose::SIGN, key_blob_ ++ , AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160) ++ , &out_params ++ , &(op_handles[i]))); ++ } ++ ++ EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, ++ Begin(KeyPurpose::SIGN, key_blob_ ++ , AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160) ++ , &out_params, &op_handle_)); ++ ++ for(int i=0; i< max_operations; i++) { ++ EXPECT_EQ(ErrorCode::OK, Finish(op_handles[i], AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), ++ message, signature, ++ &out_params, &output)); ++ } ++ ++ EXPECT_EQ(ErrorCode::OK, ++ Begin(KeyPurpose::SIGN, key_blob_ ++ , AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160) ++ , &out_params ++ , &op_handle_)); ++ ++ ++ CheckedDeleteKey(); ++ + } + + /* From 10682f62d7ac82ad5e62e21a383c753835d708da Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Thu, 18 Nov 2021 15:44:56 +0000 Subject: [PATCH 21/33] Updated the patches --- .../{build-make.patch => build_make.patch} | 0 aosp_integration_patches/cuttlefish.patch | 45 -------------- .../device_google_cuttlefish.patch | 58 ++++++++++++++++++ aosp_integration_patches/sepolicy.patch | 61 ------------------- .../system_sepolicy.patch | 28 +++++++++ 5 files changed, 86 insertions(+), 106 deletions(-) rename aosp_integration_patches/{build-make.patch => build_make.patch} (100%) delete mode 100644 aosp_integration_patches/cuttlefish.patch create mode 100644 aosp_integration_patches/device_google_cuttlefish.patch delete mode 100644 aosp_integration_patches/sepolicy.patch create mode 100644 aosp_integration_patches/system_sepolicy.patch diff --git a/aosp_integration_patches/build-make.patch b/aosp_integration_patches/build_make.patch similarity index 100% rename from aosp_integration_patches/build-make.patch rename to aosp_integration_patches/build_make.patch diff --git a/aosp_integration_patches/cuttlefish.patch b/aosp_integration_patches/cuttlefish.patch deleted file mode 100644 index 4007d0ad..00000000 --- a/aosp_integration_patches/cuttlefish.patch +++ /dev/null @@ -1,45 +0,0 @@ -commit a205fd77b33093e31a0e1b8d8e9fed453e6dea20 -Author: Manish Dwivedi -Date: Tue Jun 29 01:39:31 2021 +0000 - - aosp upmerge - - Change-Id: I6e5ef5ffcfa0d34c5a5bf65cbc73604270a3222c - -diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk -index aec8869f2..a8185aacd 100644 ---- a/shared/BoardConfig.mk -+++ b/shared/BoardConfig.mk -@@ -207,6 +207,8 @@ BOARD_RAMDISK_USE_LZ4 := true - # To see full logs from init, disable ratelimiting. - # The default is 5 messages per second amortized, with a burst of up to 10. - BOARD_KERNEL_CMDLINE += printk.devkmsg=on -+BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive -+ - BOARD_KERNEL_CMDLINE += firmware_class.path=/vendor/etc/ - - BOARD_KERNEL_CMDLINE += init=/init -diff --git a/shared/device.mk b/shared/device.mk -index 290b61f97..6ccae3bae 100644 ---- a/shared/device.mk -+++ b/shared/device.mk -@@ -502,6 +502,11 @@ endif - PRODUCT_PACKAGES += \ - $(LOCAL_KEYMINT_PRODUCT_PACKAGE) - -+PRODUCT_PACKAGES += \ -+ android.hardware.keymaster@4.1-strongbox.service \ -+ -+ -+ - # Keymint configuration - PRODUCT_COPY_FILES += \ - frameworks/native/data/etc/android.software.device_id_attestation.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.device_id_attestation.xml -@@ -611,6 +616,7 @@ PRODUCT_PACKAGES += setup_wifi - PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=virt_wifi - endif - -+ - # Host packages to install - PRODUCT_HOST_PACKAGES += socket_vsock_proxy - diff --git a/aosp_integration_patches/device_google_cuttlefish.patch b/aosp_integration_patches/device_google_cuttlefish.patch new file mode 100644 index 00000000..750eb6ff --- /dev/null +++ b/aosp_integration_patches/device_google_cuttlefish.patch @@ -0,0 +1,58 @@ +diff --git a/shared/device.mk b/shared/device.mk +index c0b6112c7..6d9362ea4 100644 +--- a/shared/device.mk ++++ b/shared/device.mk +@@ -576,6 +576,9 @@ endif + PRODUCT_PACKAGES += \ + $(LOCAL_KEYMINT_PRODUCT_PACKAGE) + ++PRODUCT_PACKAGES += \ ++ android.hardware.keymaster@4.1-strongbox.service \ ++ + # Keymint configuration + ifneq ($(LOCAL_PREFER_VENDOR_APEX),true) + PRODUCT_COPY_FILES += \ +diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts +index 55b8d964e..80732eb7b 100644 +--- a/shared/sepolicy/vendor/file_contexts ++++ b/shared/sepolicy/vendor/file_contexts +@@ -86,6 +86,7 @@ + /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 + /vendor/bin/hw/android\.hardware\.keymaster@4\.1-service.remote u:object_r:hal_keymaster_remote_exec:s0 ++/vendor/bin/hw/android\.hardware\.keymaster@4\.1-strongbox\.service u:object_r:hal_keymaster_strongbox_exec:s0 + /vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service.remote u:object_r:hal_gatekeeper_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.confirmationui@1\.0-service.cuttlefish u:object_r:hal_confirmationui_cuttlefish_exec:s0 + /vendor/bin/hw/android\.hardware\.oemlock-service.example u:object_r:hal_oemlock_default_exec:s0 +diff --git a/shared/sepolicy/vendor/hal_keymaster_strongbox.te b/shared/sepolicy/vendor/hal_keymaster_strongbox.te +new file mode 100644 +index 000000000..40cb82c3f +--- /dev/null ++++ b/shared/sepolicy/vendor/hal_keymaster_strongbox.te +@@ -0,0 +1,14 @@ ++type hal_keymaster_strongbox, domain; ++hal_server_domain(hal_keymaster_strongbox, hal_keymaster) ++ ++type hal_keymaster_strongbox_exec, exec_type, vendor_file_type, file_type; ++init_daemon_domain(hal_keymaster_strongbox) ++ ++vndbinder_use(hal_keymaster_strongbox) ++get_prop(hal_keymaster_strongbox, vendor_security_patch_level_prop); ++ ++# Allow access to sockets ++allow hal_keymaster_strongbox self:tcp_socket { connect create write read getattr getopt setopt }; ++allow hal_keymaster_strongbox port_type:tcp_socket name_connect; ++allow hal_keymaster_strongbox port:tcp_socket { name_connect }; ++allow hal_keymaster_strongbox vendor_data_file:file { open read getattr }; +diff --git a/shared/sepolicy/vendor/service_contexts b/shared/sepolicy/vendor/service_contexts +index d20d026cf..214576e3e 100644 +--- a/shared/sepolicy/vendor/service_contexts ++++ b/shared/sepolicy/vendor/service_contexts +@@ -4,6 +4,7 @@ android.hardware.neuralnetworks.IDevice/nnapi-sample_float_slow u:object_r:hal_n + android.hardware.neuralnetworks.IDevice/nnapi-sample_minimal u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_quant u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_shim u:object_r:hal_neuralnetworks_service:s0 ++android.hardware.keymaster@4.1::IKeymasterDevice/strongbox u:object_r:hal_keymaster_service:s0 + + # Binder service mappings + gce u:object_r:gce_service:s0 diff --git a/aosp_integration_patches/sepolicy.patch b/aosp_integration_patches/sepolicy.patch deleted file mode 100644 index f0f1a676..00000000 --- a/aosp_integration_patches/sepolicy.patch +++ /dev/null @@ -1,61 +0,0 @@ -commit ad5c9bf6cc9c806717ced142cc44b19b0b46d334 -Author: Manish Dwivedi -Date: Tue Jun 29 01:36:44 2021 +0000 - - aosp upmerge - - Change-Id: I99cde570325f6d56103fbbf318a5afdf0cbd1831 - -diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te -index faec07420..020ee16bd 100644 ---- a/public/hal_neverallows.te -+++ b/public/hal_neverallows.te -@@ -2,6 +2,7 @@ - # network capabilities - neverallow { - halserverdomain -+ -hal_keymaster_server - -hal_bluetooth_server - -hal_can_controller_server - -hal_wifi_server -@@ -19,6 +20,7 @@ neverallow { - # will result in CTS failure. - neverallow { - halserverdomain -+ -hal_keymaster_server - -hal_automotive_socket_exemption - -hal_can_controller_server - -hal_tetheroffload_server -@@ -31,6 +33,7 @@ neverallow { - - neverallow { - halserverdomain -+ -hal_keymaster_server - -hal_automotive_socket_exemption - -hal_can_controller_server - -hal_tetheroffload_server -diff --git a/vendor/file_contexts b/vendor/file_contexts -index 12e5d9f97..a6d7bd72f 100644 ---- a/vendor/file_contexts -+++ b/vendor/file_contexts -@@ -71,6 +71,7 @@ - /(vendor|system/vendor)/bin/hw/android\.hardware\.sensors@[0-9]\.[0-9]-service(\.multihal)? u:object_r:hal_sensors_default_exec:s0 - /(vendor|system/vendor)/bin/hw/android\.hardware\.secure_element@1\.0-service u:object_r:hal_secure_element_default_exec:s0 - /(vendor|system/vendor)/bin/hw/android\.hardware\.security\.keymint-service u:object_r:hal_keymint_default_exec:s0 -+/(vendor|system/vendor)/bin/hw/android\.hardware\.keymaster@4\.1-strongbox\.service u:object_r:hal_keymaster_default_exec:s0 - /(vendor|system/vendor)/bin/hw/rild u:object_r:rild_exec:s0 - /(vendor|system/vendor)/bin/hw/android\.hardware\.thermal@1\.[01]-service u:object_r:hal_thermal_default_exec:s0 - /(vendor|system/vendor)/bin/hw/android\.hardware\.tv\.cec@1\.[01]-service u:object_r:hal_tv_cec_default_exec:s0 -diff --git a/vendor/hal_keymaster_default.te b/vendor/hal_keymaster_default.te -index 6f0d82aa7..2bbcff6a7 100644 ---- a/vendor/hal_keymaster_default.te -+++ b/vendor/hal_keymaster_default.te -@@ -5,3 +5,8 @@ type hal_keymaster_default_exec, exec_type, vendor_file_type, file_type; - init_daemon_domain(hal_keymaster_default) - - get_prop(hal_keymaster_default, vendor_security_patch_level_prop); -+allow hal_keymaster_default self:tcp_socket { connect create write read getattr getopt setopt }; -+allow hal_keymaster_default port_type:tcp_socket name_connect; -+allow hal_keymaster_default port:tcp_socket { name_connect }; -+allow hal_keymaster_default vendor_data_file:file { open read getattr }; -+ diff --git a/aosp_integration_patches/system_sepolicy.patch b/aosp_integration_patches/system_sepolicy.patch new file mode 100644 index 00000000..c2dd4c27 --- /dev/null +++ b/aosp_integration_patches/system_sepolicy.patch @@ -0,0 +1,28 @@ +diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te +index cd1591009..56f3ad1c4 100644 +--- a/public/hal_neverallows.te ++++ b/public/hal_neverallows.te +@@ -2,6 +2,7 @@ + # network capabilities + neverallow { + halserverdomain ++ -hal_keymaster_server + -hal_bluetooth_server + -hal_can_controller_server + -hal_wifi_server +@@ -21,6 +22,7 @@ neverallow { + # will result in CTS failure. + neverallow { + halserverdomain ++ -hal_keymaster_server + -hal_automotive_socket_exemption + -hal_can_controller_server + -hal_tetheroffload_server +@@ -35,6 +37,7 @@ neverallow { + + neverallow { + halserverdomain ++ -hal_keymaster_server + -hal_automotive_socket_exemption + -hal_can_controller_server + -hal_tetheroffload_server From a0f60237eccafdb36c01ad4048f4180e15b0316b Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Fri, 19 Nov 2021 22:31:55 +0000 Subject: [PATCH 22/33] Cache earlybootEnded event in HAL and send in getHmacSharedParameters --- .../javacard/keymaster/KMKeymasterApplet.java | 14 +++- .../4.1/JavacardKeymaster4Device.cpp | 76 +++++++------------ .../include/JavacardKeymaster4Device.h | 42 +++++++++- 3 files changed, 78 insertions(+), 54 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e15ec737..a57c3bf4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -974,7 +974,19 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { } private void processGetHmacSharingParamCmd(APDU apdu) { - // No Arguments + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Arguments + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); + // Decode the argument + short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); + //reclaim memory + repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); + if ((short) 1 == KMInteger.cast(KMArray.cast(args).get((short) 0)).getShort()) { + repository.setEarlyBootEndedStatus(true); + } + // Create HMAC Sharing Parameters tmpVariables[2] = KMHmacSharingParameters.instance(); KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(repository.getHmacNonce()); diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index e2467460..194316ba 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -17,8 +17,6 @@ #include #include -#include -#include #include #include #include @@ -50,9 +48,6 @@ #define APDU_P2 0x00 #define APDU_RESP_STATUS_OK 0x9000 -#define INS_BEGIN_KM_CMD 0x00 -#define INS_END_KM_PROVISION_CMD 0x20 -#define INS_END_KM_CMD 0x7F #define SW_KM_OPR 0UL #define SB_KM_OPR 1UL #define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) @@ -73,43 +68,6 @@ struct KM_AUTH_LIST_Delete { void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); } }; -enum class Instruction { - // Keymaster commands - INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD+1, - INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+2, - INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD+3, - INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+4, - INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD+5, - INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD+6, - INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD+7, - INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD+8, - INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD+9, - INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD+10, - INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD+11, - INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD+12, - INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD+13, - INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD+14, - INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD+15, - INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD+16, - INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD+17, - INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD+18, - INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD+19, - INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD+20, - INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD+21, - INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD+22, - INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+7, - INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+8, -}; - -enum ProvisionStatus { - NOT_PROVISIONED = 0x00, - PROVISION_STATUS_ATTESTATION_KEY = 0x01, - PROVISION_STATUS_ATTESTATION_CERT_DATA = 0x02, - PROVISION_STATUS_ATTEST_IDS = 0x04, - PROVISION_STATUS_PRESHARED_SECRET = 0x08, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x10, -}; - //Extended error codes enum ExtendedErrors { SW_CONDITIONS_NOT_SATISFIED = -10001, @@ -406,7 +364,7 @@ uint16_t getStatus(std::vector& inputData) { return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); } -ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response) { +ErrorCode JavacardKeymaster4Device::sendData(Instruction ins, std::vector& inData, std::vector& response) { ErrorCode ret = ErrorCode::UNKNOWN_ERROR; std::vector apdu; @@ -431,11 +389,25 @@ ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response) { + uint8_t earlyBootEndedFlag = 0; + if (cachedEarlyBootEvent) { + earlyBootEndedFlag = 1; + } + arr.add(earlyBootEndedFlag); + std::vector input = arr.encode(); + auto ret = sendData(ins, input, response); + if (ret == ErrorCode::OK) { + cachedEarlyBootEvent = false; + } + return ret; +} + /** * Sends android system properties like os_version, os_patchlevel and vendor_patchlevel to * the Applet. */ -static ErrorCode setAndroidSystemProperties(CborConverter& cborConverter_, const std::unique_ptr& oprCtx) { +ErrorCode JavacardKeymaster4Device::setAndroidSystemProperties() { ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; cppbor::Array array; std::unique_ptr item; @@ -450,7 +422,7 @@ static ErrorCode setAndroidSystemProperties(CborConverter& cborConverter_, const if (ErrorCode::OK == errorCode) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx); + true, oprCtx_); } if (ErrorCode::OK != errorCode) LOG(ERROR) << "Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t) errorCode; @@ -465,12 +437,13 @@ JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::A return context; }(), kOperationTableSize, keymaster::MessageVersion(keymaster::KmVersion::KEYMASTER_4_1, - 0 /* km_date */) )), oprCtx_(new OperationContext()), isEachSystemPropertySet(false) { + 0 /* km_date */) )), oprCtx_(new OperationContext()), + isEachSystemPropertySet(false), cachedEarlyBootEvent(false) { // Send Android system properties like os_version, os_patchlevel and vendor_patchlevel // to the Applet. Incase if setting system properties fails here, again try setting // it from computeSharedHmac. - if (ErrorCode::OK == setAndroidSystemProperties(cborConverter_, oprCtx_)) { + if (ErrorCode::OK == setAndroidSystemProperties()) { LOG(ERROR) << "javacard strongbox : setAndroidSystemProperties from constructor - successful"; isEachSystemPropertySet = true; } @@ -517,11 +490,11 @@ Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) { std::vector cborData; - std::vector input; + cppbor::Array array; std::unique_ptr item; HmacSharingParameters hmacSharingParameters; ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, input, cborData); + errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, array, cborData); if (ErrorCode::OK == errorCode) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborData.begin(), cborData.end()-2), @@ -551,7 +524,7 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec JavacardKeymaster4Device //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData( cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); + } else { + // Incase of failure cache the event and send in the next immediate request to Applet. + cachedEarlyBootEvent = true; } return errorCode; } diff --git a/HAL/keymaster/include/JavacardKeymaster4Device.h b/HAL/keymaster/include/JavacardKeymaster4Device.h index e34a2aee..09a02d2d 100644 --- a/HAL/keymaster/include/JavacardKeymaster4Device.h +++ b/HAL/keymaster/include/JavacardKeymaster4Device.h @@ -24,6 +24,8 @@ #include #include "CborConverter.h" #include "TransportFactory.h" +#include +#include #include #include #include @@ -32,6 +34,9 @@ namespace keymaster { namespace V4_1 { namespace javacard { +#define INS_BEGIN_KM_CMD 0x00 +#define INS_END_KM_PROVISION_CMD 0x20 +#define INS_END_KM_CMD 0x7F using ::android::hardware::hidl_vec; using ::android::hardware::hidl_string; @@ -62,6 +67,34 @@ enum class OperationType { UNKNOWN = 2, }; +enum class Instruction { + // Keymaster commands + INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD+1, + INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+2, + INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD+3, + INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+4, + INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD+5, + INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD+6, + INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD+7, + INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD+8, + INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD+9, + INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD+10, + INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD+11, + INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD+12, + INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD+13, + INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD+14, + INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD+15, + INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD+16, + INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD+17, + INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD+18, + INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD+19, + INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD+20, + INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD+21, + INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD+22, + INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+7, + INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+8, +}; + class JavacardKeymaster4Device : public IKeymasterDevice { public: @@ -93,9 +126,6 @@ class JavacardKeymaster4Device : public IKeymasterDevice { Return deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) override; Return earlyBootEnded() override; -protected: - CborConverter cborConverter_; - private: ErrorCode handleBeginPublicKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, @@ -120,9 +150,15 @@ class JavacardKeymaster4Device : public IKeymasterDevice { ErrorCode abortPrivateKeyOperation(uint64_t operationHandle); + ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response); + ErrorCode sendData(Instruction ins, cppbor::Array& arr, std::vector& response); + ErrorCode setAndroidSystemProperties(); + std::unique_ptr<::keymaster::AndroidKeymaster> softKm_; std::unique_ptr oprCtx_; bool isEachSystemPropertySet; + bool cachedEarlyBootEvent; + CborConverter cborConverter_; }; } // namespace javacard From 5df5d5efd889c3ece45c403f1b2dc6697e3307d0 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sat, 20 Nov 2021 06:09:46 +0000 Subject: [PATCH 23/33] Added support PKCS8 decoder --- .../javacard/keymaster/KMKeymasterApplet.java | 110 +++++++----------- .../4.1/JavacardKeymaster4Device.cpp | 13 +-- ProvisioningTool/include/constants.h | 2 +- ProvisioningTool/src/construct_apdus.cpp | 14 +-- ProvisioningTool/test_resources/batch_key.der | Bin 121 -> 138 bytes 5 files changed, 45 insertions(+), 94 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index a57c3bf4..dc5dfbdf 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -34,6 +34,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. + public static final byte[] F4 = {0x01, 0x00, 0x01}; public static final byte AES_BLOCK_SIZE = 16; public static final byte DES_BLOCK_SIZE = 8; public static final short MAX_LENGTH = (short) 0x2000; @@ -790,7 +791,7 @@ private void processProvisionAttestationKey(APDU apdu) { data[IMPORTED_KEY_BLOB] = KMArray.cast(args).get((short) 2); // Key format must be RAW format tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - if (tmpVariables[0] != KMType.RAW) { + if (tmpVariables[0] != KMType.PKCS8) { KMException.throwIt(KMError.UNIMPLEMENTED); } data[ORIGIN] = KMType.IMPORTED; @@ -1284,7 +1285,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); // import of RSA and EC not supported with pkcs8 or x509 format if ((tmpVariables[1] == KMType.RSA || tmpVariables[1] == KMType.EC) - && (tmpVariables[2] != KMType.RAW)) { + && (tmpVariables[2] != KMType.PKCS8)) { KMException.throwIt(KMError.UNIMPLEMENTED); } @@ -1394,18 +1395,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { data[ORIGIN] = KMType.SECURELY_IMPORTED; data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); // create key blob array - data[IMPORTED_KEY_BLOB] = KMArray.instance((short) 1); - // add the byte blob containing decrypted input data - KMArray.cast(data[IMPORTED_KEY_BLOB]) - .add( - (short) 0, - KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length())); - // encode the key blob - tmpVariables[0] = repository.alloc((short) (KMByteBlob.cast(data[INPUT_DATA]).length() + 16)); - tmpVariables[1] = - encoder.encode(data[IMPORTED_KEY_BLOB], repository.getHeap(), tmpVariables[0]); - data[IMPORTED_KEY_BLOB] = - KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); + data[IMPORTED_KEY_BLOB] = KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length()); importKey(apdu, scratchPad); } @@ -3013,9 +3003,15 @@ private void processImportKeyCmd(APDU apdu) { data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); // Key format must be RAW format - X509 and PKCS8 not implemented. tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); - if (tmpVariables[3] != KMType.RAW) { - KMException.throwIt(KMError.UNIMPLEMENTED); + + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && tmpVariables[3] != KMType.RAW ){ + KMException.throwIt(KMError.UNIMPLEMENTED); } + if((alg == KMType.RSA || alg == KMType.EC) && tmpVariables[3] != KMType.PKCS8){ + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[ORIGIN] = KMType.IMPORTED; importKey(apdu, scratchPad); } @@ -3083,17 +3079,10 @@ private void importKey(APDU apdu, byte[] scratchPad) { private void importECKeys(byte[] scratchPad) { // Decode key material - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); + data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); + data[SECRET] = KMArray.cast(keyBlob).get((short) 1); // initialize 256 bit p256 key for given private key and public key. tmpVariables[4] = 0; // index for update list in scratchPad @@ -3161,17 +3150,7 @@ private void importECKeys(byte[] scratchPad) { private void importHmacKey(byte[] scratchPad) { // Get Key - tmpVariables[0] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - // create HMAC key of up to 512 bit - + data[SECRET] = data[IMPORTED_KEY_BLOB]; tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = @@ -3212,15 +3191,7 @@ private void importHmacKey(byte[] scratchPad) { private void importTDESKey(byte[] scratchPad) { // Decode Key Material - tmpVariables[0] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[SECRET] = data[IMPORTED_KEY_BLOB]; tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = @@ -3251,9 +3222,12 @@ private void importTDESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); - // validate TDES Key parameters - validateTDESKey(); - + // Read Minimum Mac length - it must not be present + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_TAG); + } data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3265,15 +3239,7 @@ private void validateAesKeySize(short keySizeBits) { private void importAESKey(byte[] scratchPad) { // Get Key - tmpVariables[0] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[SECRET] = data[IMPORTED_KEY_BLOB]; // create 128 or 256 bit AES key tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. @@ -3311,17 +3277,19 @@ private void importAESKey(byte[] scratchPad) { private void importRSAKey(byte[] scratchPad) { // Decode key material - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret = private exponent - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // modulus - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]); + data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0); + short pubKeyExp = KMArray.cast(keyblob).get((short)1); + data[SECRET] = KMArray.cast(keyblob).get((short) 2); + + if(F4.length != KMByteBlob.cast(pubKeyExp).length()) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + if(Util.arrayCompare(F4, (short)0, KMByteBlob.cast(pubKeyExp).getBuffer(), + KMByteBlob.cast(pubKeyExp).getStartOff(), (short)F4.length) != 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } tmpVariables[4] = 0; // index in scratchPad for update parameters. // validate public exponent if present in key params - it must be 0x010001 tmpVariables[2] = @@ -3738,7 +3706,7 @@ private static void validateTDESKey() { if (tmpVariables[1] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } - if (tmpVariables[1] != 168 && tmpVariables[1] != 192) { + if (tmpVariables[1] != 168) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index 194316ba..bb554c54 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -656,16 +656,9 @@ Return JavacardKeymaster4Device::importKey(const hidl_vec& k return Void(); } cborConverter_.addKeyparameters(array, keyParams); - array.add(static_cast(KeyFormat::RAW)); //javacard accepts only RAW. - if(ErrorCode::OK != (errorCode = prepareCborArrayFromKeyData(keyParams, keyFormat, keyData, subArray))) { - LOG(ERROR) << "INS_IMPORT_KEY_CMD Error in while creating cbor data from key data:" << (int32_t) errorCode; - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); - } - std::vector encodedArray = subArray.encode(); - cppbor::Bstr bstr(encodedArray.begin(), encodedArray.end()); - array.add(bstr); - + array.add(static_cast(keyFormat)); //javacard accepts only RAW. + + array.add(std::vector(keyData)); std::vector cborData = array.encode(); errorCode = sendData(Instruction::INS_IMPORT_KEY_CMD, cborData, cborOutData); diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h index ffc0011f..27eb30d8 100644 --- a/ProvisioningTool/include/constants.h +++ b/ProvisioningTool/include/constants.h @@ -71,7 +71,7 @@ constexpr uint64_t kCurveP256 = 1; constexpr uint64_t kAlgorithmEc = 3; constexpr uint64_t kDigestSha256 = 4; constexpr uint64_t kPurposeAttest = 0x7F; -constexpr uint64_t kKeyFormatRaw = 3; +constexpr uint64_t kKeyFormatPkcs8 = 1; // json keys constexpr char kAttestKey[] = "attest_key"; diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp index fdad6be0..8b8999a5 100644 --- a/ProvisioningTool/src/construct_apdus.cpp +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -184,32 +184,22 @@ int processAttestationKey() { Json::Value keyFile = root.get(kAttestKey, Json::Value::nullRef); if (!keyFile.isNull()) { std::vector data; - std::vector privateKey; - std::vector publicKey; - std::string keyFileName = keyFile.asString(); if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { printf("\n Failed to read the attestation key from the file.\n"); return FAILURE; } - if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, publicKey)) { - return FAILURE; - } - // Prepare cbor input. Array input; - Array keys; Map map; - keys.add(privateKey); - keys.add(publicKey); map.add(kTagAlgorithm, kAlgorithmEc); map.add(kTagDigest, std::vector({kDigestSha256})); map.add(kTagCurve, kCurveP256); map.add(kTagPurpose, std::vector({kPurposeAttest})); // Add elements inside cbor array. input.add(std::move(map)); - input.add(kKeyFormatRaw); - input.add(keys.encode()); + input.add(kKeyFormatPkcs8); + input.add(data); std::vector cborData = input.encode(); if(SUCCESS != addApduHeader(kAttestationKeyCmd, cborData)) { diff --git a/ProvisioningTool/test_resources/batch_key.der b/ProvisioningTool/test_resources/batch_key.der index f490207337bc76f637d018d63e19616e2fc4c7eb..d7de570568afc8a92caf17af1534ec93872749e3 100644 GIT binary patch delta 41 tcmb>IVw5*%Y-eI*Fc4;A*J|@PXUoLM#sOw9GqSVf8e~soQk`h*1^~r^37-G} delta 25 gcmeBTtYk7MpD3@&vw(|@L#xf>oGmjW`$QKv096wPZ~y=R From be8509a810cec7c7052a212239abf3e950ff574a Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sat, 20 Nov 2021 06:10:14 +0000 Subject: [PATCH 24/33] Added support PKCS8 decoder --- .../javacard/keymaster/KMPKCS8Decoder.java | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java new file mode 100644 index 00000000..5e67eb83 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -0,0 +1,205 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8Decoder { + public static final byte ASN1_OCTET_STRING= 0x04; + public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_INTEGER= 0x02; + public static final byte ASN1_A0_TAG = (byte) 0xA0; + public static final byte ASN1_A1_TAG = (byte) 0xA1; + public static final byte ASN1_BIT_STRING = 0x03; + public static final byte[] EC_CURVE = { + 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, + 0x01,0x07 + }; + public static final byte[] RSA_ALGORITHM = { + 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, + (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + }; + public static final byte[] EC_ALGORITHM = { + 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, + 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, + (byte)0xce,0x3d,0x03,0x01,0x07 + }; + private byte[] data; + private short start; + private short length; + private short cur; + private static KMPKCS8Decoder inst; + private KMPKCS8Decoder(){ + start = 0; + length = 0; + cur = 0; + } + + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + public short decodeEcSubjectPublicKeyInfo(short blob) { + init(blob); + header(ASN1_SEQUENCE); + short len = header(ASN1_SEQUENCE); + short ecPublicInfo = KMByteBlob.instance(len); + getBytes(ecPublicInfo); + if(Util.arrayCompare( + KMByteBlob.cast(ecPublicInfo).getBuffer(), + KMByteBlob.cast(ecPublicInfo).getStartOff(), + EC_ALGORITHM, + (short)0,KMByteBlob.cast(ecPublicInfo).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + return pubKey; + } + + //Seq[Int,Int,Int,Int,] + public short decodeRsaPrivateKey(short version){ + short resp = KMArray.instance((short)3); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len =header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_INTEGER); + short modulus = getModulus(len); + len = header(ASN1_INTEGER); + short pubKey = KMByteBlob.instance(len); + getBytes(pubKey); + len = header(ASN1_INTEGER); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + KMArray.cast(resp).add((short)0, modulus); + KMArray.cast(resp).add((short)1, pubKey); + KMArray.cast(resp).add((short)2, privKey); + return resp; + } + + // Seq [Int, Blob] + public void decodeCommon(short version, byte[] alg){ + short len = header(ASN1_SEQUENCE); + len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_SEQUENCE); + short blob = KMByteBlob.instance(len); + getBytes(blob); + if(Util.arrayCompare( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + alg, + (short)0,KMByteBlob.cast(blob).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + //Seq[Int,blob,blob] + public short decodeEcPrivateKey(short version){ + short resp = KMArray.instance((short)2); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_OCTET_STRING); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + validateTag0IfPresent(); + header(ASN1_A1_TAG); + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + KMArray.cast(resp).add((short)0, pubKey); + KMArray.cast(resp).add((short)1, privKey); + return resp; + } + private void validateTag0IfPresent(){ + if(data[cur] != ASN1_A0_TAG) return;; + short len = header(ASN1_A0_TAG); + if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + incrementCursor(len); + } + private short header(short tag){ + short t = getByte(); + if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + return getLength(); + } + + private byte getByte(){ + byte d = data[cur]; + incrementCursor((short)1); + return d; + } + + private short getShort(){ + short d = Util.getShort(data, cur); + incrementCursor((short)2); + return d; + } + + private short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + private void getBytes(short blob){ + short len = KMByteBlob.cast(blob).length(); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), len); + incrementCursor(len); + } + + private short getLength(){ + byte len = getByte(); + if(len >= 0) return len; + len = (byte)(len & 0x7F); + if(len == 1) return (short)(getByte() & 0xFF); + else if(len == 2) return getShort(); + else KMException.throwIt(KMError.UNKNOWN_ERROR); + return KMType.INVALID_VALUE; //should not come here + } + public static KMPKCS8Decoder instance() { + if (inst == null) { + inst = new KMPKCS8Decoder(); + } + return inst; + } + + public void init(short blob) { + data = KMByteBlob.cast(blob).getBuffer(); + start = KMByteBlob.cast(blob).getStartOff(); + length = KMByteBlob.cast(blob).length(); + cur = start; + } + + public void incrementCursor(short n){ + cur += n; + if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + } +} From 524c02258717691807ba426b1d90e1c0cb3e99a3 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sat, 20 Nov 2021 22:49:49 +0000 Subject: [PATCH 25/33] Send earlybootEndedEvent if pending from begin, generateKey, importKey --- .../javacard/keymaster/KMKeymasterApplet.java | 14 +------ .../4.1/JavacardKeymaster4Device.cpp | 42 +++++++++++-------- .../include/JavacardKeymaster4Device.h | 4 +- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index dc5dfbdf..b8fa2fcb 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -975,19 +975,7 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { } private void processGetHmacSharingParamCmd(APDU apdu) { - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Arguments - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - if ((short) 1 == KMInteger.cast(KMArray.cast(args).get((short) 0)).getShort()) { - repository.setEarlyBootEndedStatus(true); - } - + // No Arguments // Create HMAC Sharing Parameters tmpVariables[2] = KMHmacSharingParameters.instance(); KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(repository.getHmacNonce()); diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index bb554c54..7e80740a 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -389,18 +389,13 @@ ErrorCode JavacardKeymaster4Device::sendData(Instruction ins, std::vector& response) { - uint8_t earlyBootEndedFlag = 0; - if (cachedEarlyBootEvent) { - earlyBootEndedFlag = 1; - } - arr.add(earlyBootEndedFlag); - std::vector input = arr.encode(); - auto ret = sendData(ins, input, response); - if (ret == ErrorCode::OK) { - cachedEarlyBootEvent = false; +void JavacardKeymaster4Device::handleSendEarlyBootEndedEvent() { + if (isEarlyBootEventPending) { + LOG(INFO) << "JavacardKeymaster4Device::handleSendEarlyBootEndedEvent send earlyBootEnded Event."; + if (V41ErrorCode::OK == earlyBootEnded()) { + isEarlyBootEventPending = false; + } } - return ret; } /** @@ -438,7 +433,7 @@ JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::A }(), kOperationTableSize, keymaster::MessageVersion(keymaster::KmVersion::KEYMASTER_4_1, 0 /* km_date */) )), oprCtx_(new OperationContext()), - isEachSystemPropertySet(false), cachedEarlyBootEvent(false) { + isEachSystemPropertySet(false), isEarlyBootEventPending(false) { // Send Android system properties like os_version, os_patchlevel and vendor_patchlevel // to the Applet. Incase if setting system properties fails here, again try setting // it from computeSharedHmac. @@ -490,11 +485,11 @@ Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) { std::vector cborData; - cppbor::Array array; + std::vector input; std::unique_ptr item; HmacSharingParameters hmacSharingParameters; ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, array, cborData); + errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, input, cborData); if (ErrorCode::OK == errorCode) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborData.begin(), cborData.end()-2), @@ -505,7 +500,9 @@ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingPa errorCode = ErrorCode::UNKNOWN_ERROR; } } - LOG(ERROR) << "javacard strongbox : received getHmacSharingParameter from Javacard - successful"; + LOG(DEBUG) << "javacard strongbox : received getHmacSharingParameter from Javacard - successful"; + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); } _hidl_cb(errorCode, hmacSharingParameters); return Void(); @@ -535,6 +532,9 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec JavacardKeymaster4Device::generateKey(const hidl_vec& ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; KeyCharacteristics keyCharacteristics; hidl_vec updatedParams(keyParams); - + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); if(!findTag(keyParams, Tag::CREATION_DATETIME) && !findTag(keyParams, Tag::ACTIVE_DATETIME)) { //Add CREATION_DATETIME in HAL, as secure element is not having clock. @@ -649,6 +650,8 @@ Return JavacardKeymaster4Device::importKey(const hidl_vec& k ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; KeyCharacteristics keyCharacteristics; cppbor::Array subArray; + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); if(keyFormat != KeyFormat::PKCS8 && keyFormat != KeyFormat::RAW) { LOG(ERROR) << "INS_IMPORT_KEY_CMD unsupported key format " << (int32_t)keyFormat; @@ -697,7 +700,8 @@ Return JavacardKeymaster4Device::importWrappedKey(const hidl_vec& hidl_vec authList; KeyFormat keyFormat; std::vector wrappedKeyDescription; - + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); if(ErrorCode::OK != (errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, authList, keyFormat, wrappedKeyDescription))) { LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD error while parsing wrapped key status: " << (int32_t) errorCode; @@ -1031,6 +1035,8 @@ ErrorCode JavacardKeymaster4Device::handleBeginPrivateKeyOperation( KeyCharacteristics keyCharacteristics; KeyParameter param; + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); /* Convert input data to cbor format */ array.add(static_cast(purpose)); array.add(std::vector(keyBlob)); @@ -1507,7 +1513,7 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); } else { // Incase of failure cache the event and send in the next immediate request to Applet. - cachedEarlyBootEvent = true; + isEarlyBootEventPending = true; } return errorCode; } diff --git a/HAL/keymaster/include/JavacardKeymaster4Device.h b/HAL/keymaster/include/JavacardKeymaster4Device.h index 09a02d2d..617457b1 100644 --- a/HAL/keymaster/include/JavacardKeymaster4Device.h +++ b/HAL/keymaster/include/JavacardKeymaster4Device.h @@ -151,13 +151,13 @@ class JavacardKeymaster4Device : public IKeymasterDevice { ErrorCode abortPrivateKeyOperation(uint64_t operationHandle); ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response); - ErrorCode sendData(Instruction ins, cppbor::Array& arr, std::vector& response); ErrorCode setAndroidSystemProperties(); + void handleSendEarlyBootEndedEvent(); std::unique_ptr<::keymaster::AndroidKeymaster> softKm_; std::unique_ptr oprCtx_; bool isEachSystemPropertySet; - bool cachedEarlyBootEvent; + bool isEarlyBootEventPending; CborConverter cborConverter_; }; From c8655fe08302ca6187f6779990f0f8af6926fb0d Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Mon, 22 Nov 2021 19:21:57 +0000 Subject: [PATCH 26/33] ProvisionAttestaionKey is accepted only as RAW --- .../javacard/keymaster/KMKeymasterApplet.java | 58 +++++++++++++------ ProvisioningTool/include/constants.h | 2 +- ProvisioningTool/src/construct_apdus.cpp | 14 ++++- 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b8fa2fcb..b2df2690 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -773,11 +773,11 @@ private void processProvisionAttestationKey(APDU apdu) { byte[] scratchPad = apdu.getBuffer(); // Arguments short keyparams = KMKeyParameters.exp(); - short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT); short blob = KMByteBlob.exp(); short argsProto = KMArray.instance((short) 3); KMArray.cast(argsProto).add((short) 0, keyparams); - KMArray.cast(argsProto).add((short) 1, keyFormat); + KMArray.cast(argsProto).add((short) 1, keyFormatPtr); KMArray.cast(argsProto).add((short) 2, blob); // Decode the argument @@ -790,8 +790,8 @@ private void processProvisionAttestationKey(APDU apdu) { tmpVariables[0] = KMArray.cast(args).get((short) 1); data[IMPORTED_KEY_BLOB] = KMArray.cast(args).get((short) 2); // Key format must be RAW format - tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - if (tmpVariables[0] != KMType.PKCS8) { + byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal(); + if (keyFormat != KMType.RAW) { KMException.throwIt(KMError.UNIMPLEMENTED); } data[ORIGIN] = KMType.IMPORTED; @@ -830,7 +830,7 @@ private void processProvisionAttestationKey(APDU apdu) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // Import EC Key - initializes data[SECRET] data[PUB_KEY] - importECKeys(scratchPad); + importECKeys(scratchPad, keyFormat); // persist key seProvider.createAttestationKey( @@ -1270,10 +1270,9 @@ private void processImportWrappedKeyCmd(APDU apdu) { tmpVariables[1] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[0]); // read key format tmpVariables[2] = KMArray.cast(args).get((short) 1); - tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - // import of RSA and EC not supported with pkcs8 or x509 format + byte keyFormat = KMEnum.cast(tmpVariables[2]).getVal(); if ((tmpVariables[1] == KMType.RSA || tmpVariables[1] == KMType.EC) - && (tmpVariables[2] != KMType.PKCS8)) { + && (keyFormat != KMType.PKCS8)) { KMException.throwIt(KMError.UNIMPLEMENTED); } @@ -1384,7 +1383,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); // create key blob array data[IMPORTED_KEY_BLOB] = KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length()); - importKey(apdu, scratchPad); + importKey(apdu, scratchPad, keyFormat); } private void processAttestKeyCmd(APDU apdu) { @@ -2989,22 +2988,22 @@ private void processImportKeyCmd(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); - // Key format must be RAW format - X509 and PKCS8 not implemented. - tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); - + + byte keyFormat = KMEnum.cast(tmpVariables[3]).getVal(); + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && tmpVariables[3] != KMType.RAW ){ + if((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFormat != KMType.RAW ) { KMException.throwIt(KMError.UNIMPLEMENTED); } - if((alg == KMType.RSA || alg == KMType.EC) && tmpVariables[3] != KMType.PKCS8){ + if((alg == KMType.RSA || alg == KMType.EC) && keyFormat != KMType.PKCS8){ KMException.throwIt(KMError.UNIMPLEMENTED); } data[ORIGIN] = KMType.IMPORTED; - importKey(apdu, scratchPad); + importKey(apdu, scratchPad, keyFormat); } - private void importKey(APDU apdu, byte[] scratchPad) { + private void importKey(APDU apdu, byte[] scratchPad, byte keyFormat) { tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); @@ -3044,7 +3043,7 @@ private void importKey(APDU apdu, byte[] scratchPad) { importHmacKey(scratchPad); break; case KMType.EC: - importECKeys(scratchPad); + importECKeys(scratchPad, keyFormat); break; default: KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); @@ -3065,12 +3064,35 @@ private void importKey(APDU apdu, byte[] scratchPad) { sendOutgoing(apdu); } - private void importECKeys(byte[] scratchPad) { + private void decodeRawECKey() { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + } + + private void decodePKCS8ECKeys() { // Decode key material KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); data[SECRET] = KMArray.cast(keyBlob).get((short) 1); + } + + private void importECKeys(byte[] scratchPad, byte keyFormat) { + if (keyFormat == KMType.RAW) { + decodeRawECKey(); + } else { + decodePKCS8ECKeys(); + } // initialize 256 bit p256 key for given private key and public key. tmpVariables[4] = 0; // index for update list in scratchPad diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h index 27eb30d8..ffc0011f 100644 --- a/ProvisioningTool/include/constants.h +++ b/ProvisioningTool/include/constants.h @@ -71,7 +71,7 @@ constexpr uint64_t kCurveP256 = 1; constexpr uint64_t kAlgorithmEc = 3; constexpr uint64_t kDigestSha256 = 4; constexpr uint64_t kPurposeAttest = 0x7F; -constexpr uint64_t kKeyFormatPkcs8 = 1; +constexpr uint64_t kKeyFormatRaw = 3; // json keys constexpr char kAttestKey[] = "attest_key"; diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp index 8b8999a5..fdad6be0 100644 --- a/ProvisioningTool/src/construct_apdus.cpp +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -184,22 +184,32 @@ int processAttestationKey() { Json::Value keyFile = root.get(kAttestKey, Json::Value::nullRef); if (!keyFile.isNull()) { std::vector data; + std::vector privateKey; + std::vector publicKey; + std::string keyFileName = keyFile.asString(); if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { printf("\n Failed to read the attestation key from the file.\n"); return FAILURE; } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, publicKey)) { + return FAILURE; + } + // Prepare cbor input. Array input; + Array keys; Map map; + keys.add(privateKey); + keys.add(publicKey); map.add(kTagAlgorithm, kAlgorithmEc); map.add(kTagDigest, std::vector({kDigestSha256})); map.add(kTagCurve, kCurveP256); map.add(kTagPurpose, std::vector({kPurposeAttest})); // Add elements inside cbor array. input.add(std::move(map)); - input.add(kKeyFormatPkcs8); - input.add(data); + input.add(kKeyFormatRaw); + input.add(keys.encode()); std::vector cborData = input.encode(); if(SUCCESS != addApduHeader(kAttestationKeyCmd, cborData)) { From d31849071c2a36b561adff714e32c109b5d379af Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Sun, 28 Nov 2021 18:37:41 +0000 Subject: [PATCH 27/33] Added applet upgrade versioning support --- .../AndroidSEProvider/AndroidSEProvider.iml | 13 + .../javacard/keymaster/KMAndroidSEApplet.java | 70 +++- .../keymaster/KMAndroidSEProvider.java | 62 +++- .../keymaster/KMAttestationCertImpl.java | 56 ++-- .../javacard/keymaster/KMOperationImpl.java | 6 +- .../android/javacard/keymaster/KMUtils.java | 8 +- .../android/javacard/keymaster/KMDecoder.java | 13 +- .../android/javacard/keymaster/KMEnum.java | 2 +- .../android/javacard/keymaster/KMEnumTag.java | 2 +- .../android/javacard/keymaster/KMInteger.java | 4 +- .../javacard/keymaster/KMKeyParameters.java | 118 ++++--- .../javacard/keymaster/KMKeymasterApplet.java | 31 +- .../javacard/keymaster/KMOperationState.java | 6 +- .../javacard/keymaster/KMRepository.java | 317 ++++++++++-------- .../javacard/keymaster/KMUpgradable.java | 2 +- 15 files changed, 428 insertions(+), 282 deletions(-) create mode 100644 Applet/AndroidSEProvider/AndroidSEProvider.iml diff --git a/Applet/AndroidSEProvider/AndroidSEProvider.iml b/Applet/AndroidSEProvider/AndroidSEProvider.iml new file mode 100644 index 00000000..1e48a48d --- /dev/null +++ b/Applet/AndroidSEProvider/AndroidSEProvider.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 7c6f31fe..d888dcc0 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -19,6 +19,8 @@ import org.globalplatform.upgrade.OnUpgradeListener; import org.globalplatform.upgrade.UpgradeManager; +import javacard.framework.Util; + public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { KMAndroidSEApplet() { @@ -49,8 +51,15 @@ public void onRestore(Element element) { element.initRead(); provisionStatus = element.readByte(); keymasterState = element.readByte(); - repository.onRestore(element); - seProvider.onRestore(element); + // TODO write a comment + if (dataBaseVersion <= CURRENT_DATABASE_VERSION) { + dataBaseVersion = element.readShort(); + } + repository.onRestore(element, dataBaseVersion, CURRENT_DATABASE_VERSION); + seProvider.onRestore(element, dataBaseVersion, CURRENT_DATABASE_VERSION); + if (dataBaseVersion == INVALID_DATA_VERSION) { + handleDataUpgradeToVersion1(); + } } @Override @@ -70,6 +79,7 @@ public Element onSave() { primitiveCount, objectCount); element.write(provisionStatus); element.write(keymasterState); + element.write(dataBaseVersion); repository.onSave(element); seProvider.onSave(element); return element; @@ -77,11 +87,65 @@ public Element onSave() { private short computePrimitveDataSize() { // provisionStatus + keymasterState - return (short) 2; + return (short) 4; } private short computeObjectCount() { return (short) 0; } + + public void handleDataUpgradeToVersion1() { + dataBaseVersion = CURRENT_DATABASE_VERSION; + // Update computed HMAC key. + short blob = repository.getComputedHmacKey(); + if (blob != KMType.INVALID_VALUE) { + seProvider.createComputedHmacKey( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + KMByteBlob.cast(blob).length() + ); + } + short certExpiryLen = 0; + short issuerLen = 0; + short certExpiry = repository.getCertExpiryTime(); + if (certExpiry != KMType.INVALID_VALUE) { + certExpiryLen = KMByteBlob.cast(certExpiry).length(); + } + short issuer = repository.getIssuer(); + if (issuer != KMType.INVALID_VALUE) { + issuerLen = KMByteBlob.cast(issuer).length(); + } + short certChainLen = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); + short offset = repository.allocReclaimableMemory((short) (certExpiryLen + issuerLen + certChainLen)); + // Get the start offset of the certificate chain. + short certChaionOff = + decoder.getCborBytesStartOffset( + repository.getHeap(), + offset, + seProvider.readProvisionedData(KMSEProvider.CERTIFICATE_CHAIN, repository.getHeap(), offset)); + certChainLen -= (short) (certChaionOff - offset); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(issuer).getBuffer(), + KMByteBlob.cast(issuer).getStartOff(), + repository.getHeap(), + (short) (certChaionOff + certChainLen), + issuerLen); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(certExpiry).getBuffer(), + KMByteBlob.cast(certExpiry).getStartOff(), + repository.getHeap(), + (short) (certChaionOff + certChainLen + issuerLen), + certExpiryLen); + + seProvider.persistProvisionData( + repository.getHeap(), + certChaionOff, // cert chain offset + certChainLen, + (short) (certChaionOff + certChainLen), // issuer offset + issuerLen, + (short) (certChaionOff + certChainLen + issuerLen), // cert expiry offset + certExpiryLen); + repository.reclaimMemory((short) (certExpiryLen + issuerLen + certChainLen)); + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 985d87fb..f3cad8ef 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -18,6 +18,8 @@ import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; @@ -119,13 +121,14 @@ public class KMAndroidSEProvider implements KMSEProvider { private static final short MAX_OPERATIONS = 4; private static final short HMAC_MAX_OPERATIONS = 8; private static final short COMPUTED_HMAC_KEY_SIZE = 32; + public static final short INVALID_DATA_VERSION = 0x7FFF; private static final short CERT_CHAIN_OFFSET = 0; private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; private static final short CERT_EXPIRY_OFFSET = (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); - final byte[] CIPHER_ALGS = { + private static final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, Cipher.ALG_DES_CBC_NOPAD, @@ -136,7 +139,7 @@ public class KMAndroidSEProvider implements KMSEProvider { Cipher.ALG_RSA_NOPAD, AEADCipher.ALG_AES_GCM}; - final byte[] SIG_ALGS = { + private static final byte[] SIG_ALGS = { Signature.ALG_RSA_SHA_256_PKCS1, Signature.ALG_RSA_SHA_256_PKCS1_PSS, Signature.ALG_ECDSA_SHA_256, @@ -145,6 +148,14 @@ public class KMAndroidSEProvider implements KMSEProvider { KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST, KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST}; + // [L] 256 bits - hardcoded 32 bits as per + // reference impl in keymaster. + private static final byte[] CMAC_KDF_CONSTANT_L = { + 0, 0, 1, 0 + }; + private static final byte[] CMAC_KDF_CONSTANT_ZERO = { + 0 + }; // AESKey private AESKey aesKeys[]; // DES3Key @@ -715,15 +726,7 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta // This is hardcoded to requirement - 32 byte output with two concatenated // 16 bytes K1 and K2. final byte n = 2; // hardcoded - // [L] 256 bits - hardcoded 32 bits as per - // reference impl in keymaster. - final byte[] L = { - 0, 0, 1, 0 - }; - // byte - final byte[] zero = { - 0 - }; + // [i] counter - 32 bits short iBufLen = 4; short keyOutLen = n * 16; @@ -746,10 +749,10 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta // 4 bytes of iBuf with counter in it kdf.update(tmpArray, (short) 0, (short) iBufLen); kdf.update(label, labelStart, (short) labelLen); // label - kdf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 + kdf.update(CMAC_KDF_CONSTANT_ZERO, (short) 0, (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00 kdf.update(context, contextStart, contextLength); // context // 4 bytes of L - signature of 16 bytes - pos = kdf.sign(L, (short) 0, (short) 4, tmpArray, + pos = kdf.sign(CMAC_KDF_CONSTANT_L, (short) 0, (short) CMAC_KDF_CONSTANT_L.length, tmpArray, (short) (iBufLen + pos)); i++; } @@ -1015,6 +1018,8 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; KMHmacKey key = (KMHmacKey) computedHmacKey; + byte[] data = new byte[32]; + key.setKey(data, (short) 0, (short) 32); Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); opr = getHmacSignOperationInstanceFromPool(); opr.setMode(KMType.VERIFY); @@ -1147,8 +1152,7 @@ private void persistProvisionData(byte[] buf, short off, short len, short maxSiz KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(provisionData, copyToOff, len); - Util.arrayCopyNonAtomic(buf, off, provisionData, (short) (copyToOff + 2), len); + Util.arrayCopyNonAtomic(buf, off, provisionData, Util.setShort(provisionData, copyToOff, len), len); JCSystem.commitTransaction(); } @@ -1228,12 +1232,20 @@ public void onSave(Element element) { } @Override - public void onRestore(Element element) { + public void onRestore(Element element, short oldVersion, short currentVersion) { provisionData = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); - computedHmacKey = KMHmacKey.onRestore(element); + if (oldVersion == INVALID_DATA_VERSION) { + handleDataUpgradeToVersion1(); + } else if (oldVersion <= currentVersion) { + computedHmacKey = KMHmacKey.onRestore(element); + } else { + // Invalid case + // TODO test exception in on Restore. + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } } @Override @@ -1350,4 +1362,20 @@ public void releaseAllOperations() { public KMComputedHmacKey getComputedHmacKey() { return computedHmacKey; } + + private void handleDataUpgradeToVersion1() { + short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + + KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); + byte[] oldBuffer = provisionData; + provisionData = new byte[totalLen]; + persistCertificateChain( + oldBuffer, + (short) 2, + Util.getShort(oldBuffer, (short) 0)); + + // Request object deletion + oldBuffer = null; + JCSystem.requestObjectDeletion(); + + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index e35853ca..7e5eb5cc 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -70,6 +70,31 @@ public class KMAttestationCertImpl implements KMAttestationCert { 0x03, 0x02 }; + + // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. + private static final short[] swTagIds = { + KMType.ATTESTATION_APPLICATION_ID, + KMType.CREATION_DATETIME, + KMType.USAGE_EXPIRE_DATETIME, + KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.ACTIVE_DATETIME, + KMType.UNLOCKED_DEVICE_REQUIRED + }; + + // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. + private static final short[] hwTagIds = { + KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, + KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, + KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, + KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, + KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, + KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, + KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, + KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, + KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, + KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, + KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; + // Validity is not fixed field // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys private static final byte[] X509Subject = { @@ -451,44 +476,27 @@ private static void pushKeyDescription() { private static void pushSWParams() { short last = stackPtr; - // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. - short[] tagIds = { - KMType.ATTESTATION_APPLICATION_ID, KMType.CREATION_DATETIME, - KMType.USAGE_EXPIRE_DATETIME, KMType.ORIGINATION_EXPIRE_DATETIME, - KMType.ACTIVE_DATETIME, KMType.UNLOCKED_DEVICE_REQUIRED}; byte index = 0; + short length = (short) swTagIds.length; do { - pushParams(swParams, swParamsIndex, tagIds[index]); - } while (++index < tagIds.length); + pushParams(swParams, swParamsIndex, swTagIds[index]); + } while (++index < length); pushSequenceHeader((short) (last - stackPtr)); } private static void pushHWParams() { short last = stackPtr; - // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. - short[] tagIds = { - KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, - KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, - KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, - KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, - KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, - KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, - KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, - KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, - KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, - KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, - KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; - byte index = 0; + short length = (short) hwTagIds.length; do { - if (tagIds[index] == KMType.ROOT_OF_TRUST) { + if (hwTagIds[index] == KMType.ROOT_OF_TRUST) { pushRoT(); continue; } - if (pushParams(hwParams, hwParamsIndex, tagIds[index])) { + if (pushParams(hwParams, hwParamsIndex, hwTagIds[index])) { continue; } - } while (++index < tagIds.length); + } while (++index < length); pushSequenceHeader((short) (last - stackPtr)); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 81641ee7..de304d8f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -28,6 +28,7 @@ public class KMOperationImpl implements KMOperation { private static final short OPER_MODE_OFFSET = 0x02; private static final short BLOCK_MODE_OFFSET = 0x03; private static final short MAC_LENGTH_OFFSET = 0x04; + private static final byte[] EMPTY = {}; //This will hold the length of the buffer stored inside the //Java Card after the GCM update operation. private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; @@ -239,13 +240,12 @@ public void abort() { if (operationInst[0] != null) { if ((parameters[OPER_MODE_OFFSET] == KMType.SIGN || parameters[OPER_MODE_OFFSET] == KMType.VERIFY) && (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { - byte[] empty = {}; Signature signer = (Signature) operationInst[0]; try { if (parameters[OPER_MODE_OFFSET] == KMType.SIGN) { - signer.verify(empty, (short) 0, (short) 0, empty, (short) 0, (short) 0); + signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); } else { - signer.sign(empty, (short) 0, (short) 0, empty, (short) 0); + signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0); } } catch(Exception e) { // Ignore. diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index 88b7b4d1..e41663ec 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -52,6 +52,8 @@ public class KMUtils { 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 public static final short year2051 = 2051; public static final short year2020 = 2020; + // Convert to milliseconds constants + public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3}; // -------------------------------------- public static short convertToDate(short time, byte[] scratchPad, @@ -422,11 +424,11 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, short scratchPadOff) { - byte[] shiftPos = {9, 8, 7, 6, 5, 3}; short index = 0; - while (index < (short) (shiftPos.length)) { + short length = (short) SEC_TO_MILLIS_SHIFT_POS.length; + while (index < length) { Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); - shiftLeft(buf, scratchPadOff, shiftPos[index]); + shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]); Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8); add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff)); Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8); diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index e305913c..7bd0e6ec 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -436,14 +436,14 @@ private void incrementStartOff(short inc) { } } -//Reads the offset and length values of the ByteBlobs from a CBOR array buffer. + // Reads the offset and length values of the ByteBlobs from a CBOR array buffer. public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOffset, short bufLen, byte[] out, short outOff) { - // Read Array length bufferRef[0] = buf; scratchBuf[START_OFFSET] = bufOffset; scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); short byteBlobLength = 0; + // Read Array length short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); if (expectedArrLen != payloadLength) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -459,5 +459,14 @@ public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOff index++; } } + + public short getCborBytesStartOffset(byte[] buf, short bufOffset, short bufLen) { + bufferRef[0] = buf; + scratchBuf[START_OFFSET] = bufOffset; + scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); + + readMajorTypeWithPayloadLength(BYTES_TYPE); + return scratchBuf[START_OFFSET]; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/src/com/android/javacard/keymaster/KMEnum.java index 2b55a6ce..a55c243d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -30,7 +30,7 @@ public class KMEnum extends KMType { private static KMEnum prototype; // The allowed enum types. - private static short[] types = { + private static final short[] types = { HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 0e45414e..f69aaf51 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -31,7 +31,7 @@ public class KMEnumTag extends KMTag { // The allowed tag keys of type enum tag. - private static short[] tags = { + private static final short[] tags = { ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE }; diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index fd080198..2ae32ac1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -101,14 +101,14 @@ public static short uint_16(short num) { // create integer and copy integer value public static short uint_32(byte[] num, short offset) { short ptr = instance(UINT_32); - Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); + Util.arrayCopyNonAtomic(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); return ptr; } // create integer and copy integer value public static short uint_64(byte[] num, short offset) { short ptr = instance(UINT_64); - Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); + Util.arrayCopyNonAtomic(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); return ptr; } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 3592e32b..f57fffbe 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -26,9 +26,61 @@ * arrayPtr is a pointer to array with any KMTag subtype instances. */ public class KMKeyParameters extends KMType { - - - private static short[] customTags; + + private static final short[] tagArr = { + // Unsupported tags. + KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS + }; + + private static final short[] hwEnforcedTagArr = { + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMType.ENUM_TAG, KMType.ALGORITHM, + KMType.UINT_TAG, KMType.KEYSIZE, + KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, + KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMType.ENUM_ARRAY_TAG, KMType.PADDING, + KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.BOOL_TAG, KMType.CALLER_NONCE, + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMType.ENUM_TAG, KMType.ECCURVE, + KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, + KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, + KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, + KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED + }; + + private static final short[] swEnforcedTagsArr = { + KMType.DATE_TAG, KMType.ACTIVE_DATETIME, + KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, + KMType.UINT_TAG, KMType.USERID, + KMType.DATE_TAG, KMType.CREATION_DATETIME, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY + }; + + private static final short[] invalidTagsArr = { + KMType.BYTES_TAG, KMType.NONCE, + KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, + KMType.BYTES_TAG, KMType.UNIQUE_ID, + KMType.UINT_TAG, KMType.MAC_LENGTH, + }; + + private static final short[] customTags = { + KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + }; private static KMKeyParameters prototype; @@ -111,12 +163,6 @@ public short findTag(short tagType, short tagKey) { } public static boolean hasUnsupportedTags(short keyParamsPtr) { - final short[] tagArr = { - // Unsupported tags. - KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, - KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS - }; byte index = 0; short tagInd; short tagPtr; @@ -145,33 +191,6 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { public static short makeHwEnforced(short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, short bootPatchObjPtr, byte[] scratchPad) { - final short[] hwEnforcedTagArr = { - // HW Enforced - KMType.ENUM_TAG, KMType.ORIGIN, - KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, - KMType.ENUM_TAG, KMType.ALGORITHM, - KMType.UINT_TAG, KMType.KEYSIZE, - KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, - KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, - KMType.ENUM_ARRAY_TAG, KMType.DIGEST, - KMType.ENUM_ARRAY_TAG, KMType.PADDING, - KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, - KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, - KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, - KMType.UINT_TAG, KMType.AUTH_TIMEOUT, - KMType.BOOL_TAG, KMType.CALLER_NONCE, - KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, - KMType.ENUM_TAG, KMType.ECCURVE, - KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, - KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, - KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, - KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, - KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, - KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, - KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED - }; byte index = 0; short tagInd; short arrInd = 0; @@ -224,14 +243,6 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, // ALL_USERS, EXPORTABLE missing from types.hal public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { - final short[] swEnforcedTagsArr = { - KMType.DATE_TAG, KMType.ACTIVE_DATETIME, - KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, - KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, - KMType.UINT_TAG, KMType.USERID, - KMType.DATE_TAG, KMType.CREATION_DATETIME, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY - }; byte index = 0; short tagInd; short arrInd = 0; @@ -296,12 +307,6 @@ public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfT } public static boolean isValidTag(short tagType, short tagKey) { - short[] invalidTagsArr = { - KMType.BYTES_TAG, KMType.NONCE, - KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, - KMType.BYTES_TAG, KMType.UNIQUE_ID, - KMType.UINT_TAG, KMType.MAC_LENGTH, - }; short index = 0; if (tagKey == KMType.INVALID_TAG) { return false; @@ -326,18 +331,8 @@ public static short createKeyParameters(byte[] ptrArr, short len) { } return KMKeyParameters.instance(arrPtr); } - - private static short[] getCustomTags() { - if (customTags == null) { - customTags = new short[] { - KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, - }; - } - return customTags; - } - + public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { - short[] customTags = getCustomTags(); short index = 0; short tagPtr; short len = (short) customTags.length; @@ -364,7 +359,6 @@ public static short addCustomTags(short keyParams, byte[] scratchPad, short offs public void deleteCustomTags() { short arrPtr = getVals(); - short[] customTags = getCustomTags(); short index = (short) (customTags.length - 1); short obj; while (index >= 0) { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b2df2690..9122126e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -43,6 +43,11 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + // DATABASE version + public static final short DATABASE_VERSION_1 = 1; + public static final short CURRENT_DATABASE_VERSION = DATABASE_VERSION_1; + public static final short INVALID_DATA_VERSION = 0x7FFF; + protected short dataBaseVersion; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -68,6 +73,14 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x6B, 0x65, 0x6E }; + + // getHardwareInfo constants. + private static final byte[] JAVACARD_KEYMASTER_DEVICE = { + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + private static final byte[] GOOGLE = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + // Possible states of the applet. private static final byte KM_BEGIN_STATE = 0x00; @@ -199,9 +212,11 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { boolean isUpgrading = seImpl.isUpgrading(); repository = new KMRepository(isUpgrading); initializeTransientArrays(); + dataBaseVersion = INVALID_DATA_VERSION; if (!isUpgrading) { keymasterState = KMKeymasterApplet.INIT_STATE; seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); + dataBaseVersion = CURRENT_DATABASE_VERSION; } KMType.initialize(); encoder = new KMEncoder(); @@ -615,12 +630,6 @@ public static void receiveIncoming(APDU apdu) { private void processGetHwInfoCmd(APDU apdu) { // No arguments expected - final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - // Make the response short respPtr = KMArray.instance((short) 3); KMArray resp = KMArray.cast(respPtr); @@ -628,8 +637,8 @@ private void processGetHwInfoCmd(APDU apdu) { resp.add( (short) 1, KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); - resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + JAVACARD_KEYMASTER_DEVICE, (short) 0, (short) JAVACARD_KEYMASTER_DEVICE.length)); + resp.add((short) 2, KMByteBlob.instance(GOOGLE, (short) 0, (short) GOOGLE.length)); bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); // Encode the response - actual bufferProp[BUF_LEN_OFFSET] is 86 @@ -2025,7 +2034,7 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scr // empty Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); short len = (short) authVerification.length; // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); @@ -2063,7 +2072,7 @@ private boolean verifyVerificationTokenMacInLittleEndian(short verToken, byte[] // empty Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); short len = (short) authVerification.length; // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); @@ -2307,7 +2316,7 @@ private void processBeginOperationCmd(APDU apdu) { // While sending the iv back for DES/CBC mode of opeation only send // 8 bytes back. tmpVariables[1] = KMByteBlob.instance((short) 8); - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), KMByteBlob.cast(tmpVariables[1]).getBuffer(), diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 7d2a7f4d..bfd67ceb 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -88,7 +88,7 @@ public static KMOperationState instance(short opHandle) { public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr, Object hmacSignerOpr) { KMOperationState opState = proto(); opState.reset(); - Util.arrayCopy(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); + Util.arrayCopyNonAtomic(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); prototype.objRefs[OPERATION] = opr; prototype.objRefs[HMAC_SIGNER_OPERATION] = hmacSignerOpr; Util.setShort(prototype.data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); @@ -176,7 +176,7 @@ public short getAuthTime() { } public void setAuthTime(byte[] timeBuf, short start) { - Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); + Util.arrayCopyNonAtomic(timeBuf, start, data, (short) AUTH_TIME, (short) 8); dataUpdated(); } @@ -220,7 +220,7 @@ public void setUserSecureId(short integerArrayPtr) { offset += 2; while (index < length) { obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index); - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMInteger.cast(obj).getBuffer(), KMInteger.cast(obj).getStartOff(), data, diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index d8646130..9755d2e0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -29,25 +29,10 @@ */ public class KMRepository implements KMUpgradable { - public static final byte DEFAULT_TABLE_TABLE = 0; - public static final byte ATTEST_IDS_DATA_TABLE = 1; + // Data table configuration + public static final short DATA_INDEX_SIZE = 33; public static final short DATA_INDEX_ENTRY_SIZE = 4; - // Data table configuration for attestation ids - public static final short ATTEST_IDS_DATA_INDEX_SIZE = 8; - public static final short ATTEST_IDS_DATA_TABLE_SIZE = - (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE) - + KMConfigurations.TOTAL_ATTEST_IDS_SIZE; - public static final byte ATT_ID_BRAND = 0; - public static final byte ATT_ID_DEVICE = 1; - public static final byte ATT_ID_PRODUCT = 2; - public static final byte ATT_ID_SERIAL = 3; - public static final byte ATT_ID_IMEI = 4; - public static final byte ATT_ID_MEID = 5; - public static final byte ATT_ID_MANUFACTURER = 6; - public static final byte ATT_ID_MODEL = 7; - - // Data table configuration other non provisioned parameters. - public static final short DATA_INDEX_SIZE = 22; + public static final short DATA_MEM_SIZE = 2048; public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -60,24 +45,33 @@ public class KMRepository implements KMUpgradable { private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; // Data table offsets - public static final byte BEGIN_OFFSET = 0; - public static final byte HMAC_NONCE = BEGIN_OFFSET + 0; - public static final byte BOOT_OS_VERSION = BEGIN_OFFSET + 1; - public static final byte BOOT_OS_PATCH_LEVEL = BEGIN_OFFSET + 2; - public static final byte VENDOR_PATCH_LEVEL = BEGIN_OFFSET + 3; - public static final byte BOOT_PATCH_LEVEL = BEGIN_OFFSET + 4; - public static final byte BOOT_VERIFIED_BOOT_KEY = BEGIN_OFFSET + 5; - public static final byte BOOT_VERIFIED_BOOT_HASH = BEGIN_OFFSET + 6; - public static final byte BOOT_VERIFIED_BOOT_STATE = BEGIN_OFFSET + 7; - public static final byte BOOT_DEVICE_LOCKED_STATUS = BEGIN_OFFSET + 8; - public static final byte DEVICE_LOCKED_TIME = BEGIN_OFFSET + 9; - public static final byte DEVICE_LOCKED = BEGIN_OFFSET + 10; - public static final byte DEVICE_LOCKED_PASSWORD_ONLY = BEGIN_OFFSET + 11; - // Total 8 Auth Tags start offset 12 and end offset 19. - public static final byte AUTH_TAG_1 = BEGIN_OFFSET + 12; - public static final byte BOOT_ENDED_STATUS = BEGIN_OFFSET + 20; - public static final byte EARLY_BOOT_ENDED_STATUS = BEGIN_OFFSET + 21; - public static final byte END_OFFSET = 22; + public static final byte ATT_ID_BRAND = 0; + public static final byte ATT_ID_DEVICE = 1; + public static final byte ATT_ID_PRODUCT = 2; + public static final byte ATT_ID_SERIAL = 3; + public static final byte ATT_ID_IMEI = 4; + public static final byte ATT_ID_MEID = 5; + public static final byte ATT_ID_MANUFACTURER = 6; + public static final byte ATT_ID_MODEL = 7; + public static final byte COMPUTED_HMAC_KEY = 8; + public static final byte HMAC_NONCE = 9; + public static final byte CERT_ISSUER = 10; + public static final byte CERT_EXPIRY_TIME = 11; + public static final byte BOOT_OS_VERSION = 12; + public static final byte BOOT_OS_PATCH_LEVEL = 13; + public static final byte VENDOR_PATCH_LEVEL = 14; + public static final byte BOOT_PATCH_LEVEL = 15; + public static final byte BOOT_VERIFIED_BOOT_KEY = 16; + public static final byte BOOT_VERIFIED_BOOT_HASH = 17; + public static final byte BOOT_VERIFIED_BOOT_STATE = 18; + public static final byte BOOT_DEVICE_LOCKED_STATUS = 19; + public static final byte DEVICE_LOCKED_TIME = 20; + public static final byte DEVICE_LOCKED = 21; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 22; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + public static final byte AUTH_TAG_1 = 23; + public static final byte BOOT_ENDED_STATUS = 31; + public static final byte EARLY_BOOT_ENDED_STATUS = 32; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -94,8 +88,8 @@ public class KMRepository implements KMUpgradable { public static final short DEVICE_LOCKED_PASSWORD_ONLY_SIZE = 1; public static final short BOOT_STATE_SIZE = 1; public static final short MAX_OPS = 4; - public static final byte BOOT_KEY_MAX_SIZE = 32; - public static final byte BOOT_HASH_MAX_SIZE = 32; + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; public static final short MAX_BLOB_STORAGE = 8; public static final short AUTH_TAG_LENGTH = 16; public static final short AUTH_TAG_COUNTER_SIZE = 4; @@ -103,23 +97,10 @@ public class KMRepository implements KMUpgradable { public static final short BOOT_ENDED_FLAG_SIZE = 1; public static final short EARLY_BOOT_ENDED_FLAG_SIZE = 1; private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; - - private static final short DATA_MEM_SIZE = (DATA_INDEX_ENTRY_SIZE * DATA_INDEX_SIZE) - + HMAC_SEED_NONCE_SIZE - + OS_VERSION_SIZE - + OS_PATCH_SIZE - + VENDOR_PATCH_SIZE - + BOOT_PATCH_SIZE - + BOOT_KEY_MAX_SIZE - + BOOT_HASH_MAX_SIZE - + BOOT_STATE_SIZE - + BOOT_DEVICE_LOCK_FLAG_SIZE - + DEVICE_LOCK_TS_SIZE - + DEVICE_LOCKED_FLAG_SIZE - + DEVICE_LOCKED_PASSWORD_ONLY_SIZE - + (8 * AUTH_TAG_ENTRY_SIZE) - + BOOT_ENDED_FLAG_SIZE - + EARLY_BOOT_ENDED_FLAG_SIZE; + + // Buffer type + public static final byte DEFAULT_BUF_TYPE = 0; + public static final byte ATTEST_IDS_BUF_TYPE = 1; // Class Attributes private Object[] operationStateTable; @@ -127,9 +108,8 @@ public class KMRepository implements KMUpgradable { private short[] heapIndex; private byte[] dataTable; private short dataIndex; - private byte[] attestIdsTable; - private short attestIdsIdsIndex; private short[] reclaimIndex; + private short attestIdsIndex; // This variable is used to monitor the power reset status as the Applet does not get // any power reset event. Initially the value of this variable is set to POWER_RESET_STATUS_FLAG. // If the power reset happens then this value becomes 0. @@ -276,7 +256,7 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOper KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), KMByteBlob.cast(buf).length()))) { - Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + Util.arrayCopyNonAtomic(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), KMOperationState.MAX_DATA); operations[index] = op; hmacSignerOprs[index] = hmacSignerOp; @@ -291,13 +271,13 @@ public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOper offset = (short) (index * OPER_DATA_LEN); if (0 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) { oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] = 1;/*reserved */ - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), OPERATION_HANDLE_SIZE); - Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), + Util.arrayCopyNonAtomic(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), KMOperationState.MAX_DATA); operations[index] = op; hmacSignerOprs[index] = hmacSignerOp; @@ -433,54 +413,32 @@ public short alloc(short length) { return (short) (heapIndex[0] - length); } - private short dataAlloc(byte dataTableType, short length) { - byte[] dataTable = getDataTable(dataTableType); - short dataIndex = getDataTableIndex(dataTableType); + private short dataAlloc(byte bufType, short length) { + short maxSize = getMaxLimitSize(bufType); + short dataIndex = getDataTableIndex(bufType); if (length < 0) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - if (((short) (dataIndex + length)) > dataTable.length) { + if (((short) (dataIndex + length)) > maxSize) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } dataIndex += length; - setDataTableIndex(dataTableType, dataIndex); + setDataTableIndex(bufType, dataIndex); return (short) (dataIndex - length); } - - private void newDataTable(boolean isUpgrading) { - if (!isUpgrading) { - if (dataTable == null) { - dataTable = new byte[DATA_MEM_SIZE]; - dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - } - if (attestIdsTable == null) { - attestIdsTable = new byte[ATTEST_IDS_DATA_TABLE_SIZE]; - attestIdsIdsIndex = (short) (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - } - } - } - - public byte[] getDataTable(byte dataTableType) { - if (dataTableType == ATTEST_IDS_DATA_TABLE) { - return this.attestIdsTable; - } else { - return this.dataTable; - } - } - - private short getDataTableIndex(byte dataTableType) { - if (dataTableType == ATTEST_IDS_DATA_TABLE) { - return this.attestIdsIdsIndex; + private short getDataTableIndex(byte bufType) { + if (bufType == ATTEST_IDS_BUF_TYPE) { + return this.attestIdsIndex; } else { return this.dataIndex; } } - - private void setDataTableIndex(byte dataTableType, short index) { - if (dataTableType == ATTEST_IDS_DATA_TABLE) { + + private void setDataTableIndex(byte bufType, short index) { + if (bufType == ATTEST_IDS_BUF_TYPE) { JCSystem.beginTransaction(); - this.attestIdsIdsIndex = index; + this.attestIdsIndex = index; JCSystem.commitTransaction(); } else { JCSystem.beginTransaction(); @@ -488,9 +446,30 @@ private void setDataTableIndex(byte dataTableType, short index) { JCSystem.commitTransaction(); } } + + private short getMaxLimitSize(byte bufType) { + if (bufType == ATTEST_IDS_BUF_TYPE) { + return (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + } else { // Default buf type. + return (short) dataTable.length; + } + } + + private void newDataTable(boolean isUpgrading) { + if (!isUpgrading) { + if (dataTable == null) { + dataTable = new byte[DATA_MEM_SIZE]; + attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + } + } + } + + public byte[] getDataTable() { + return dataTable; + } - private void clearDataEntry(byte dataTableType, short id) { - byte[] dataTable = getDataTable(dataTableType); + private void clearDataEntry(short id) { id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen != 0) { @@ -501,13 +480,30 @@ private void clearDataEntry(byte dataTableType, short id) { } } - private void writeDataEntry(byte dataTableType, short id, byte[] buf, short offset, short len) { + private void writeDataEntry(short id, byte[] buf, short offset, short len) { + writeDataEntry(DEFAULT_BUF_TYPE, id, buf, offset, len); + } + + private short readDataEntry(short id, byte[] buf, short offset) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (len != 0) { + Util.arrayCopyNonAtomic( + dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), + buf, + offset, + len); + } + return len; + } + + private void writeDataEntry(byte bufType, short id, byte[] buf, short offset, short len) { short dataPtr; - byte[] dataTable = getDataTable(dataTableType); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen == 0) { - dataPtr = dataAlloc(dataTableType, len); + dataPtr = dataAlloc(bufType, len); // Begin Transaction JCSystem.beginTransaction(); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); @@ -528,43 +524,11 @@ private void writeDataEntry(byte dataTableType, short id, byte[] buf, short offs } } - private short readDataEntry(byte dataTableType, short id, byte[] buf, short offset) { - byte[] dataTable = getDataTable(dataTableType); - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (len != 0) { - Util.arrayCopyNonAtomic( - dataTable, - Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), - buf, - offset, - len); - } - return len; - } - - private void clearDataEntry(short id) { - clearDataEntry(DEFAULT_TABLE_TABLE, id); - } - - private void writeDataEntry(short id, byte[] buf, short offset, short len) { - writeDataEntry(DEFAULT_TABLE_TABLE, id, buf, offset, len); - } - - private short readDataEntry(short id, byte[] buf, short offset) { - return readDataEntry(DEFAULT_TABLE_TABLE, id, buf, offset); - } - - private short dataLength(byte dataTableType, short id) { - byte[] dataTable = getDataTable(dataTableType); + private short dataLength(short id) { id = (short) (id * DATA_INDEX_ENTRY_SIZE); return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); } - private short dataLength(short id) { - return dataLength(DEFAULT_TABLE_TABLE, id); - } - public byte[] getHeap() { return heap; } @@ -573,35 +537,58 @@ public short getHmacNonce() { return readData(HMAC_NONCE); } + public short getComputedHmacKey() { + return readData(COMPUTED_HMAC_KEY); + } public void persistAttId(byte id, byte[] buf, short start, short len) { - writeDataEntry(ATTEST_IDS_DATA_TABLE, id, buf, start, len); + writeDataEntry(ATTEST_IDS_BUF_TYPE, id, buf, start, len); } public short getAttId(byte id) { - return readData(ATTEST_IDS_DATA_TABLE, id); + return readData(id); } public void deleteAttIds() { JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(attestIdsTable, (short) 0, (short) attestIdsTable.length, (byte) 0); - attestIdsIdsIndex = (short) (ATTEST_IDS_DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + attestIdsIndex = (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + Util.arrayFillNonAtomic(dataTable, attestIdsIndex, KMConfigurations.TOTAL_ATTEST_IDS_SIZE, (byte) 0); JCSystem.commitTransaction(); } - public short readData(byte dataTableType, short id) { - short len = dataLength(dataTableType, id); + public short getIssuer() { + return readData(CERT_ISSUER); + } + + public short readData(short id) { + short len = dataLength(id); if (len != 0) { short blob = KMByteBlob.instance(len); - readDataEntry(dataTableType, id, KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff()); + readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); return blob; } return KMType.INVALID_VALUE; } - public short readData(short id) { - return readData(DEFAULT_TABLE_TABLE, id); + public short readData(byte[] dataTable, short id, byte[] buf, short startOff, short bufLen) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (len > bufLen) { + return KMType.INVALID_VALUE; + } + if (len != 0) { + Util.arrayCopyNonAtomic( + dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), + buf, + startOff, + len); + } + return len; + } + + public short getCertExpiryTime() { + return readData(CERT_EXPIRY_TIME); } public short getOsVersion() { @@ -943,17 +930,24 @@ public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short l @Override public void onSave(Element ele) { ele.write(dataIndex); - ele.write(attestIdsIdsIndex); ele.write(dataTable); - ele.write(attestIdsTable); + ele.write(attestIdsIndex); } @Override - public void onRestore(Element ele) { + public void onRestore(Element ele, short oldVersion, short currentVersion) { dataIndex = ele.readShort(); - attestIdsIdsIndex = ele.readShort(); dataTable = (byte[]) ele.readObject(); - attestIdsTable = (byte[]) ele.readObject(); + if (oldVersion == KMKeymasterApplet.INVALID_DATA_VERSION) { + // Previous revisions does not contain version information. + handleDataUpgradeToVersion1(); + } else if (oldVersion <= currentVersion) { + // No change in the data base version. + attestIdsIndex = ele.readShort(); + } else { + // Invalid case + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } } @Override @@ -965,9 +959,9 @@ public short getBackupPrimitiveByteCount() { @Override public short getBackupObjectCount() { // dataTable - return (short) 2; + return (short) 1; } - + public boolean getBootEndedStatus() { short blob = readData(BOOT_ENDED_STATUS); if (blob == KMType.INVALID_VALUE) { @@ -1004,4 +998,29 @@ public void setEarlyBootEndedStatus(boolean flag) { writeDataEntry(EARLY_BOOT_ENDED_STATUS, getHeap(), start, EARLY_BOOT_ENDED_FLAG_SIZE); } + public void handleDataUpgradeToVersion1() { + byte[] oldDataTable = dataTable; + dataTable = new byte[2048]; + attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + // temp buffer. + short startOffset = alloc((short) 256); + + short index = ATT_ID_BRAND; + short len = 0; + while (index <= DEVICE_LOCKED) { + len = readData(oldDataTable, index, heap, startOffset, (short) 256); + writeDataEntry(index, heap, startOffset, len); + index++; + } + // set default values for the new IDS. + setDeviceLockPasswordOnly(false); + setBootEndedStatus(false); + setEarlyBootEndedStatus(false); + + // Request object deletion + oldDataTable = null; + JCSystem.requestObjectDeletion(); + } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java index 0a241652..87204a06 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java @@ -21,7 +21,7 @@ public interface KMUpgradable { void onSave(Element ele); - void onRestore(Element ele); + void onRestore(Element ele, short oldVersion, short currentVersion); short getBackupPrimitiveByteCount(); From 9c05e26e49d3471b10ffbb170b206473e9c1bc79 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Mon, 29 Nov 2021 08:48:47 +0000 Subject: [PATCH 28/33] optimized the AUTH_DATA creation for Keyblob --- .../keymaster/KMAndroidSEProvider.java | 25 +- .../keymaster/KMPKCS8DecoderImpl.java | 185 ++++++++++++++ .../javacard/keymaster/KMJCardSimulator.java | 24 +- .../keymaster/KMPKCS8DecoderImpl.java | 185 ++++++++++++++ .../javacard/keymaster/KMKeymasterApplet.java | 120 ++++----- .../javacard/keymaster/KMPKCS8Decoder.java | 231 +++--------------- .../javacard/keymaster/KMSEProvider.java | 19 ++ 7 files changed, 514 insertions(+), 275 deletions(-) create mode 100644 Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java create mode 100644 Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index f3cad8ef..9c40ffab 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -1018,8 +1018,6 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; KMHmacKey key = (KMHmacKey) computedHmacKey; - byte[] data = new byte[32]; - key.setKey(data, (short) 0, (short) 32); Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); opr = getHmacSignOperationInstanceFromPool(); opr.setMode(KMType.VERIFY); @@ -1124,6 +1122,11 @@ public KMAttestationCert getAttestationCert(boolean rsaCert) { return KMAttestationCertImpl.instance(rsaCert); } + @Override + public KMPKCS8Decoder getPKCS8DecoderInstance() { + return KMPKCS8DecoderImpl.instance(); + } + @Override public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, @@ -1378,4 +1381,22 @@ private void handleDataUpgradeToVersion1() { JCSystem.requestObjectDeletion(); } + + @Override + public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest.OneShot mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } finally { + if (mDigest != null) { + mDigest.close(); + mDigest = null; + } + } + return len; + } + } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java new file mode 100644 index 00000000..8585587f --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -0,0 +1,185 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { + public static final byte ASN1_OCTET_STRING= 0x04; + public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_INTEGER= 0x02; + public static final byte ASN1_A0_TAG = (byte) 0xA0; + public static final byte ASN1_A1_TAG = (byte) 0xA1; + public static final byte ASN1_BIT_STRING = 0x03; + public static final byte[] EC_CURVE = { + 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, + 0x01,0x07 + }; + public static final byte[] RSA_ALGORITHM = { + 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, + (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + }; + public static final byte[] EC_ALGORITHM = { + 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, + 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, + (byte)0xce,0x3d,0x03,0x01,0x07 + }; + private byte[] data; + private short start; + private short length; + private short cur; + private static KMPKCS8DecoderImpl inst; + private KMPKCS8DecoderImpl(){ + start = 0; + length = 0; + cur = 0; + } + + @Override + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + @Override + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + //Seq[Int,Int,Int,Int,] + public short decodeRsaPrivateKey(short version){ + short resp = KMArray.instance((short)3); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len =header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_INTEGER); + short modulus = getModulus(len); + len = header(ASN1_INTEGER); + short pubKey = KMByteBlob.instance(len); + getBytes(pubKey); + len = header(ASN1_INTEGER); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + KMArray.cast(resp).add((short)0, modulus); + KMArray.cast(resp).add((short)1, pubKey); + KMArray.cast(resp).add((short)2, privKey); + return resp; + } + + // Seq [Int, Blob] + public void decodeCommon(short version, byte[] alg){ + short len = header(ASN1_SEQUENCE); + len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_SEQUENCE); + short blob = KMByteBlob.instance(len); + getBytes(blob); + if(Util.arrayCompare( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + alg, + (short)0,KMByteBlob.cast(blob).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + //Seq[Int,blob,blob] + public short decodeEcPrivateKey(short version){ + short resp = KMArray.instance((short)2); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_OCTET_STRING); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + validateTag0IfPresent(); + header(ASN1_A1_TAG); + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + KMArray.cast(resp).add((short)0, pubKey); + KMArray.cast(resp).add((short)1, privKey); + return resp; + } + private void validateTag0IfPresent(){ + if(data[cur] != ASN1_A0_TAG) return;; + short len = header(ASN1_A0_TAG); + if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + incrementCursor(len); + } + private short header(short tag){ + short t = getByte(); + if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + return getLength(); + } + + private byte getByte(){ + byte d = data[cur]; + incrementCursor((short)1); + return d; + } + + private short getShort(){ + short d = Util.getShort(data, cur); + incrementCursor((short)2); + return d; + } + + private short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + private void getBytes(short blob){ + short len = KMByteBlob.cast(blob).length(); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), len); + incrementCursor(len); + } + + private short getLength(){ + byte len = getByte(); + if(len >= 0) return len; + len = (byte)(len & 0x7F); + if(len == 1) return (short)(getByte() & 0xFF); + else if(len == 2) return getShort(); + else KMException.throwIt(KMError.UNKNOWN_ERROR); + return KMType.INVALID_VALUE; //should not come here + } + public static KMPKCS8DecoderImpl instance() { + if (inst == null) { + inst = new KMPKCS8DecoderImpl(); + } + return inst; + } + + public void init(short blob) { + data = KMByteBlob.cast(blob).getBuffer(); + start = KMByteBlob.cast(blob).getStartOff(); + length = KMByteBlob.cast(blob).length(); + cur = start; + } + + public void incrementCursor(short n){ + cur += n; + if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + } +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index e0d2b546..2086620f 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -40,6 +40,7 @@ import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; import javacard.security.RandomData; @@ -1178,6 +1179,12 @@ public void addRngEntropy(byte[] num, short offset, short length) { public KMAttestationCert getAttestationCert(boolean rsaCert) { return KMAttestationCertImpl.instance(rsaCert); } + + @Override + public KMPKCS8Decoder getPKCS8DecoderInstance() { + return KMPKCS8DecoderImpl.instance(); + } + private short getProvisionDataBufferOffset(byte dataType) { switch(dataType) { case CERTIFICATE_CHAIN: @@ -1286,7 +1293,7 @@ public void onSave(Element ele) { } @Override - public void onRestore(Element ele) { + public void onRestore(Element ele, short oldVersion, short currentVersion) { } @Override @@ -1383,4 +1390,19 @@ public KMComputedHmacKey getComputedHmacKey() { public void releaseAllOperations() { //Do nothing. } + + @Override + public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.getInitializedMessageDigestInstance(MessageDigest.ALG_SHA_256, false); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } catch (Exception e) { + + } + return len; + } + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java new file mode 100644 index 00000000..8585587f --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -0,0 +1,185 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { + public static final byte ASN1_OCTET_STRING= 0x04; + public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_INTEGER= 0x02; + public static final byte ASN1_A0_TAG = (byte) 0xA0; + public static final byte ASN1_A1_TAG = (byte) 0xA1; + public static final byte ASN1_BIT_STRING = 0x03; + public static final byte[] EC_CURVE = { + 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, + 0x01,0x07 + }; + public static final byte[] RSA_ALGORITHM = { + 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, + (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + }; + public static final byte[] EC_ALGORITHM = { + 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, + 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, + (byte)0xce,0x3d,0x03,0x01,0x07 + }; + private byte[] data; + private short start; + private short length; + private short cur; + private static KMPKCS8DecoderImpl inst; + private KMPKCS8DecoderImpl(){ + start = 0; + length = 0; + cur = 0; + } + + @Override + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + @Override + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + //Seq[Int,Int,Int,Int,] + public short decodeRsaPrivateKey(short version){ + short resp = KMArray.instance((short)3); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len =header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_INTEGER); + short modulus = getModulus(len); + len = header(ASN1_INTEGER); + short pubKey = KMByteBlob.instance(len); + getBytes(pubKey); + len = header(ASN1_INTEGER); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + KMArray.cast(resp).add((short)0, modulus); + KMArray.cast(resp).add((short)1, pubKey); + KMArray.cast(resp).add((short)2, privKey); + return resp; + } + + // Seq [Int, Blob] + public void decodeCommon(short version, byte[] alg){ + short len = header(ASN1_SEQUENCE); + len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_SEQUENCE); + short blob = KMByteBlob.instance(len); + getBytes(blob); + if(Util.arrayCompare( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + alg, + (short)0,KMByteBlob.cast(blob).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + //Seq[Int,blob,blob] + public short decodeEcPrivateKey(short version){ + short resp = KMArray.instance((short)2); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_OCTET_STRING); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + validateTag0IfPresent(); + header(ASN1_A1_TAG); + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + KMArray.cast(resp).add((short)0, pubKey); + KMArray.cast(resp).add((short)1, privKey); + return resp; + } + private void validateTag0IfPresent(){ + if(data[cur] != ASN1_A0_TAG) return;; + short len = header(ASN1_A0_TAG); + if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + incrementCursor(len); + } + private short header(short tag){ + short t = getByte(); + if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + return getLength(); + } + + private byte getByte(){ + byte d = data[cur]; + incrementCursor((short)1); + return d; + } + + private short getShort(){ + short d = Util.getShort(data, cur); + incrementCursor((short)2); + return d; + } + + private short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + private void getBytes(short blob){ + short len = KMByteBlob.cast(blob).length(); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), len); + incrementCursor(len); + } + + private short getLength(){ + byte len = getByte(); + if(len >= 0) return len; + len = (byte)(len & 0x7F); + if(len == 1) return (short)(getByte() & 0xFF); + else if(len == 2) return getShort(); + else KMException.throwIt(KMError.UNKNOWN_ERROR); + return KMType.INVALID_VALUE; //should not come here + } + public static KMPKCS8DecoderImpl instance() { + if (inst == null) { + inst = new KMPKCS8DecoderImpl(); + } + return inst; + } + + public void init(short blob) { + data = KMByteBlob.cast(blob).getBuffer(); + start = KMByteBlob.cast(blob).getStartOff(); + length = KMByteBlob.cast(blob).length(); + cur = start; + } + + public void incrementCursor(short n){ + cur += n; + if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 9122126e..77bfdf6a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -40,7 +40,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final short MAX_LENGTH = (short) 0x2000; private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; - private static final short MAX_AUTH_DATA_SIZE = (short) 512; + private static final short MAX_AUTH_DATA_SIZE = (short) 256; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; // DATABASE version @@ -3090,8 +3090,7 @@ private void decodeRawECKey() { private void decodePKCS8ECKeys() { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); - short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); + short keyBlob = seProvider.getPKCS8DecoderInstance().decodeEc(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); data[SECRET] = KMArray.cast(keyBlob).get((short) 1); } @@ -3242,6 +3241,7 @@ private void importTDESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // Read Minimum Mac length - it must not be present + //Added this error check based on default reference implementation. tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { @@ -3296,8 +3296,7 @@ private void importAESKey(byte[] scratchPad) { private void importRSAKey(byte[] scratchPad) { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); - short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]); + short keyblob = seProvider.getPKCS8DecoderInstance().decodeRsa(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0); short pubKeyExp = KMArray.cast(keyblob).get((short)1); data[SECRET] = KMArray.cast(keyblob).get((short) 2); @@ -3998,87 +3997,64 @@ private static void encryptSecret(byte[] scratchPad) { } private static void makeAuthData(byte[] scratchPad) { - tmpVariables[0] = - addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[SW_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - tmpVariables[1] = KMArray.instance((short) (tmpVariables[0] + 1)); - } else { - tmpVariables[1] = KMArray.instance(tmpVariables[0]); - } - // convert scratch pad to KMArray - short index = 0; - short objPtr; - while (index < tmpVariables[0]) { - objPtr = Util.getShort(scratchPad, (short) (index * 2)); - KMArray.cast(tmpVariables[1]).add(index, objPtr); - index++; - } - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - KMArray.cast(tmpVariables[1]).add(index, data[PUB_KEY]); - } - - data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); - short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); - data[AUTH_DATA_LENGTH] = len; - } - - private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { - short index = (short) (offset * 2); - short tagInd = 0; - short tagPtr; - short arrLen = KMArray.cast(dataArrPtr).length(); - while (tagInd < arrLen) { - tagPtr = KMArray.cast(dataArrPtr).get(tagInd); - Util.setShort(aadBuf, index, tagPtr); - index += 2; - tagInd++; - } - return tagInd; + + short arrayLen = 3; + if (KMArray.cast(data[KEY_BLOB]).length() == 5) { + arrayLen = 4; + } + short params = KMArray.instance((short) arrayLen); + KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (4 == arrayLen) { + KMArray.cast(params).add((short) 3, data[PUB_KEY]); + } + + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); + short index = 0; + short len = 0; + short paramsLen = KMArray.cast(params).length(); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + while (index < paramsLen) { + short tag = KMArray.cast(params).get(index); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + (short) (authIndex + len + 32), (short) 32); + len = seProvider.messageDigest256(repository.getHeap(), + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + if (len != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + index++; + } + + data[AUTH_DATA] = authIndex; + data[AUTH_DATA_LENGTH] = len; } private static short deriveKey(byte[] scratchPad) { - tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); - tmpVariables[1] = repository.alloc(DERIVE_KEY_INPUT_SIZE); - // generate derivation material from hidden parameters - tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); - if (DERIVE_KEY_INPUT_SIZE > tmpVariables[2]) { - // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE - Util.arrayCopyNonAtomic(repository.getHeap(), (short) (data[AUTH_DATA]), - repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[2]), - (short) (DERIVE_KEY_INPUT_SIZE - tmpVariables[2])); - } + // KeyDerivation: - // 1. Do HMAC Sign, with below input parameters. - // Key - 128 bit master key - // Input data - HIDDEN_PARAMETERS + KeyCharacateristics - // - Truncate beyond 256 bytes. + // 1. Do HMAC Sign, Auth data. // 2. HMAC Sign generates an output of 32 bytes length. - // Consume only first 16 bytes as derived key. + // Consume only first 16 bytes as derived key. // Hmac sign. - tmpVariables[3] = seProvider.hmacKDF( + short len = seProvider.hmacKDF( seProvider.getMasterKey(), repository.getHeap(), - tmpVariables[1], - DERIVE_KEY_INPUT_SIZE, + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], scratchPad, (short) 0); - if (tmpVariables[3] < 16) { + if (len < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - tmpVariables[3] = 16; + len = 16; + data[DERIVED_KEY] = repository.alloc(len); // store the derived secret in data dictionary - data[DERIVED_KEY] = tmpVariables[1]; Util.arrayCopyNonAtomic( - scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); - return tmpVariables[3]; + scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], len); + return len; } // This function masks the error code with POWER_RESET_MASK_FLAG diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index 5e67eb83..d8d472e2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -1,205 +1,36 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.javacard.keymaster; -import javacard.framework.Util; +public interface KMPKCS8Decoder { -public class KMPKCS8Decoder { - public static final byte ASN1_OCTET_STRING= 0x04; - public static final byte ASN1_SEQUENCE= 0x30; - public static final byte ASN1_INTEGER= 0x02; - public static final byte ASN1_A0_TAG = (byte) 0xA0; - public static final byte ASN1_A1_TAG = (byte) 0xA1; - public static final byte ASN1_BIT_STRING = 0x03; - public static final byte[] EC_CURVE = { - 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, - 0x01,0x07 - }; - public static final byte[] RSA_ALGORITHM = { - 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, - (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 - }; - public static final byte[] EC_ALGORITHM = { - 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, - 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, - (byte)0xce,0x3d,0x03,0x01,0x07 - }; - private byte[] data; - private short start; - private short length; - private short cur; - private static KMPKCS8Decoder inst; - private KMPKCS8Decoder(){ - start = 0; - length = 0; - cur = 0; - } - - public short decodeRsa(short blob){ - init(blob); - decodeCommon((short)0, RSA_ALGORITHM); - return decodeRsaPrivateKey((short)0); - } - - public short decodeEc(short blob){ - init(blob); - decodeCommon((short)0, EC_ALGORITHM); - return decodeEcPrivateKey((short)1); - } - - public short decodeEcSubjectPublicKeyInfo(short blob) { - init(blob); - header(ASN1_SEQUENCE); - short len = header(ASN1_SEQUENCE); - short ecPublicInfo = KMByteBlob.instance(len); - getBytes(ecPublicInfo); - if(Util.arrayCompare( - KMByteBlob.cast(ecPublicInfo).getBuffer(), - KMByteBlob.cast(ecPublicInfo).getStartOff(), - EC_ALGORITHM, - (short)0,KMByteBlob.cast(ecPublicInfo).length()) !=0){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - len = header(ASN1_BIT_STRING); - if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - byte unusedBits = getByte(); - if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); - short pubKey = KMByteBlob.instance((short)(len -1)); - getBytes(pubKey); - return pubKey; - } - - //Seq[Int,Int,Int,Int,] - public short decodeRsaPrivateKey(short version){ - short resp = KMArray.instance((short)3); - header(ASN1_OCTET_STRING); - header(ASN1_SEQUENCE); - short len =header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); - len = header(ASN1_INTEGER); - short modulus = getModulus(len); - len = header(ASN1_INTEGER); - short pubKey = KMByteBlob.instance(len); - getBytes(pubKey); - len = header(ASN1_INTEGER); - short privKey = KMByteBlob.instance(len); - getBytes(privKey); - KMArray.cast(resp).add((short)0, modulus); - KMArray.cast(resp).add((short)1, pubKey); - KMArray.cast(resp).add((short)2, privKey); - return resp; - } - - // Seq [Int, Blob] - public void decodeCommon(short version, byte[] alg){ - short len = header(ASN1_SEQUENCE); - len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - short ver = getByte(); - if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); - len = header(ASN1_SEQUENCE); - short blob = KMByteBlob.instance(len); - getBytes(blob); - if(Util.arrayCompare( - KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), - alg, - (short)0,KMByteBlob.cast(blob).length()) !=0){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } - - //Seq[Int,blob,blob] - public short decodeEcPrivateKey(short version){ - short resp = KMArray.instance((short)2); - header(ASN1_OCTET_STRING); - header(ASN1_SEQUENCE); - short len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); - len = header(ASN1_OCTET_STRING); - short privKey = KMByteBlob.instance(len); - getBytes(privKey); - validateTag0IfPresent(); - header(ASN1_A1_TAG); - len = header(ASN1_BIT_STRING); - if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); - byte unusedBits = getByte(); - if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); - short pubKey = KMByteBlob.instance((short)(len -1)); - getBytes(pubKey); - KMArray.cast(resp).add((short)0, pubKey); - KMArray.cast(resp).add((short)1, privKey); - return resp; - } - private void validateTag0IfPresent(){ - if(data[cur] != ASN1_A0_TAG) return;; - short len = header(ASN1_A0_TAG); - if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); - if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); - incrementCursor(len); - } - private short header(short tag){ - short t = getByte(); - if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); - return getLength(); - } - - private byte getByte(){ - byte d = data[cur]; - incrementCursor((short)1); - return d; - } - - private short getShort(){ - short d = Util.getShort(data, cur); - incrementCursor((short)2); - return d; - } + /** + * Decodes the PKCS8 encoded RSA Key and extracts the private and public key + * + * @param Instance of the PKCS8 encoded data + * @return Instance of KMArray holding RSA public key, RSA private key and modulus. + */ + short decodeRsa(short blob); - private short getModulus(short modulusLen) { - if(0 == data[cur] && modulusLen == 257) { - incrementCursor((short) 1); - modulusLen--; - } - short blob = KMByteBlob.instance(modulusLen); - getBytes(blob); - return blob; - } - - private void getBytes(short blob){ - short len = KMByteBlob.cast(blob).length(); - Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), len); - incrementCursor(len); - } - - private short getLength(){ - byte len = getByte(); - if(len >= 0) return len; - len = (byte)(len & 0x7F); - if(len == 1) return (short)(getByte() & 0xFF); - else if(len == 2) return getShort(); - else KMException.throwIt(KMError.UNKNOWN_ERROR); - return KMType.INVALID_VALUE; //should not come here - } - public static KMPKCS8Decoder instance() { - if (inst == null) { - inst = new KMPKCS8Decoder(); - } - return inst; - } - - public void init(short blob) { - data = KMByteBlob.cast(blob).getBuffer(); - start = KMByteBlob.cast(blob).getStartOff(); - length = KMByteBlob.cast(blob).length(); - cur = start; - } - - public void incrementCursor(short n){ - cur += n; - if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); - } + /** + * Decodes the PKCS8 encoded EC Key and extracts the private and public key + * + * @param Instance of the PKCS8 encoded data. + * @return Instance of KMArray holding EC public key and EC private key. + */ + short decodeEc(short blob); + } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index da57e3ee..b7cb5609 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -453,6 +453,13 @@ KMOperation initAsymmetricOperation( */ KMAttestationCert getAttestationCert(boolean rsaCert); + /** + * Returns the implementation of the PKCS8 decoder. + * + * @return Instance of PKCS8 decoder. + */ + KMPKCS8Decoder getPKCS8DecoderInstance(); + /** * This operation persists the provision data in the persistent memory. * @@ -591,4 +598,16 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, */ void releaseAllOperations(); + /** + * This is a one-shot operation the does digest of the input mesage. + * + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. + */ + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset); + } From 99973a2f3b90c425ffe3e640ad5cc19af24d7571 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Mon, 29 Nov 2021 21:50:24 +0000 Subject: [PATCH 29/33] Added Indentation --- .../keymaster/KMPKCS8DecoderImpl.java | 160 +++++++++++------- .../keymaster/KMPKCS8DecoderImpl.java | 160 +++++++++++------- .../javacard/keymaster/KMKeymasterApplet.java | 66 ++++---- .../javacard/keymaster/KMPKCS8Decoder.java | 10 +- .../javacard/keymaster/KMSEProvider.java | 39 ++--- 5 files changed, 255 insertions(+), 180 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java index 8585587f..921cae28 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -3,59 +3,65 @@ import javacard.framework.Util; public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { - public static final byte ASN1_OCTET_STRING= 0x04; - public static final byte ASN1_SEQUENCE= 0x30; - public static final byte ASN1_INTEGER= 0x02; + + public static final byte ASN1_OCTET_STRING = 0x04; + public static final byte ASN1_SEQUENCE = 0x30; + public static final byte ASN1_INTEGER = 0x02; public static final byte ASN1_A0_TAG = (byte) 0xA0; public static final byte ASN1_A1_TAG = (byte) 0xA1; public static final byte ASN1_BIT_STRING = 0x03; public static final byte[] EC_CURVE = { - 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, - 0x01,0x07 + 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, + 0x01, 0x07 }; public static final byte[] RSA_ALGORITHM = { - 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, - (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, + (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; public static final byte[] EC_ALGORITHM = { - 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, - 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, - (byte)0xce,0x3d,0x03,0x01,0x07 + 0x06, 0x07, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, + 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, + (byte) 0xce, 0x3d, 0x03, 0x01, 0x07 }; private byte[] data; private short start; private short length; private short cur; private static KMPKCS8DecoderImpl inst; - private KMPKCS8DecoderImpl(){ + + private KMPKCS8DecoderImpl() { start = 0; - length = 0; + length = 0; cur = 0; } @Override - public short decodeRsa(short blob){ + public short decodeRsa(short blob) { init(blob); - decodeCommon((short)0, RSA_ALGORITHM); - return decodeRsaPrivateKey((short)0); + decodeCommon((short) 0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short) 0); } @Override - public short decodeEc(short blob){ + public short decodeEc(short blob) { init(blob); - decodeCommon((short)0, EC_ALGORITHM); - return decodeEcPrivateKey((short)1); + decodeCommon((short) 0, EC_ALGORITHM); + return decodeEcPrivateKey((short) 1); } //Seq[Int,Int,Int,Int,] - public short decodeRsaPrivateKey(short version){ - short resp = KMArray.instance((short)3); + public short decodeRsaPrivateKey(short version) { + short resp = KMArray.instance((short) 3); header(ASN1_OCTET_STRING); header(ASN1_SEQUENCE); - short len =header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short len = header(ASN1_INTEGER); + if (len != 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (ver != version) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } len = header(ASN1_INTEGER); short modulus = getModulus(len); len = header(ASN1_INTEGER); @@ -64,82 +70,105 @@ public short decodeRsaPrivateKey(short version){ len = header(ASN1_INTEGER); short privKey = KMByteBlob.instance(len); getBytes(privKey); - KMArray.cast(resp).add((short)0, modulus); - KMArray.cast(resp).add((short)1, pubKey); - KMArray.cast(resp).add((short)2, privKey); + KMArray.cast(resp).add((short) 0, modulus); + KMArray.cast(resp).add((short) 1, pubKey); + KMArray.cast(resp).add((short) 2, privKey); return resp; } // Seq [Int, Blob] - public void decodeCommon(short version, byte[] alg){ + public void decodeCommon(short version, byte[] alg) { short len = header(ASN1_SEQUENCE); len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len != 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } short ver = getByte(); - if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (ver != version) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } len = header(ASN1_SEQUENCE); short blob = KMByteBlob.instance(len); getBytes(blob); - if(Util.arrayCompare( + if (Util.arrayCompare( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), alg, - (short)0,KMByteBlob.cast(blob).length()) !=0){ + (short) 0, KMByteBlob.cast(blob).length()) != 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } //Seq[Int,blob,blob] - public short decodeEcPrivateKey(short version){ - short resp = KMArray.instance((short)2); + public short decodeEcPrivateKey(short version) { + short resp = KMArray.instance((short) 2); header(ASN1_OCTET_STRING); header(ASN1_SEQUENCE); short len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len != 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (ver != version) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } len = header(ASN1_OCTET_STRING); short privKey = KMByteBlob.instance(len); getBytes(privKey); validateTag0IfPresent(); header(ASN1_A1_TAG); len = header(ASN1_BIT_STRING); - if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len < 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } byte unusedBits = getByte(); - if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); - short pubKey = KMByteBlob.instance((short)(len -1)); + if (unusedBits != 0) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + short pubKey = KMByteBlob.instance((short) (len - 1)); getBytes(pubKey); - KMArray.cast(resp).add((short)0, pubKey); - KMArray.cast(resp).add((short)1, privKey); + KMArray.cast(resp).add((short) 0, pubKey); + KMArray.cast(resp).add((short) 1, privKey); return resp; } - private void validateTag0IfPresent(){ - if(data[cur] != ASN1_A0_TAG) return;; + + private void validateTag0IfPresent() { + if (data[cur] != ASN1_A0_TAG) { + return; + } + ; short len = header(ASN1_A0_TAG); - if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); - if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len != EC_CURVE.length) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (Util.arrayCompare(data, cur, EC_CURVE, (short) 0, len) != 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } incrementCursor(len); } - private short header(short tag){ + + private short header(short tag) { short t = getByte(); - if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (t != tag) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } return getLength(); } - private byte getByte(){ + private byte getByte() { byte d = data[cur]; - incrementCursor((short)1); + incrementCursor((short) 1); return d; } - private short getShort(){ + private short getShort() { short d = Util.getShort(data, cur); - incrementCursor((short)2); + incrementCursor((short) 2); return d; } - + private short getModulus(short modulusLen) { - if(0 == data[cur] && modulusLen == 257) { + if (0 == data[cur] && modulusLen == 257) { incrementCursor((short) 1); modulusLen--; } @@ -148,22 +177,29 @@ private short getModulus(short modulusLen) { return blob; } - private void getBytes(short blob){ + private void getBytes(short blob) { short len = KMByteBlob.cast(blob).length(); Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), len); incrementCursor(len); } - private short getLength(){ + private short getLength() { byte len = getByte(); - if(len >= 0) return len; - len = (byte)(len & 0x7F); - if(len == 1) return (short)(getByte() & 0xFF); - else if(len == 2) return getShort(); - else KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len >= 0) { + return len; + } + len = (byte) (len & 0x7F); + if (len == 1) { + return (short) (getByte() & 0xFF); + } else if (len == 2) { + return getShort(); + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } return KMType.INVALID_VALUE; //should not come here } + public static KMPKCS8DecoderImpl instance() { if (inst == null) { inst = new KMPKCS8DecoderImpl(); @@ -178,8 +214,10 @@ public void init(short blob) { cur = start; } - public void incrementCursor(short n){ + public void incrementCursor(short n) { cur += n; - if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (cur > ((short) (start + length))) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } } } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java index 8585587f..921cae28 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -3,59 +3,65 @@ import javacard.framework.Util; public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { - public static final byte ASN1_OCTET_STRING= 0x04; - public static final byte ASN1_SEQUENCE= 0x30; - public static final byte ASN1_INTEGER= 0x02; + + public static final byte ASN1_OCTET_STRING = 0x04; + public static final byte ASN1_SEQUENCE = 0x30; + public static final byte ASN1_INTEGER = 0x02; public static final byte ASN1_A0_TAG = (byte) 0xA0; public static final byte ASN1_A1_TAG = (byte) 0xA1; public static final byte ASN1_BIT_STRING = 0x03; public static final byte[] EC_CURVE = { - 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, - 0x01,0x07 + 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, + 0x01, 0x07 }; public static final byte[] RSA_ALGORITHM = { - 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, - (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, + (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; public static final byte[] EC_ALGORITHM = { - 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, - 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, - (byte)0xce,0x3d,0x03,0x01,0x07 + 0x06, 0x07, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, + 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, + (byte) 0xce, 0x3d, 0x03, 0x01, 0x07 }; private byte[] data; private short start; private short length; private short cur; private static KMPKCS8DecoderImpl inst; - private KMPKCS8DecoderImpl(){ + + private KMPKCS8DecoderImpl() { start = 0; - length = 0; + length = 0; cur = 0; } @Override - public short decodeRsa(short blob){ + public short decodeRsa(short blob) { init(blob); - decodeCommon((short)0, RSA_ALGORITHM); - return decodeRsaPrivateKey((short)0); + decodeCommon((short) 0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short) 0); } @Override - public short decodeEc(short blob){ + public short decodeEc(short blob) { init(blob); - decodeCommon((short)0, EC_ALGORITHM); - return decodeEcPrivateKey((short)1); + decodeCommon((short) 0, EC_ALGORITHM); + return decodeEcPrivateKey((short) 1); } //Seq[Int,Int,Int,Int,] - public short decodeRsaPrivateKey(short version){ - short resp = KMArray.instance((short)3); + public short decodeRsaPrivateKey(short version) { + short resp = KMArray.instance((short) 3); header(ASN1_OCTET_STRING); header(ASN1_SEQUENCE); - short len =header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short len = header(ASN1_INTEGER); + if (len != 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (ver != version) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } len = header(ASN1_INTEGER); short modulus = getModulus(len); len = header(ASN1_INTEGER); @@ -64,82 +70,105 @@ public short decodeRsaPrivateKey(short version){ len = header(ASN1_INTEGER); short privKey = KMByteBlob.instance(len); getBytes(privKey); - KMArray.cast(resp).add((short)0, modulus); - KMArray.cast(resp).add((short)1, pubKey); - KMArray.cast(resp).add((short)2, privKey); + KMArray.cast(resp).add((short) 0, modulus); + KMArray.cast(resp).add((short) 1, pubKey); + KMArray.cast(resp).add((short) 2, privKey); return resp; } // Seq [Int, Blob] - public void decodeCommon(short version, byte[] alg){ + public void decodeCommon(short version, byte[] alg) { short len = header(ASN1_SEQUENCE); len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len != 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } short ver = getByte(); - if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (ver != version) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } len = header(ASN1_SEQUENCE); short blob = KMByteBlob.instance(len); getBytes(blob); - if(Util.arrayCompare( + if (Util.arrayCompare( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), alg, - (short)0,KMByteBlob.cast(blob).length()) !=0){ + (short) 0, KMByteBlob.cast(blob).length()) != 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } //Seq[Int,blob,blob] - public short decodeEcPrivateKey(short version){ - short resp = KMArray.instance((short)2); + public short decodeEcPrivateKey(short version) { + short resp = KMArray.instance((short) 2); header(ASN1_OCTET_STRING); header(ASN1_SEQUENCE); short len = header(ASN1_INTEGER); - if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len != 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } short ver = getByte(); - if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (ver != version) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } len = header(ASN1_OCTET_STRING); short privKey = KMByteBlob.instance(len); getBytes(privKey); validateTag0IfPresent(); header(ASN1_A1_TAG); len = header(ASN1_BIT_STRING); - if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len < 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } byte unusedBits = getByte(); - if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); - short pubKey = KMByteBlob.instance((short)(len -1)); + if (unusedBits != 0) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + short pubKey = KMByteBlob.instance((short) (len - 1)); getBytes(pubKey); - KMArray.cast(resp).add((short)0, pubKey); - KMArray.cast(resp).add((short)1, privKey); + KMArray.cast(resp).add((short) 0, pubKey); + KMArray.cast(resp).add((short) 1, privKey); return resp; } - private void validateTag0IfPresent(){ - if(data[cur] != ASN1_A0_TAG) return;; + + private void validateTag0IfPresent() { + if (data[cur] != ASN1_A0_TAG) { + return; + } + ; short len = header(ASN1_A0_TAG); - if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); - if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len != EC_CURVE.length) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (Util.arrayCompare(data, cur, EC_CURVE, (short) 0, len) != 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } incrementCursor(len); } - private short header(short tag){ + + private short header(short tag) { short t = getByte(); - if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (t != tag) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } return getLength(); } - private byte getByte(){ + private byte getByte() { byte d = data[cur]; - incrementCursor((short)1); + incrementCursor((short) 1); return d; } - private short getShort(){ + private short getShort() { short d = Util.getShort(data, cur); - incrementCursor((short)2); + incrementCursor((short) 2); return d; } - + private short getModulus(short modulusLen) { - if(0 == data[cur] && modulusLen == 257) { + if (0 == data[cur] && modulusLen == 257) { incrementCursor((short) 1); modulusLen--; } @@ -148,22 +177,29 @@ private short getModulus(short modulusLen) { return blob; } - private void getBytes(short blob){ + private void getBytes(short blob) { short len = KMByteBlob.cast(blob).length(); Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), len); incrementCursor(len); } - private short getLength(){ + private short getLength() { byte len = getByte(); - if(len >= 0) return len; - len = (byte)(len & 0x7F); - if(len == 1) return (short)(getByte() & 0xFF); - else if(len == 2) return getShort(); - else KMException.throwIt(KMError.UNKNOWN_ERROR); + if (len >= 0) { + return len; + } + len = (byte) (len & 0x7F); + if (len == 1) { + return (short) (getByte() & 0xFF); + } else if (len == 2) { + return getShort(); + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } return KMType.INVALID_VALUE; //should not come here } + public static KMPKCS8DecoderImpl instance() { if (inst == null) { inst = new KMPKCS8DecoderImpl(); @@ -178,8 +214,10 @@ public void init(short blob) { cur = start; } - public void incrementCursor(short n){ + public void incrementCursor(short n) { cur += n; - if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + if (cur > ((short) (start + length))) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 77bfdf6a..1d59fab3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -3997,47 +3997,45 @@ private static void encryptSecret(byte[] scratchPad) { } private static void makeAuthData(byte[] scratchPad) { - - short arrayLen = 3; - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - arrayLen = 4; - } - short params = KMArray.instance((short) arrayLen); - KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); - if (4 == arrayLen) { - KMArray.cast(params).add((short) 3, data[PUB_KEY]); - } - + + short arrayLen = 3; + if (KMArray.cast(data[KEY_BLOB]).length() == 5) { + arrayLen = 4; + } + short params = KMArray.instance((short) arrayLen); + KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (4 == arrayLen) { + KMArray.cast(params).add((short) 3, data[PUB_KEY]); + } + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); - short index = 0; - short len = 0; - short paramsLen = KMArray.cast(params).length(); - Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); - while (index < paramsLen) { - short tag = KMArray.cast(params).get(index); - len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); - Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), - (short) (authIndex + len + 32), (short) 32); - len = seProvider.messageDigest256(repository.getHeap(), - (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); - if (len != 32) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - index++; - } - - data[AUTH_DATA] = authIndex; - data[AUTH_DATA_LENGTH] = len; + short index = 0; + short len = 0; + short paramsLen = KMArray.cast(params).length(); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + while (index < paramsLen) { + short tag = KMArray.cast(params).get(index); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + (short) (authIndex + len + 32), (short) 32); + len = seProvider.messageDigest256(repository.getHeap(), + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + if (len != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + index++; + } + data[AUTH_DATA] = authIndex; + data[AUTH_DATA_LENGTH] = len; } private static short deriveKey(byte[] scratchPad) { - // KeyDerivation: // 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 len = seProvider.hmacKDF( seProvider.getMasterKey(), diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index d8d472e2..7bf5bb4b 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -17,20 +17,20 @@ public interface KMPKCS8Decoder { - /** - * Decodes the PKCS8 encoded RSA Key and extracts the private and public key + /** + * Decodes the PKCS8 encoded RSA Key and extracts the private and public key * * @param Instance of the PKCS8 encoded data * @return Instance of KMArray holding RSA public key, RSA private key and modulus. */ short decodeRsa(short blob); - + /** - * Decodes the PKCS8 encoded EC Key and extracts the private and public key + * Decodes the PKCS8 encoded EC Key and extracts the private and public key * * @param Instance of the PKCS8 encoded data. * @return Instance of KMArray holding EC public key and EC private key. */ short decodeEc(short blob); - + } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index b7cb5609..dbfa3710 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -401,14 +401,14 @@ KMOperation initSymmetricOperation( short ivLength, short macLength); - /** - * Initializes the trusted confirmation operation. - * - * @param computedHmacKey Instance of the computed Hmac key. - * @return instance of KMOperation. - */ - KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); - + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * This creates a persistent operation for signing, verify, encryption and decryption using RSA * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public @@ -555,14 +555,14 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, */ KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length); - /** - * 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. - */ + /** + * 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); /** @@ -585,11 +585,11 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, * @return Instance of the KMPreSharedKey. */ KMPreSharedKey getPresharedKey(); - + /** * Returns the computed Hmac key. * - * @return Instance of the computed hmac key. + * @return Instance of the computed hmac key. */ KMComputedHmacKey getComputedHmacKey(); @@ -608,6 +608,7 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, * @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); + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, + short outOffset); } From ec606d5de042f56dc2219c80e32d36fa46784c54 Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Mon, 29 Nov 2021 23:58:47 +0000 Subject: [PATCH 30/33] Added package version --- .../javacard/keymaster/KMAndroidSEApplet.java | 71 ++++++++++++++----- .../keymaster/KMAndroidSEProvider.java | 17 +++-- .../javacard/keymaster/KMKeymasterApplet.java | 21 +++--- .../javacard/keymaster/KMRepository.java | 16 ++--- .../javacard/keymaster/KMUpgradable.java | 2 +- 5 files changed, 84 insertions(+), 43 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index d888dcc0..1b2bce30 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -19,6 +19,8 @@ import org.globalplatform.upgrade.OnUpgradeListener; import org.globalplatform.upgrade.UpgradeManager; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { @@ -49,16 +51,25 @@ public void onConsolidate() { @Override public void onRestore(Element element) { element.initRead(); - provisionStatus = element.readByte(); - keymasterState = element.readByte(); - // TODO write a comment - if (dataBaseVersion <= CURRENT_DATABASE_VERSION) { - dataBaseVersion = element.readShort(); - } - repository.onRestore(element, dataBaseVersion, CURRENT_DATABASE_VERSION); - seProvider.onRestore(element, dataBaseVersion, CURRENT_DATABASE_VERSION); - if (dataBaseVersion == INVALID_DATA_VERSION) { - handleDataUpgradeToVersion1(); + byte magicNumber = element.readByte(); + if (magicNumber != KMKeymasterApplet.KM_MAGIC_NUMBER) { + // Previous version of the applet does not have versioning support. + // In this case the first byte is the provision status. + provisionStatus = magicNumber; + keymasterState = element.readByte(); + repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + handleDataUpgradeToVersion1_1(); + } else { + byte[] version = (byte[]) element.readObject(); + if (!isUpgradeAllowed(version)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + packageVersion = version; + provisionStatus = element.readByte(); + keymasterState = element.readByte(); + repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); } } @@ -77,25 +88,53 @@ public Element onSave() { // Create element. Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, primitiveCount, objectCount); + element.write(KM_MAGIC_NUMBER); + element.write(packageVersion); element.write(provisionStatus); element.write(keymasterState); - element.write(dataBaseVersion); repository.onSave(element); seProvider.onSave(element); return element; } private short computePrimitveDataSize() { - // provisionStatus + keymasterState - return (short) 4; + // provisionStatus + keymasterState + magic byte + return (short) 3; } private short computeObjectCount() { - return (short) 0; + return (short) 1; + } + + public boolean isUpgradeAllowed(byte[] version) { + short oldMajorVersion = Util.getShort(version, (short) 0); + short oldMinorVersion = Util.getShort(version, (short) 2); + short currentMajorVersion = Util.getShort(CURRENT_PACKAGE_VERSION, (short) 0); + short currentMinorVersion = Util.getShort(CURRENT_PACKAGE_VERSION, (short) 2); + // Downgrade of the Applet is not allowed. + if (oldMajorVersion > currentMajorVersion || + (oldMajorVersion == currentMajorVersion && oldMinorVersion > currentMinorVersion)) { + return false; + } + // Upgrade is not allowed to a next version which is not immediate. + if (1 < (currentMajorVersion - oldMajorVersion) || + (oldMajorVersion == currentMajorVersion && 1 < (currentMinorVersion - oldMinorVersion)) || + (oldMajorVersion < currentMajorVersion && 0 != currentMinorVersion)) { + return false; + } + return true; } - public void handleDataUpgradeToVersion1() { - dataBaseVersion = CURRENT_DATABASE_VERSION; + public void handleDataUpgradeToVersion1_1() { + + // Copy the package version. + Util.arrayCopy( + CURRENT_PACKAGE_VERSION, + (short) 0, + packageVersion, + (short) 0, + (short) CURRENT_PACKAGE_VERSION.length); + // Update computed HMAC key. short blob = repository.getComputedHmacKey(); if (blob != KMType.INVALID_VALUE) { diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 9c40ffab..42048756 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -1235,19 +1235,18 @@ public void onSave(Element element) { } @Override - public void onRestore(Element element, short oldVersion, short currentVersion) { + public void onRestore(Element element, byte[] oldVersion, byte[] currentVersion) { provisionData = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); - if (oldVersion == INVALID_DATA_VERSION) { - handleDataUpgradeToVersion1(); - } else if (oldVersion <= currentVersion) { - computedHmacKey = KMHmacKey.onRestore(element); + short oldMajorVersion = Util.getShort(oldVersion, (short) 0); + short oldMinorVersion = Util.getShort(oldVersion, (short) 2); + if (oldMajorVersion == 0 && oldMinorVersion == 0) { + // Previous revisions does not contain version information. + handleDataUpgradeToVersion1_1(); } else { - // Invalid case - // TODO test exception in on Restore. - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + computedHmacKey = KMHmacKey.onRestore(element); } } @@ -1366,7 +1365,7 @@ public KMComputedHmacKey getComputedHmacKey() { return computedHmacKey; } - private void handleDataUpgradeToVersion1() { + private void handleDataUpgradeToVersion1_1() { short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); byte[] oldBuffer = provisionData; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 1d59fab3..73e43c90 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -43,11 +43,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final short MAX_AUTH_DATA_SIZE = (short) 256; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; - // DATABASE version - public static final short DATABASE_VERSION_1 = 1; - public static final short CURRENT_DATABASE_VERSION = DATABASE_VERSION_1; - public static final short INVALID_DATA_VERSION = 0x7FFF; - protected short dataBaseVersion; + // Magic number version + public static final byte KM_MAGIC_NUMBER = 0x7F; + public static final byte[] CURRENT_PACKAGE_VERSION = {0x00, 0x01, 0x00, 0x01}; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -202,6 +200,8 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static short[] tmpVariables; protected static short[] data; protected static byte provisionStatus = NOT_PROVISIONED; + // First two bytes are Major version and second bytes are minor version. + protected byte[] packageVersion; /** @@ -212,11 +212,16 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { boolean isUpgrading = seImpl.isUpgrading(); repository = new KMRepository(isUpgrading); initializeTransientArrays(); - dataBaseVersion = INVALID_DATA_VERSION; + packageVersion = new byte[4]; if (!isUpgrading) { keymasterState = KMKeymasterApplet.INIT_STATE; seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); - dataBaseVersion = CURRENT_DATABASE_VERSION; + Util.arrayCopy( + CURRENT_PACKAGE_VERSION, + (short) 0, + packageVersion, + (short) 0, + (short) CURRENT_PACKAGE_VERSION.length); } KMType.initialize(); encoder = new KMEncoder(); @@ -4035,7 +4040,7 @@ private static short deriveKey(byte[] scratchPad) { // KeyDerivation: // 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 len = seProvider.hmacKDF( seProvider.getMasterKey(), diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 9755d2e0..fcdbb0d0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -935,18 +935,16 @@ public void onSave(Element ele) { } @Override - public void onRestore(Element ele, short oldVersion, short currentVersion) { + public void onRestore(Element ele, byte[] oldVersion, byte[] currentVersion) { dataIndex = ele.readShort(); dataTable = (byte[]) ele.readObject(); - if (oldVersion == KMKeymasterApplet.INVALID_DATA_VERSION) { + short oldMajorVersion = Util.getShort(oldVersion, (short) 0); + short oldMinorVersion = Util.getShort(oldVersion, (short) 2); + if (oldMajorVersion == 0 && oldMinorVersion == 0) { // Previous revisions does not contain version information. - handleDataUpgradeToVersion1(); - } else if (oldVersion <= currentVersion) { - // No change in the data base version. - attestIdsIndex = ele.readShort(); + handleDataUpgradeToVersion1_1(); } else { - // Invalid case - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + attestIdsIndex = ele.readShort(); } } @@ -998,7 +996,7 @@ public void setEarlyBootEndedStatus(boolean flag) { writeDataEntry(EARLY_BOOT_ENDED_STATUS, getHeap(), start, EARLY_BOOT_ENDED_FLAG_SIZE); } - public void handleDataUpgradeToVersion1() { + public void handleDataUpgradeToVersion1_1() { byte[] oldDataTable = dataTable; dataTable = new byte[2048]; attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java index 87204a06..09e234a4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java @@ -21,7 +21,7 @@ public interface KMUpgradable { void onSave(Element ele); - void onRestore(Element ele, short oldVersion, short currentVersion); + void onRestore(Element ele, byte[] oldVersion, byte[] currentVersion); short getBackupPrimitiveByteCount(); From 780495e0e073d30a5920bd4ab1d823d62d981aac Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Tue, 30 Nov 2021 00:46:12 +0000 Subject: [PATCH 31/33] Removed *.iml file --- Applet/AndroidSEProvider/AndroidSEProvider.iml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 Applet/AndroidSEProvider/AndroidSEProvider.iml diff --git a/Applet/AndroidSEProvider/AndroidSEProvider.iml b/Applet/AndroidSEProvider/AndroidSEProvider.iml deleted file mode 100644 index 1e48a48d..00000000 --- a/Applet/AndroidSEProvider/AndroidSEProvider.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file From 8b2d166288f124894f874971d9185e44fcc2651c Mon Sep 17 00:00:00 2001 From: Subrahmanya Manikanta Venkateswarlu Bhamidipati Kameswara Sri Date: Wed, 1 Dec 2021 00:37:49 +0000 Subject: [PATCH 32/33] Incorporated review comments --- .../javacard/keymaster/KMAndroidSEApplet.java | 82 +++--- .../keymaster/KMAndroidSEProvider.java | 15 +- .../javacard/keymaster/KMKeyParameters.java | 9 +- .../javacard/keymaster/KMKeymasterApplet.java | 38 ++- .../javacard/keymaster/KMRepository.java | 15 +- ProvisioningTool/src/provision.cpp | 13 +- .../hardware_interfaces_keymaster.patch | 241 +----------------- 7 files changed, 112 insertions(+), 301 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 1b2bce30..26a1c8ce 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -21,6 +21,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { @@ -51,26 +52,24 @@ public void onConsolidate() { @Override public void onRestore(Element element) { element.initRead(); - byte magicNumber = element.readByte(); - if (magicNumber != KMKeymasterApplet.KM_MAGIC_NUMBER) { - // Previous version of the applet does not have versioning support. - // In this case the first byte is the provision status. - provisionStatus = magicNumber; - keymasterState = element.readByte(); - repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); - seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); - handleDataUpgradeToVersion1_1(); - } else { - byte[] version = (byte[]) element.readObject(); - if (!isUpgradeAllowed(version)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - packageVersion = version; - provisionStatus = element.readByte(); - keymasterState = element.readByte(); - repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); - seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + byte firstByte = element.readByte(); + byte[] packageVersion_ = null; + byte provisionStatus_ = firstByte; + if (firstByte == KMKeymasterApplet.KM_MAGIC_NUMBER) { + packageVersion_ = (byte[]) element.readObject(); + provisionStatus_ = element.readByte(); + } + if (null != packageVersion_ && !isUpgradeAllowed(packageVersion_)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + JCSystem.beginTransaction(); + packageVersion = packageVersion_; + provisionStatus = provisionStatus_; + keymasterState = element.readByte(); + JCSystem.commitTransaction(); + repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + handleDataUpgradeToVersion1_1(); } @Override @@ -107,33 +106,58 @@ private short computeObjectCount() { } public boolean isUpgradeAllowed(byte[] version) { + boolean upgradeAllowed = false; short oldMajorVersion = Util.getShort(version, (short) 0); short oldMinorVersion = Util.getShort(version, (short) 2); short currentMajorVersion = Util.getShort(CURRENT_PACKAGE_VERSION, (short) 0); short currentMinorVersion = Util.getShort(CURRENT_PACKAGE_VERSION, (short) 2); // Downgrade of the Applet is not allowed. - if (oldMajorVersion > currentMajorVersion || - (oldMajorVersion == currentMajorVersion && oldMinorVersion > currentMinorVersion)) { - return false; - } // Upgrade is not allowed to a next version which is not immediate. - if (1 < (currentMajorVersion - oldMajorVersion) || - (oldMajorVersion == currentMajorVersion && 1 < (currentMinorVersion - oldMinorVersion)) || - (oldMajorVersion < currentMajorVersion && 0 != currentMinorVersion)) { - return false; + if (currentMajorVersion - oldMajorVersion == 1) { + if (currentMinorVersion == 0) { + upgradeAllowed = true; + } + } else if (currentMajorVersion - oldMajorVersion == 0) { + if (currentMinorVersion - oldMinorVersion == 1) { + upgradeAllowed = true; + } } - return true; + return upgradeAllowed; } public void handleDataUpgradeToVersion1_1() { + if (packageVersion != null) { + // No Data upgrade required. + return; + } + byte status = provisionStatus; + // In the current version of the applet set boot parameters is removed from + // provision status so readjust the provision locked flag. + // 0x40 is provision locked flag in the older applet. + // Unset the 5th bit. setboot parameters flag. + status = (byte) (status & 0xDF); + // Readjust the lock provisioned status flag. + if ((status & 0x40) == 0x40) { + // 0x40 to 0x20 + // Unset 6th bit + status = (byte) (status & 0xBF); + // set the 5th bit + status = (byte) (status | 0x20); + } + byte[] version = new byte[4]; + + JCSystem.beginTransaction(); + packageVersion = version; + provisionStatus = status; // Copy the package version. - Util.arrayCopy( + Util.arrayCopyNonAtomic( CURRENT_PACKAGE_VERSION, (short) 0, packageVersion, (short) 0, (short) CURRENT_PACKAGE_VERSION.length); + JCSystem.commitTransaction(); // Update computed HMAC key. short blob = repository.getComputedHmacKey(); diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 42048756..422c6b6d 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -1236,17 +1236,19 @@ public void onSave(Element element) { @Override public void onRestore(Element element, byte[] oldVersion, byte[] currentVersion) { + JCSystem.beginTransaction(); provisionData = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); - short oldMajorVersion = Util.getShort(oldVersion, (short) 0); - short oldMinorVersion = Util.getShort(oldVersion, (short) 2); - if (oldMajorVersion == 0 && oldMinorVersion == 0) { - // Previous revisions does not contain version information. + JCSystem.commitTransaction(); + if (oldVersion == null) { + // Previous versions does not contain version information. handleDataUpgradeToVersion1_1(); } else { + JCSystem.beginTransaction(); computedHmacKey = KMHmacKey.onRestore(element); + JCSystem.commitTransaction(); } } @@ -1369,7 +1371,10 @@ private void handleDataUpgradeToVersion1_1() { short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); byte[] oldBuffer = provisionData; - provisionData = new byte[totalLen]; + byte[] newBuffer = new byte[totalLen]; + JCSystem.beginTransaction(); + provisionData = newBuffer; + JCSystem.commitTransaction(); persistCertificateChain( oldBuffer, (short) 2, diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index f57fffbe..709b604d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -27,10 +27,9 @@ */ public class KMKeyParameters extends KMType { - private static final short[] tagArr = { + private static final short[] unsupportedTags = { // Unsupported tags. KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS }; @@ -175,9 +174,9 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { tagPtr = KMArray.cast(arrPtr).get(index); tagKey = KMTag.getKey(tagPtr); tagType = KMTag.getTagType(tagPtr); - while (tagInd < (short) tagArr.length) { - if ((tagArr[tagInd] == tagType) - && (tagArr[(short) (tagInd + 1)] == tagKey)) { + while (tagInd < (short) unsupportedTags.length) { + if ((unsupportedTags[tagInd] == tagType) + && (unsupportedTags[(short) (tagInd + 1)] == tagKey)) { return true; } tagInd += 2; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 73e43c90..d534972a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -129,12 +129,13 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_END_KM_CMD = 0x7F; // Provision reporting status - private static final byte NOT_PROVISIONED = 0x00; - private static final byte PROVISION_STATUS_ATTESTATION_KEY = 0x01; - private static final byte PROVISION_STATUS_ATTESTATION_CERT_DATA = 0x02; - private static final byte PROVISION_STATUS_ATTEST_IDS = 0x04; - private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x08; - private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x10; + protected static final byte NOT_PROVISIONED = 0x00; + protected static final byte PROVISION_STATUS_ATTESTATION_KEY = 0x01; + private static final byte PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02; + private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; + protected static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; + protected static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; + protected static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; // Data Dictionary items public static final byte DATA_ARRAY_SIZE = 30; @@ -212,10 +213,10 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { boolean isUpgrading = seImpl.isUpgrading(); repository = new KMRepository(isUpgrading); initializeTransientArrays(); - packageVersion = new byte[4]; if (!isUpgrading) { keymasterState = KMKeymasterApplet.INIT_STATE; seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); + packageVersion = new byte[4]; Util.arrayCopy( CURRENT_PACKAGE_VERSION, (short) 0, @@ -372,7 +373,8 @@ public void process(APDU apdu) { case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: processProvisionAttestationCertDataCmd(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_DATA; + provisionStatus |= (KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_CHAIN | + KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_PARAMS); sendError(apdu, KMError.OK); return; @@ -539,7 +541,8 @@ private void generateUniqueOperationHandle(byte[] buf, short offset, short len) private boolean isProvisioningComplete() { if ((0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_KEY)) - && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_DATA)) + && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) + && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && (0 != (provisionStatus & PROVISION_STATUS_PRESHARED_SECRET))) { return true; } else { @@ -3246,7 +3249,7 @@ private void importTDESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // Read Minimum Mac length - it must not be present - //Added this error check based on default reference implementation. + // Added this error check based on default reference implementation. tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { @@ -3718,6 +3721,7 @@ private static void generateECKeys(byte[] scratchPad) { private static void validateTDESKey() { // Read Minimum Mac length - it must not be present + // This below check is done based on the reference implementation. tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { @@ -3743,21 +3747,27 @@ private static void generateTDESKey(byte[] scratchPad) { private static void validateHmacKey() { // If params does not contain any digest throw unsupported digest error. - if (KMType.INVALID_VALUE - == KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS])) { + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (KMType.INVALID_VALUE == tmpVariables[0]) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } - // 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])) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } + // Strongbox supports only SHA256. + if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } // Read Minimum Mac length tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); } + // Check whether digest size is greater than or equal to min mac length. + // This below check is done based on the reference implementation. if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64) || tmpVariables[0] > (short) 256) { diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index fcdbb0d0..fff44825 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -936,15 +936,17 @@ public void onSave(Element ele) { @Override public void onRestore(Element ele, byte[] oldVersion, byte[] currentVersion) { + JCSystem.beginTransaction(); dataIndex = ele.readShort(); dataTable = (byte[]) ele.readObject(); - short oldMajorVersion = Util.getShort(oldVersion, (short) 0); - short oldMinorVersion = Util.getShort(oldVersion, (short) 2); - if (oldMajorVersion == 0 && oldMinorVersion == 0) { - // Previous revisions does not contain version information. + JCSystem.commitTransaction(); + if (oldVersion == null) { + // Previous versions does not contain version information. handleDataUpgradeToVersion1_1(); } else { + JCSystem.beginTransaction(); attestIdsIndex = ele.readShort(); + JCSystem.commitTransaction(); } } @@ -998,9 +1000,12 @@ public void setEarlyBootEndedStatus(boolean flag) { public void handleDataUpgradeToVersion1_1() { byte[] oldDataTable = dataTable; - dataTable = new byte[2048]; + byte[] newBuffer = new byte[2048]; + JCSystem.beginTransaction(); + dataTable = newBuffer; attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); + JCSystem.commitTransaction(); // temp buffer. short startOffset = alloc((short) 256); diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index e06c389f..bf3f96e8 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -33,12 +33,11 @@ enum ProvisionStatus { PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, PROVISION_STATUS_ATTEST_IDS = 0x08, PROVISION_STATUS_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x20, }; -std::string provisionStatusApdu = hex2str("80084000000000"); -std::string lockProvisionApdu = hex2str("80074000000000"); +std::string provisionStatusApdu = hex2str("80074000000000"); +std::string lockProvisionApdu = hex2str("80064000000000"); Json::Value root; static double keymasterVersion = -1; @@ -247,8 +246,7 @@ int getProvisionStatus() { if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) && (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { + (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET))) { printf("\n SE is provisioned \n"); } else { if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) { @@ -263,9 +261,6 @@ int getProvisionStatus() { if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { printf("\n Shared secret is not provisioned \n"); } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM)) { - printf("\n Boot params are not provisioned \n"); - } } } else { printf("\n Fail to parse the response \n"); diff --git a/aosp_integration_patches/hardware_interfaces_keymaster.patch b/aosp_integration_patches/hardware_interfaces_keymaster.patch index f18f6ed7..dd6d8326 100644 --- a/aosp_integration_patches/hardware_interfaces_keymaster.patch +++ b/aosp_integration_patches/hardware_interfaces_keymaster.patch @@ -16,248 +16,21 @@ index a7be660c4..dd91e9089 100644 "libkeymaster4vtstest", ], diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp -index 476eed8b1..3b2cdb641 100644 +index 476eed8b1..823683d75 100644 --- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp +++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp -@@ -29,6 +29,9 @@ - - #include - -+#include -+#include -+ - #include - #include - #include -@@ -1079,20 +1082,229 @@ TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) { +@@ -1079,9 +1079,12 @@ TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) { * presented. */ TEST_P(SigningOperationsTest, NoUserConfirmation) { - if (SecLevel() == SecurityLevel::STRONGBOX) return; ++ size_t key_size = 1024; ++ if (SecLevel() == SecurityLevel::STRONGBOX){ ++ key_size = 2048; ++ } ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() - .RsaSigningKey(1024, 65537) -+ .RsaSigningKey(2048, 65537) -+ .Digest(Digest::NONE) -+ .Padding(PaddingMode::NONE) -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))); -+ -+ const string message = "12345678901234567890123456789012"; -+ EXPECT_EQ(ErrorCode::OK, -+ Begin(KeyPurpose::SIGN, -+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); -+ string signature; -+ EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature)); -+} -+ -+/* -+ * SigningOperationsTest.TrustedConfirmationRequired -+ * -+ * Verifies that keymaster performs signing operations for keys with -+ * TRUSTED_CONFIRMATION_REQUIRED and a valid confirmation token -+ * presented. -+ */ -+TEST_P(SigningOperationsTest, TrustedConfirmationRequired) { -+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() -+ .RsaSigningKey(2048, 65537) -+ .Digest(Digest::NONE) -+ .Padding(PaddingMode::NONE) -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))); -+ -+ const string conf_token = "confirmation token"; -+ const string message = "12345678901234567890123456789012"; -+ string signature; -+ -+ std::vector input; -+ input.insert(input.end(), conf_token.begin(), conf_token.end()); -+ input.insert(input.end(), message.begin(), message.end()); -+ -+ std::vector macKey(32, 0); -+ std::array hash; -+ unsigned int hashLen = 0; -+ -+ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); -+ EXPECT_TRUE(result != nullptr); -+ -+ std::vector confToken(hash.begin(), hash.end()); -+ -+ EXPECT_EQ(ErrorCode::OK, -+ Begin(KeyPurpose::SIGN, -+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); -+ -+ AuthorizationSet out_params; -+ string output; -+ string retText; -+ -+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), message, signature, -+ &out_params, &output)); -+ CheckedDeleteKey(); -+} -+ -+/* -+ * SigningOperationsTest.NoUserConfirmation2_FailureSuccess -+ * -+ * Verifies that keymaster validates failure and success signing operations for key with -+ * TRUSTED_CONFIRMATION_REQUIRED and a valid confirmation token -+ * presented. -+ */ -+ -+TEST_P(SigningOperationsTest, NoUserConfirmation2_FailureSuccess) { -+ -+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() -+ .RsaSigningKey(2048, 65537) ++ .RsaSigningKey(key_size, 65537) .Digest(Digest::NONE) .Padding(PaddingMode::NONE) .Authorization(TAG_NO_AUTH_REQUIRED) - .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))); - - const string message = "12345678901234567890123456789012"; -+ const string conf_token = "confirmation token"; -+ - EXPECT_EQ(ErrorCode::OK, - Begin(KeyPurpose::SIGN, - AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); - string signature; - EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature)); -+ -+ std::vector input; -+ input.insert(input.end(), conf_token.begin(), conf_token.end()); -+ input.insert(input.end(), message.begin(), message.end()); -+ -+ std::vector macKey(32, 0); -+ std::array hash; -+ unsigned int hashLen = 0; -+ -+ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); -+ EXPECT_TRUE(result != nullptr); -+ std::vector confToken(hash.begin(), hash.end()); -+ -+ EXPECT_EQ(ErrorCode::OK, -+ Begin(KeyPurpose::SIGN, -+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); -+ -+ AuthorizationSet out_params; -+ string output; -+ string retText; -+ -+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), message, signature, -+ &out_params, &output)); -+ CheckedDeleteKey(); -+} -+ -+/* -+ * SigningOperationsTest.TrustedConfirmationRequired_HMAC -+ * -+ * Verifies that keymaster rejects signing operations for keys with -+ * TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token -+ * presented. -+ */ -+ -+TEST_P(SigningOperationsTest, TrustedConfirmationRequired_HMAC) { -+ -+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .HmacKey(128) -+ .Digest(Digest::SHA_2_256) -+ .Padding(PaddingMode::NONE) -+ .Authorization(TAG_MIN_MAC_LENGTH, 160) -+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) -+ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))) -+ << "Failed to create HMAC key with digest " ; -+ -+ const string conf_token = "confirmation token"; -+ const string message = "12345678901234567890123456789012"; -+ string signature; -+ -+ std::vector input; -+ input.insert(input.end(), conf_token.begin(), conf_token.end()); -+ input.insert(input.end(), message.begin(), message.end()); -+ -+ std::vector macKey(32, 0); -+ std::array hash; -+ unsigned int hashLen = 0; -+ -+ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); -+ EXPECT_TRUE(result != nullptr); -+ std::vector confToken(hash.begin(), hash.end()); -+ -+ EXPECT_EQ(ErrorCode::OK, -+ Begin(KeyPurpose::SIGN, -+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160))); -+ -+ AuthorizationSet out_params; -+ string output; -+ string retText; -+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), message, signature, -+ &out_params, &output)); -+ -+ CheckedDeleteKey(); -+ -+} -+ -+TEST_P(SigningOperationsTest, TrustedConfirmationRequired_TomanyOperations) { -+ -+ -+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .HmacKey(128) -+ .Digest(Digest::SHA_2_256) -+ .Padding(PaddingMode::NONE) -+ .Authorization(TAG_MIN_MAC_LENGTH, 160) -+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) -+ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED))) -+ << "Failed to create HMAC key with digest " ; -+ -+ const string conf_token = "confirmation token"; -+ const string message = "12345678901234567890123456789012"; -+ string signature; -+ -+ std::vector input; -+ input.insert(input.end(), conf_token.begin(), conf_token.end()); -+ input.insert(input.end(), message.begin(), message.end()); -+ -+ std::vector macKey(32, 0); -+ std::array hash; -+ unsigned int hashLen = 0; -+ auto result = ::HMAC(EVP_sha256(), macKey.data(), macKey.size(), input.data(), input.size(), hash.data(), &hashLen); -+ EXPECT_TRUE(result != nullptr); -+ std::vector confToken(hash.begin(), hash.end()); -+ AuthorizationSet out_params; -+ string output; -+ string retText; -+ int max_operations = SecLevel() == SecurityLevel::STRONGBOX ? 4 : 16; -+ OperationHandle op_handles[max_operations]; -+ -+ -+ for(int i=0; i< max_operations; i++) { -+ EXPECT_EQ(ErrorCode::OK, -+ Begin(KeyPurpose::SIGN, key_blob_ -+ , AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160) -+ , &out_params -+ , &(op_handles[i]))); -+ } -+ -+ EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, -+ Begin(KeyPurpose::SIGN, key_blob_ -+ , AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160) -+ , &out_params, &op_handle_)); -+ -+ for(int i=0; i< max_operations; i++) { -+ EXPECT_EQ(ErrorCode::OK, Finish(op_handles[i], AuthorizationSetBuilder().Authorization(TAG_CONFIRMATION_TOKEN,HidlBuf(confToken)), -+ message, signature, -+ &out_params, &output)); -+ } -+ -+ EXPECT_EQ(ErrorCode::OK, -+ Begin(KeyPurpose::SIGN, key_blob_ -+ , AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::NONE).Authorization(TAG_MAC_LENGTH, 160) -+ , &out_params -+ , &op_handle_)); -+ -+ -+ CheckedDeleteKey(); -+ - } - - /* From 6bbdbc95c3412fc0b25c00362b134376d35a83e6 Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Thu, 2 Dec 2021 03:46:10 +0000 Subject: [PATCH 33/33] 1. moved packageversion from byte array to short 2. Removed transactions in onRestore --- .../javacard/keymaster/KMAndroidSEApplet.java | 70 ++++++++----------- .../keymaster/KMAndroidSEProvider.java | 20 ++---- .../javacard/keymaster/KMKeymasterApplet.java | 14 ++-- .../javacard/keymaster/KMRepository.java | 13 +--- .../javacard/keymaster/KMUpgradable.java | 2 +- 5 files changed, 45 insertions(+), 74 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 26a1c8ce..7c28370a 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -21,7 +21,6 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; -import javacard.framework.JCSystem; import javacard.framework.Util; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { @@ -53,20 +52,18 @@ public void onConsolidate() { public void onRestore(Element element) { element.initRead(); byte firstByte = element.readByte(); - byte[] packageVersion_ = null; + short packageVersion_ = 0; byte provisionStatus_ = firstByte; if (firstByte == KMKeymasterApplet.KM_MAGIC_NUMBER) { - packageVersion_ = (byte[]) element.readObject(); + packageVersion_ = element.readShort(); provisionStatus_ = element.readByte(); } - if (null != packageVersion_ && !isUpgradeAllowed(packageVersion_)) { + if (0 != packageVersion_ && !isUpgradeAllowed(packageVersion_)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - JCSystem.beginTransaction(); packageVersion = packageVersion_; provisionStatus = provisionStatus_; keymasterState = element.readByte(); - JCSystem.commitTransaction(); repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); handleDataUpgradeToVersion1_1(); @@ -97,28 +94,28 @@ public Element onSave() { } private short computePrimitveDataSize() { - // provisionStatus + keymasterState + magic byte - return (short) 3; + // provisionStatus + keymasterState + magic byte + version + return (short) 5; } private short computeObjectCount() { - return (short) 1; + return (short) 0; } - public boolean isUpgradeAllowed(byte[] version) { + public boolean isUpgradeAllowed(short version) { boolean upgradeAllowed = false; - short oldMajorVersion = Util.getShort(version, (short) 0); - short oldMinorVersion = Util.getShort(version, (short) 2); - short currentMajorVersion = Util.getShort(CURRENT_PACKAGE_VERSION, (short) 0); - short currentMinorVersion = Util.getShort(CURRENT_PACKAGE_VERSION, (short) 2); + short oldMajorVersion = (short) (version >> 8 & 0x00FF); + short oldMinorVersion = (short) (version & 0x00FF); + short currentMajorVersion = (short) (CURRENT_PACKAGE_VERSION >> 8 & 0x00FF); + short currentMinorVersion = (short) (CURRENT_PACKAGE_VERSION & 0x00FF); // Downgrade of the Applet is not allowed. // Upgrade is not allowed to a next version which is not immediate. - if (currentMajorVersion - oldMajorVersion == 1) { + if ((short) (currentMajorVersion - oldMajorVersion) == 1) { if (currentMinorVersion == 0) { upgradeAllowed = true; } - } else if (currentMajorVersion - oldMajorVersion == 0) { - if (currentMinorVersion - oldMinorVersion == 1) { + } else if ((short) (currentMajorVersion - oldMajorVersion) == 0) { + if ((short) (currentMinorVersion - oldMinorVersion) == 1) { upgradeAllowed = true; } } @@ -127,7 +124,7 @@ public boolean isUpgradeAllowed(byte[] version) { public void handleDataUpgradeToVersion1_1() { - if (packageVersion != null) { + if (packageVersion != 0) { // No Data upgrade required. return; } @@ -145,29 +142,9 @@ public void handleDataUpgradeToVersion1_1() { // set the 5th bit status = (byte) (status | 0x20); } - byte[] version = new byte[4]; - - JCSystem.beginTransaction(); - packageVersion = version; provisionStatus = status; - // Copy the package version. - Util.arrayCopyNonAtomic( - CURRENT_PACKAGE_VERSION, - (short) 0, - packageVersion, - (short) 0, - (short) CURRENT_PACKAGE_VERSION.length); - JCSystem.commitTransaction(); + packageVersion = CURRENT_PACKAGE_VERSION; - // Update computed HMAC key. - short blob = repository.getComputedHmacKey(); - if (blob != KMType.INVALID_VALUE) { - seProvider.createComputedHmacKey( - KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), - KMByteBlob.cast(blob).length() - ); - } short certExpiryLen = 0; short issuerLen = 0; short certExpiry = repository.getCertExpiryTime(); @@ -208,6 +185,21 @@ public void handleDataUpgradeToVersion1_1() { issuerLen, (short) (certChaionOff + certChainLen + issuerLen), // cert expiry offset certExpiryLen); + + + // Update computed HMAC key. + short blob = repository.getComputedHmacKey(); + if (blob != KMType.INVALID_VALUE) { + seProvider.createComputedHmacKey( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + KMByteBlob.cast(blob).length() + ); + } else { + // Initialize the Key object. + Util.arrayFillNonAtomic(repository.getHeap(), offset, (short) 32, (byte) 0); + seProvider.createComputedHmacKey(repository.getHeap(), offset,(short) 32); + } repository.reclaimMemory((short) (certExpiryLen + issuerLen + certChainLen)); } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 422c6b6d..4f4d4709 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -18,8 +18,6 @@ import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; -import javacard.framework.ISO7816; -import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; @@ -33,7 +31,6 @@ import javacard.security.KeyPair; import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; -import javacard.security.RSAPublicKey; import javacard.security.RandomData; import javacard.security.Signature; import javacardx.crypto.AEADCipher; @@ -253,7 +250,9 @@ public KMAndroidSEProvider() { // Create attestation key of P-256 curve. createAttestationKey(tmpArray, (short)0, (short) 32); // Pre-shared secret key length is 32 bytes. - createPresharedKey(tmpArray, (short)0, (short) KMRepository.SHARED_SECRET_KEY_SIZE); + createPresharedKey(tmpArray, (short)0, (short) 32); + // Initialize the Computed Hmac Key object. + createComputedHmacKey(tmpArray, (short)0, (short) 32); } androidSEProvider = this; } @@ -1235,20 +1234,16 @@ public void onSave(Element element) { } @Override - public void onRestore(Element element, byte[] oldVersion, byte[] currentVersion) { - JCSystem.beginTransaction(); + public void onRestore(Element element, short oldVersion, short currentVersion) { provisionData = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); - JCSystem.commitTransaction(); - if (oldVersion == null) { + if (oldVersion == 0) { // Previous versions does not contain version information. handleDataUpgradeToVersion1_1(); } else { - JCSystem.beginTransaction(); computedHmacKey = KMHmacKey.onRestore(element); - JCSystem.commitTransaction(); } } @@ -1371,10 +1366,7 @@ private void handleDataUpgradeToVersion1_1() { short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); byte[] oldBuffer = provisionData; - byte[] newBuffer = new byte[totalLen]; - JCSystem.beginTransaction(); - provisionData = newBuffer; - JCSystem.commitTransaction(); + provisionData = new byte[totalLen]; persistCertificateChain( oldBuffer, (short) 2, diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index d534972a..f549a30d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -44,8 +44,8 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; // Magic number version - public static final byte KM_MAGIC_NUMBER = 0x7F; - public static final byte[] CURRENT_PACKAGE_VERSION = {0x00, 0x01, 0x00, 0x01}; + public static final byte KM_MAGIC_NUMBER = (byte) 0x81; + public static final short CURRENT_PACKAGE_VERSION = 0x0101; // 1.1 // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -202,7 +202,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static short[] data; protected static byte provisionStatus = NOT_PROVISIONED; // First two bytes are Major version and second bytes are minor version. - protected byte[] packageVersion; + protected short packageVersion; /** @@ -216,13 +216,7 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { if (!isUpgrading) { keymasterState = KMKeymasterApplet.INIT_STATE; seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); - packageVersion = new byte[4]; - Util.arrayCopy( - CURRENT_PACKAGE_VERSION, - (short) 0, - packageVersion, - (short) 0, - (short) CURRENT_PACKAGE_VERSION.length); + packageVersion = CURRENT_PACKAGE_VERSION; } KMType.initialize(); encoder = new KMEncoder(); diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index fff44825..7032d258 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -935,18 +935,14 @@ public void onSave(Element ele) { } @Override - public void onRestore(Element ele, byte[] oldVersion, byte[] currentVersion) { - JCSystem.beginTransaction(); + public void onRestore(Element ele, short oldVersion, short currentVersion) { dataIndex = ele.readShort(); dataTable = (byte[]) ele.readObject(); - JCSystem.commitTransaction(); - if (oldVersion == null) { + if (oldVersion == 0) { // Previous versions does not contain version information. handleDataUpgradeToVersion1_1(); } else { - JCSystem.beginTransaction(); attestIdsIndex = ele.readShort(); - JCSystem.commitTransaction(); } } @@ -1000,12 +996,9 @@ public void setEarlyBootEndedStatus(boolean flag) { public void handleDataUpgradeToVersion1_1() { byte[] oldDataTable = dataTable; - byte[] newBuffer = new byte[2048]; - JCSystem.beginTransaction(); - dataTable = newBuffer; + dataTable = new byte[2048]; attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); - JCSystem.commitTransaction(); // temp buffer. short startOffset = alloc((short) 256); diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java index 09e234a4..87204a06 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java @@ -21,7 +21,7 @@ public interface KMUpgradable { void onSave(Element ele); - void onRestore(Element ele, byte[] oldVersion, byte[] currentVersion); + void onRestore(Element ele, short oldVersion, short currentVersion); short getBackupPrimitiveByteCount();