| /* |
| * 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; |
| |
| import com.android.javacard.seprovider.KMException; |
| import javacard.framework.ISO7816; |
| import javacard.framework.ISOException; |
| import javacard.framework.Util; |
| |
| /** |
| * KMKeyParameters represents KeyParameters structure from android keymaster hal specifications. It |
| * corresponds to CBOR map type. struct{byte KEY_PARAM_TYPE; short length=2; short arrayPtr} where |
| * arrayPtr is a pointer to array with any KMTag subtype instances. |
| */ |
| public class KMKeyParameters extends KMType { |
| |
| private static final short[] customTags = { |
| KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, |
| }; |
| private static final short[] tagArr = { |
| // Unsupported tags. |
| KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, |
| KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS |
| }; |
| private static final short[] hwEnforcedTagArr = { |
| // HW Enforced |
| 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.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, |
| KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, |
| 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.BOOL_TAG, KMType.EARLY_BOOT_ONLY, |
| KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, |
| KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, |
| }; |
| 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.UINT_TAG, KMType.USAGE_COUNT_LIMIT, |
| KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, |
| KMType.UINT_TAG, KMType.MAX_BOOT_LEVEL, |
| }; |
| private static final short[] teeEnforcedTagsArr = { |
| KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, |
| KMType.UINT_TAG, KMType.AUTH_TIMEOUT, |
| KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, |
| KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, |
| KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, |
| }; |
| 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 KMKeyParameters prototype; |
| |
| private KMKeyParameters() {} |
| |
| private static KMKeyParameters proto(short ptr) { |
| if (prototype == null) { |
| prototype = new KMKeyParameters(); |
| } |
| KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr; |
| return prototype; |
| } |
| |
| public static short exp() { |
| short arrPtr = KMArray.instance((short) 11); |
| KMArray arr = KMArray.cast(arrPtr); |
| arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.FAIL_ON_INVALID_TAGS)); |
| arr.add((short) 1, KMIntegerTag.exp(UINT_TAG)); |
| arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); |
| arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG)); |
| arr.add((short) 4, KMIntegerTag.exp(DATE_TAG)); |
| arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); |
| arr.add((short) 6, KMEnumTag.exp()); |
| arr.add((short) 7, KMEnumArrayTag.exp()); |
| arr.add((short) 8, KMByteTag.exp()); |
| arr.add((short) 9, KMBoolTag.exp()); |
| arr.add((short) 10, KMBignumTag.exp()); |
| return instance(arrPtr); |
| } |
| |
| public static short expAny() { |
| short arrPtr = KMArray.instance((short) 11); |
| KMArray arr = KMArray.cast(arrPtr); |
| arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.IGNORE_INVALID_TAGS)); |
| arr.add((short) 1, KMIntegerTag.exp(UINT_TAG)); |
| arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); |
| arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG)); |
| arr.add((short) 4, KMIntegerTag.exp(DATE_TAG)); |
| arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); |
| arr.add((short) 6, KMEnumTag.exp()); |
| arr.add((short) 7, KMEnumArrayTag.exp()); |
| arr.add((short) 8, KMByteTag.exp()); |
| arr.add((short) 9, KMBoolTag.exp()); |
| arr.add((short) 10, KMBignumTag.exp()); |
| return instance(arrPtr); |
| } |
| |
| public static short instance(short vals) { |
| short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2); |
| Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); |
| return ptr; |
| } |
| |
| public static KMKeyParameters cast(short ptr) { |
| if (heap[ptr] != KEY_PARAM_TYPE) { |
| ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); |
| } |
| short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); |
| if (heap[arrPtr] != ARRAY_TYPE) { |
| ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); |
| } |
| return proto(ptr); |
| } |
| |
| public static short findTag(short tagType, short tagKey, short keyParam) { |
| KMKeyParameters instParam = KMKeyParameters.cast(keyParam); |
| return instParam.findTag(tagType, tagKey); |
| } |
| |
| public static boolean hasUnsupportedTags(short keyParamsPtr) { |
| byte index = 0; |
| short tagInd; |
| short tagPtr; |
| short tagKey; |
| short tagType; |
| short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); |
| short len = KMArray.cast(arrPtr).length(); |
| while (index < len) { |
| tagInd = 0; |
| 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)) { |
| return true; |
| } |
| tagInd += 2; |
| } |
| index++; |
| } |
| return false; |
| } |
| |
| // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal |
| public static short makeSbEnforced( |
| short keyParamsPtr, |
| byte origin, |
| short osVersionObjPtr, |
| short osPatchObjPtr, |
| short vendorPatchObjPtr, |
| short bootPatchObjPtr, |
| byte[] scratchPad) { |
| byte index = 0; |
| short tagInd; |
| short arrInd = 0; |
| short tagPtr; |
| short tagKey; |
| short tagType; |
| short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); |
| short len = KMArray.cast(arrPtr).length(); |
| while (index < len) { |
| tagInd = 0; |
| tagPtr = KMArray.cast(arrPtr).get(index); |
| tagKey = KMTag.getKey(tagPtr); |
| tagType = KMTag.getTagType(tagPtr); |
| if (!isValidTag(tagType, tagKey)) { |
| KMException.throwIt(KMError.INVALID_KEY_BLOB); |
| } |
| while (tagInd < (short) hwEnforcedTagArr.length) { |
| if ((hwEnforcedTagArr[tagInd] == tagType) |
| && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { |
| Util.setShort(scratchPad, arrInd, tagPtr); |
| arrInd += 2; |
| break; |
| } |
| tagInd += 2; |
| } |
| index++; |
| } |
| short originTag = KMEnumTag.instance(KMType.ORIGIN, origin); |
| Util.setShort(scratchPad, arrInd, originTag); |
| arrInd += 2; |
| short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr); |
| Util.setShort(scratchPad, arrInd, osVersionTag); |
| arrInd += 2; |
| short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); |
| Util.setShort(scratchPad, arrInd, osPatchTag); |
| arrInd += 2; |
| short vendorPatchTag = |
| KMIntegerTag.instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr); |
| Util.setShort(scratchPad, arrInd, vendorPatchTag); |
| arrInd += 2; |
| short bootPatchTag = |
| KMIntegerTag.instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr); |
| Util.setShort(scratchPad, arrInd, bootPatchTag); |
| arrInd += 2; |
| return createKeyParameters(scratchPad, (short) (arrInd / 2)); |
| } |
| |
| public static short makeHwEnforced(short sb, short tee) { |
| short len = KMKeyParameters.cast(sb).length(); |
| len += KMKeyParameters.cast(tee).length(); |
| short hwEnf = KMArray.instance(len); |
| sb = KMKeyParameters.cast(sb).getVals(); |
| tee = KMKeyParameters.cast(tee).getVals(); |
| len = KMArray.cast(sb).length(); |
| short src = 0; |
| short dest = 0; |
| short val = 0; |
| while (src < len) { |
| val = KMArray.cast(sb).get(src); |
| KMArray.cast(hwEnf).add(dest, val); |
| src++; |
| dest++; |
| } |
| src = 0; |
| len = KMArray.cast(tee).length(); |
| while (src < len) { |
| val = KMArray.cast(tee).get(src); |
| KMArray.cast(hwEnf).add(dest, val); |
| src++; |
| dest++; |
| } |
| return KMKeyParameters.instance(hwEnf); |
| } |
| |
| // ALL_USERS, EXPORTABLE missing from types.hal |
| public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) { |
| byte index = 0; |
| short tagInd; |
| short arrInd = 0; |
| short tagPtr; |
| short tagKey; |
| short tagType; |
| short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); |
| short len = KMArray.cast(arrPtr).length(); |
| while (index < len) { |
| tagInd = 0; |
| tagPtr = KMArray.cast(arrPtr).get(index); |
| tagKey = KMTag.getKey(tagPtr); |
| tagType = KMTag.getTagType(tagPtr); |
| if (!isValidTag(tagType, tagKey)) { |
| KMException.throwIt(KMError.INVALID_KEY_BLOB); |
| } |
| while (tagInd < (short) swEnforcedTagsArr.length) { |
| if ((swEnforcedTagsArr[tagInd] == tagType) |
| && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { |
| Util.setShort(scratchPad, arrInd, tagPtr); |
| arrInd += 2; |
| break; |
| } |
| tagInd += 2; |
| } |
| index++; |
| } |
| return createKeyParameters(scratchPad, (short) (arrInd / 2)); |
| } |
| |
| public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) { |
| byte index = 0; |
| short tagInd; |
| short arrInd = 0; |
| short tagPtr; |
| short tagKey; |
| short tagType; |
| short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); |
| short len = KMArray.cast(arrPtr).length(); |
| while (index < len) { |
| tagInd = 0; |
| tagPtr = KMArray.cast(arrPtr).get(index); |
| tagKey = KMTag.getKey(tagPtr); |
| tagType = KMTag.getTagType(tagPtr); |
| if (!isValidTag(tagType, tagKey)) { |
| KMException.throwIt(KMError.INVALID_KEY_BLOB); |
| } |
| while (tagInd < (short) teeEnforcedTagsArr.length) { |
| if ((teeEnforcedTagsArr[tagInd] == tagType) |
| && (teeEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { |
| Util.setShort(scratchPad, arrInd, tagPtr); |
| arrInd += 2; |
| break; |
| } |
| tagInd += 2; |
| } |
| index++; |
| } |
| return createKeyParameters(scratchPad, (short) (arrInd / 2)); |
| } |
| |
| public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { |
| short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); |
| if (appId != KMTag.INVALID_VALUE) { |
| appId = KMByteTag.cast(appId).getValue(); |
| if (KMByteBlob.cast(appId).length() == 0) { |
| appId = KMTag.INVALID_VALUE; |
| } |
| } |
| short appData = |
| KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); |
| if (appData != KMTag.INVALID_VALUE) { |
| appData = KMByteTag.cast(appData).getValue(); |
| if (KMByteBlob.cast(appData).length() == 0) { |
| appData = KMTag.INVALID_VALUE; |
| } |
| } |
| return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); |
| } |
| |
| public static short makeHidden( |
| short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad) { |
| // Order in which the hidden array is created should not change. |
| short index = 0; |
| KMByteBlob.cast(rootOfTrustBlob); |
| Util.setShort(scratchPad, index, rootOfTrustBlob); |
| index += 2; |
| if (appIdBlob != KMTag.INVALID_VALUE) { |
| KMByteBlob.cast(appIdBlob); |
| Util.setShort(scratchPad, index, appIdBlob); |
| index += 2; |
| } |
| if (appDataBlob != KMTag.INVALID_VALUE) { |
| KMByteBlob.cast(appDataBlob); |
| Util.setShort(scratchPad, index, appDataBlob); |
| index += 2; |
| } |
| return createKeyParameters(scratchPad, (short) (index / 2)); |
| } |
| |
| public static boolean isValidTag(short tagType, short tagKey) { |
| short index = 0; |
| if (tagKey == KMType.INVALID_TAG) { |
| return false; |
| } |
| while (index < invalidTagsArr.length) { |
| if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) { |
| return false; |
| } |
| index += 2; |
| } |
| return true; |
| } |
| |
| public static short createKeyParameters(byte[] ptrArr, short len) { |
| short arrPtr = KMArray.instance(len); |
| short index = 0; |
| short ptr = 0; |
| while (index < len) { |
| KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr)); |
| index++; |
| ptr += 2; |
| } |
| return KMKeyParameters.instance(arrPtr); |
| } |
| |
| public static short makeCustomTags(short keyParams, byte[] scratchPad) { |
| short index = 0; |
| short tagPtr; |
| short offset = 0; |
| 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 createKeyParameters(scratchPad, (short) (offset / 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))); |
| } |
| |
| public short getVals() { |
| return Util.getShort( |
| heap, (short) (KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); |
| } |
| |
| public short length() { |
| short arrPtr = getVals(); |
| return KMArray.cast(arrPtr).length(); |
| } |
| |
| public short findTag(short tagType, short tagKey) { |
| KMArray vals = KMArray.cast(getVals()); |
| short index = 0; |
| short length = vals.length(); |
| short key; |
| short type; |
| short ret = KMType.INVALID_VALUE; |
| short obj; |
| while (index < length) { |
| obj = vals.get(index); |
| key = KMTag.getKey(obj); |
| type = KMTag.getTagType(obj); |
| if ((tagKey == key) && (tagType == type)) { |
| ret = obj; |
| break; |
| } |
| index++; |
| } |
| return ret; |
| } |
| } |