aboutsummaryrefslogtreecommitdiff
path: root/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java')
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java1055
1 files changed, 1055 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
new file mode 100644
index 0000000..e7d8252
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
@@ -0,0 +1,1055 @@
+/*
+ * 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 com.android.javacard.seprovider.KMAESKey;
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * The class encodes strongbox generated and signed attestation certificates. It only encodes the
+ * required fields of the certificates. This class is not meant to be a generic X509 cert encoder.
+ * Any fields that are fixed are added as byte arrays. Extensions are encoded as per the values. The
+ * certificate is assembled with leafs first and then the sequences.
+ */
+public class KMAttestationCertImpl implements KMAttestationCert {
+
+ // The maximum size of the either software or hardware parameters.
+ private static final byte MAX_PARAMS = 30;
+ // DER encoded object identifiers required by the cert.
+ // rsaEncryption - 1.2.840.113549.1.1.1
+ private static final byte[] rsaEncryption = {
+ 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01
+ };
+ // ecPublicKey - 1.2.840.10045.2.1
+ private static final byte[] eccPubKey = {
+ 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01
+ };
+ // prime256v1 curve - 1.2.840.10045.3.1.7
+ private static final byte[] prime256v1 = {
+ 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07
+ };
+ // Key Usage Extn - 2.5.29.15
+ private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F};
+ // Android Extn - 1.3.6.1.4.1.11129.2.1.17
+ private static final byte[] androidExtn = {
+ 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11
+ };
+ // The length of the RSA signature.
+ private static final short RSA_SIG_LEN = 256;
+ // The maximum length of the ECDSA signature.
+ private static final byte ECDSA_MAX_SIG_LEN = 72;
+ // Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2
+ // SEQUENCE of alg OBJ ID and parameters = NULL.
+ private static final byte[] X509EcdsaSignAlgIdentifier = {
+ 0x30, 0x0A, 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, (byte) 0x3D, 0x04, 0x03, 0x02
+ };
+ // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11
+ // SEQUENCE of alg OBJ ID and parameters = NULL.
+ private static final byte[] X509RsaSignAlgIdentifier = {
+ 0x30,
+ 0x0D,
+ 0x06,
+ 0x09,
+ 0x2A,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0x86,
+ (byte) 0xF7,
+ 0x0D,
+ 0x01,
+ 0x01,
+ 0x0B,
+ 0x05,
+ 0x00
+ };
+
+ // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's
+ // extension.
+ private static final short[] swTagIds = {
+ KMType.ATTESTATION_APPLICATION_ID,
+ KMType.CREATION_DATETIME,
+ KMType.ALLOW_WHILE_ON_BODY,
+ KMType.USAGE_COUNT_LIMIT,
+ KMType.USAGE_EXPIRE_DATETIME,
+ KMType.ORIGINATION_EXPIRE_DATETIME,
+ KMType.ACTIVE_DATETIME,
+ };
+
+ // 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.UNLOCKED_DEVICE_REQUIRED,
+ KMType.TRUSTED_CONFIRMATION_REQUIRED,
+ KMType.AUTH_TIMEOUT,
+ KMType.USER_AUTH_TYPE,
+ KMType.NO_AUTH_REQUIRED,
+ KMType.EARLY_BOOT_ONLY,
+ KMType.ROLLBACK_RESISTANCE,
+ KMType.RSA_OAEP_MGF_DIGEST,
+ KMType.RSA_PUBLIC_EXPONENT,
+ KMType.ECCURVE,
+ KMType.PADDING,
+ KMType.DIGEST,
+ KMType.KEYSIZE,
+ KMType.ALGORITHM,
+ KMType.PURPOSE
+ };
+ // Below are the constants for the key usage extension.
+ private static final byte keyUsageSign = (byte) 0x80; // 0 bit
+ private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit
+ private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit
+ private static final byte keyUsageKeyAgreement = (byte) 0x08; // 4th- bit
+ private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit
+ // KeyMint HAL Version constant.
+ private static final short KEYMINT_VERSION = 200;
+ // Attestation version constant.
+ private static final short ATTESTATION_VERSION = 200;
+ // The X.509 version as per rfc5280#section-4.1.2.1
+ private static final byte X509_VERSION = (byte) 0x02;
+
+ // Buffer indexes in transient array
+ private static final byte NUM_INDEX_ENTRIES = 21;
+ private static final byte CERT_START = (byte) 0;
+ private static final byte CERT_LENGTH = (byte) 1;
+ private static final byte TBS_START = (byte) 2;
+ private static final byte TBS_LENGTH = (byte) 3;
+ private static final byte BUF_START = (byte) 4;
+ private static final byte BUF_LENGTH = (byte) 5;
+ private static final byte SW_PARAM_INDEX = (byte) 6;
+ private static final byte HW_PARAM_INDEX = (byte) 7;
+ // Data indexes in transient array
+ private static final byte STACK_PTR = (byte) 8;
+ private static final byte UNIQUE_ID = (byte) 9;
+ private static final byte ATT_CHALLENGE = (byte) 10;
+ private static final byte NOT_BEFORE = (byte) 11;
+ private static final byte NOT_AFTER = (byte) 12;
+ private static final byte PUB_KEY = (byte) 13;
+ private static final byte VERIFIED_BOOT_KEY = (byte) 14;
+ private static final byte VERIFIED_HASH = (byte) 15;
+ private static final byte ISSUER = (byte) 16;
+ private static final byte SUBJECT_NAME = (byte) 17;
+ private static final byte SERIAL_NUMBER = (byte) 18;
+ private static final byte CERT_ATT_KEY_SECRET = (byte) 19;
+ private static final byte CERT_ATT_KEY_RSA_PUB_MOD = (byte) 20;
+ // State indexes in transient array
+ private static final byte NUM_STATE_ENTRIES = 7;
+ private static final byte KEY_USAGE = (byte) 0;
+ private static final byte UNUSED_BITS = (byte) 1;
+ private static final byte DEVICE_LOCKED = (byte) 2;
+ private static final byte VERIFIED_STATE = (byte) 3;
+ private static final byte CERT_MODE = (byte) 4;
+ private static final byte RSA_CERT = (byte) 5;
+ private static final byte CERT_RSA_SIGN = (byte) 6;
+
+ private static KMAttestationCert inst;
+ private static KMSEProvider seProvider;
+
+ private static short[] indexes;
+ private static byte[] states;
+
+ private static byte[] stack;
+ private static short[] swParams;
+ private static short[] hwParams;
+ // The maximum size of the serial number.
+ private static final byte SERIAL_NUM_MAX_LEN = 20;
+
+ private KMAttestationCertImpl() {}
+
+ public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) {
+ if (inst == null) {
+ inst = new KMAttestationCertImpl();
+ seProvider = provider;
+
+ // Allocate transient memory
+ indexes = JCSystem.makeTransientShortArray(NUM_INDEX_ENTRIES, JCSystem.CLEAR_ON_RESET);
+ states = JCSystem.makeTransientByteArray(NUM_STATE_ENTRIES, JCSystem.CLEAR_ON_RESET);
+ swParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+ hwParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+ }
+ init(rsaCert);
+ return inst;
+ }
+
+ private static void init(boolean rsaCert) {
+ for (short i = 0; i < NUM_INDEX_ENTRIES; i++) {
+ indexes[i] = 0;
+ }
+ Util.arrayFillNonAtomic(states, (short) 0, NUM_STATE_ENTRIES, (byte) 0);
+ stack = null;
+ states[CERT_MODE] = KMType.NO_CERT;
+ states[UNUSED_BITS] = 8;
+ states[RSA_CERT] = rsaCert ? (byte) 1 : (byte) 0;
+ states[CERT_RSA_SIGN] = 1;
+ indexes[CERT_ATT_KEY_SECRET] = KMType.INVALID_VALUE;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+ indexes[ISSUER] = KMType.INVALID_VALUE;
+ indexes[SUBJECT_NAME] = KMType.INVALID_VALUE;
+ indexes[SERIAL_NUMBER] = KMType.INVALID_VALUE;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootHash(short obj) {
+ indexes[VERIFIED_HASH] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootKey(short obj) {
+ indexes[VERIFIED_BOOT_KEY] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootState(byte val) {
+ states[VERIFIED_STATE] = val;
+ return this;
+ }
+
+ private KMAttestationCert uniqueId(short obj) {
+ indexes[UNIQUE_ID] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) {
+ if (!derEncoded) {
+ // convert milliseconds to UTC date
+ indexes[NOT_BEFORE] = KMUtils.convertToDate(obj, scratchpad, true);
+ } else {
+ indexes[NOT_BEFORE] =
+ KMByteBlob.instance(
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert notAfter(
+ short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) {
+ if (!derEncoded) {
+ if (usageExpiryTimeObj != KMType.INVALID_VALUE) {
+ // compare if the expiry time is greater then 2050 then use generalized
+ // time format else use utc time format.
+ short tmpVar = KMInteger.uint_64(KMUtils.firstJan2050, (short) 0);
+ if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) {
+ usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, false);
+ } else {
+ usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, true);
+ }
+ indexes[NOT_AFTER] = usageExpiryTimeObj;
+ } else {
+ // notAfter = certExpirtyTimeObj;
+ }
+ } else {
+ indexes[NOT_AFTER] = usageExpiryTimeObj;
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert deviceLocked(boolean val) {
+ if (val) {
+ states[DEVICE_LOCKED] = (byte) 0xFF;
+ } else {
+ states[DEVICE_LOCKED] = 0;
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert publicKey(short obj) {
+ indexes[PUB_KEY] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert attestationChallenge(short obj) {
+ indexes[ATT_CHALLENGE] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) {
+ if (hwEnforced) {
+ hwParams[indexes[HW_PARAM_INDEX]] = tag;
+ indexes[HW_PARAM_INDEX]++;
+ } else {
+ swParams[indexes[SW_PARAM_INDEX]] = tag;
+ indexes[SW_PARAM_INDEX]++;
+ }
+ if (KMTag.getKey(tag) == KMType.PURPOSE) {
+ createKeyUsage(tag);
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert issuer(short obj) {
+ indexes[ISSUER] = obj;
+ return this;
+ }
+
+ private void createKeyUsage(short tag) {
+ short len = KMEnumArrayTag.cast(tag).length();
+ byte index = 0;
+ while (index < len) {
+ if (KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageSign);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyEncipher);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageDataEncipher);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyAgreement);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageCertSign);
+ }
+ index++;
+ }
+ index = states[KEY_USAGE];
+ while (index != 0) {
+ index = (byte) (index << 1);
+ states[UNUSED_BITS]--;
+ }
+ }
+
+ private static void pushTbsCert(boolean rsaCert, boolean rsa) {
+ short last = indexes[STACK_PTR];
+ pushExtensions();
+ // subject public key info
+ if (rsaCert) {
+ pushRsaSubjectKeyInfo();
+ } else {
+ pushEccSubjectKeyInfo();
+ }
+ // subject
+ pushBytes(
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).getBuffer(),
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).getStartOff(),
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).length());
+ pushValidity();
+ // issuer - der encoded
+ pushBytes(
+ KMByteBlob.cast(indexes[ISSUER]).getBuffer(),
+ KMByteBlob.cast(indexes[ISSUER]).getStartOff(),
+ KMByteBlob.cast(indexes[ISSUER]).length());
+ // Algorithm Id
+ if (rsa) {
+ pushAlgorithmId(X509RsaSignAlgIdentifier);
+ } else {
+ pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+ }
+ // Serial Number
+ pushBytes(
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).getBuffer(),
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).getStartOff(),
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+ pushIntegerHeader(KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+ // Version
+ pushByte(X509_VERSION);
+ pushIntegerHeader((short) 1);
+ pushByte((byte) 0x03);
+ pushByte((byte) 0xA0);
+ // Finally sequence header.
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushExtensions() {
+ short last = indexes[STACK_PTR];
+ // Push KeyUsage extension
+ if (states[KEY_USAGE] != 0) {
+ pushKeyUsage(states[KEY_USAGE], states[UNUSED_BITS]);
+ }
+ if (states[CERT_MODE] == KMType.ATTESTATION_CERT) {
+ pushKeyDescription();
+ }
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ // Extensions have explicit tag of [3]
+ pushLength((short) (last - indexes[STACK_PTR]));
+ pushByte((byte) 0xA3);
+ }
+
+ // Time SEQUENCE{UTCTime, UTC or Generalized Time)
+ private static void pushValidity() {
+ short last = indexes[STACK_PTR];
+ if (indexes[NOT_AFTER] != 0) {
+ pushBytes(
+ KMByteBlob.cast(indexes[NOT_AFTER]).getBuffer(),
+ KMByteBlob.cast(indexes[NOT_AFTER]).getStartOff(),
+ KMByteBlob.cast(indexes[NOT_AFTER]).length());
+ } else {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ pushTimeHeader(KMByteBlob.cast(indexes[NOT_AFTER]).length());
+ pushBytes(
+ KMByteBlob.cast(indexes[NOT_BEFORE]).getBuffer(),
+ KMByteBlob.cast(indexes[NOT_BEFORE]).getStartOff(),
+ KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+ pushTimeHeader(KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushTimeHeader(short len) {
+ if (len == 13) { // UTC Time
+ pushLength((short) 0x0D);
+ pushByte((byte) 0x17);
+ } else if (len == 15) { // Generalized Time
+ pushLength((short) 0x0F);
+ pushByte((byte) 0x18);
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ }
+
+ // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public
+ // exponent
+ // as positive integer}
+ private static void pushRsaSubjectKeyInfo() {
+ short last = indexes[STACK_PTR];
+ pushBytes(KMKeymasterApplet.F4, (short) 0, (short) KMKeymasterApplet.F4.length);
+ pushIntegerHeader((short) KMKeymasterApplet.F4.length);
+ pushBytes(
+ KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[PUB_KEY]).length());
+
+ // encode modulus as positive if the MSB is 1.
+ if (KMByteBlob.cast(indexes[PUB_KEY]).get((short) 0) < 0) {
+ pushByte((byte) 0x00);
+ pushIntegerHeader((short) (KMByteBlob.cast(indexes[PUB_KEY]).length() + 1));
+ } else {
+ pushIntegerHeader(KMByteBlob.cast(indexes[PUB_KEY]).length());
+ }
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ pushBitStringHeader((byte) 0x00, (short) (last - indexes[STACK_PTR]));
+ pushRsaEncryption();
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}}
+ private static void pushEccSubjectKeyInfo() {
+ short last = indexes[STACK_PTR];
+ pushBytes(
+ KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[PUB_KEY]).length());
+ pushBitStringHeader((byte) 0x00, KMByteBlob.cast(indexes[PUB_KEY]).length());
+ pushEcDsa();
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushEcDsa() {
+ short last = indexes[STACK_PTR];
+ pushBytes(prime256v1, (short) 0, (short) prime256v1.length);
+ pushBytes(eccPubKey, (short) 0, (short) eccPubKey.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushRsaEncryption() {
+ short last = indexes[STACK_PTR];
+ pushNullHeader();
+ pushBytes(rsaEncryption, (short) 0, (short) rsaEncryption.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // KeyDescription ::= SEQUENCE {
+ // attestationVersion INTEGER, # Value 200
+ // attestationSecurityLevel SecurityLevel, # See below
+ // keymasterVersion INTEGER, # Value 200
+ // keymasterSecurityLevel SecurityLevel, # See below
+ // attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+ // uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+ // softwareEnforced AuthorizationList, # See below
+ // hardwareEnforced AuthorizationList, # See below
+ // }
+ private static void pushKeyDescription() {
+ short last = indexes[STACK_PTR];
+ pushHWParams();
+ pushSWParams();
+ if (indexes[UNIQUE_ID] != 0) {
+ pushOctetString(
+ KMByteBlob.cast(indexes[UNIQUE_ID]).getBuffer(),
+ KMByteBlob.cast(indexes[UNIQUE_ID]).getStartOff(),
+ KMByteBlob.cast(indexes[UNIQUE_ID]).length());
+ } else {
+ pushOctetStringHeader((short) 0);
+ }
+ pushOctetString(
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).getBuffer(),
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).getStartOff(),
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).length());
+ pushEnumerated(KMType.STRONGBOX);
+ pushShort(KEYMINT_VERSION);
+ pushIntegerHeader((short) 2);
+ pushEnumerated(KMType.STRONGBOX);
+ pushShort(ATTESTATION_VERSION);
+ pushIntegerHeader((short) 2);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushBytes(androidExtn, (short) 0, (short) androidExtn.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushSWParams() {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ short length = (short) swTagIds.length;
+ do {
+ pushParams(swParams, indexes[SW_PARAM_INDEX], swTagIds[index]);
+ } while (++index < length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushHWParams() {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ short length = (short) hwTagIds.length;
+ do {
+ if (hwTagIds[index] == KMType.ROOT_OF_TRUST) {
+ pushRoT();
+ continue;
+ }
+ if (pushParams(hwParams, indexes[HW_PARAM_INDEX], hwTagIds[index])) {
+ continue;
+ }
+ } while (++index < length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static boolean pushParams(short[] params, short len, short tagId) {
+ short index = 0;
+ while (index < len) {
+ if (tagId == KMTag.getKey(params[index])) {
+ pushTag(params[index]);
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ private static void pushTag(short tag) {
+ short type = KMTag.getTagType(tag);
+ short tagId = KMTag.getKey(tag);
+ short val;
+ switch (type) {
+ case KMType.BYTES_TAG:
+ val = KMByteTag.cast(tag).getValue();
+ pushBytesTag(
+ tagId,
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ break;
+ case KMType.ENUM_TAG:
+ val = KMEnumTag.cast(tag).getValue();
+ pushEnumTag(tagId, (byte) val);
+ break;
+ case KMType.ENUM_ARRAY_TAG:
+ val = KMEnumArrayTag.cast(tag).getValues();
+ pushEnumArrayTag(
+ tagId,
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ break;
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ val = KMIntegerTag.cast(tag).getValue();
+ pushIntegerTag(
+ tagId,
+ KMInteger.cast(val).getBuffer(),
+ KMInteger.cast(val).getStartOff(),
+ KMInteger.cast(val).length());
+ break;
+ case KMType.UINT_ARRAY_TAG:
+ case KMType.ULONG_ARRAY_TAG:
+ // According to KeyMint hal only one user secure id is used but this conflicts with
+ // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS
+ val = KMIntegerArrayTag.cast(tag).getValues();
+ pushIntegerArrayTag(tagId, val);
+ break;
+ case KMType.BOOL_TAG:
+ val = KMBoolTag.cast(tag).getVal();
+ pushBoolTag(tagId);
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_TAG);
+ break;
+ }
+ }
+
+ // RootOfTrust ::= SEQUENCE {
+ // verifiedBootKey OCTET_STRING,
+ // deviceLocked BOOLEAN,
+ // verifiedBootState VerifiedBootState,
+ // verifiedBootHash OCTET_STRING,
+ // }
+ // VerifiedBootState ::= ENUMERATED {
+ // Verified (0),
+ // SelfSigned (1),
+ // Unverified (2),
+ // Failed (3),
+ // }
+ private static void pushRoT() {
+ short last = indexes[STACK_PTR];
+ // verified boot hash
+ pushOctetString(
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).getBuffer(),
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).getStartOff(),
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).length());
+
+ pushEnumerated(states[VERIFIED_STATE]);
+
+ pushBoolean(states[DEVICE_LOCKED]);
+ // verified boot Key
+ pushOctetString(
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).length());
+
+ // Finally sequence header
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ // ... and tag Id
+ pushTagIdHeader(KMType.ROOT_OF_TRUST, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushOctetString(byte[] buf, short start, short len) {
+ pushBytes(buf, start, len);
+ pushOctetStringHeader(len);
+ }
+
+ private static void pushBoolean(byte val) {
+ pushByte(val);
+ pushBooleanHeader((short) 1);
+ }
+
+ private static void pushBooleanHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x01);
+ }
+
+ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+ // All of these are enum array tags i.e. byte long values
+ private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ short index = 0;
+ while (index < len) {
+ pushByte(buf[(short) (start + index)]);
+ pushIntegerHeader((short) 1);
+ index++;
+ }
+ pushSetHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+ // All of these are enum array tags i.e. byte long values
+ private static void pushIntegerArrayTag(short tagId, short arr) {
+ short last = indexes[STACK_PTR];
+ short index = 0;
+ short len = KMArray.cast(arr).length();
+ short ptr;
+ while (index < len) {
+ ptr = KMArray.cast(arr).get(index);
+ pushInteger(
+ KMInteger.cast(ptr).getBuffer(),
+ KMInteger.cast(ptr).getStartOff(),
+ KMInteger.cast(ptr).length());
+ index++;
+ }
+ pushSetHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushSetHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x31);
+ }
+
+ private static void pushEnumerated(byte val) {
+ short last = indexes[STACK_PTR];
+ pushByte(val);
+ pushEnumeratedHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushEnumeratedHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x0A);
+ }
+
+ private static void pushBoolTag(short tagId) {
+ short last = indexes[STACK_PTR];
+ pushNullHeader();
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushNullHeader() {
+ pushByte((byte) 0);
+ pushByte((byte) 0x05);
+ }
+
+ private static void pushEnumTag(short tagId, byte val) {
+ short last = indexes[STACK_PTR];
+ pushByte(val);
+ pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ pushInteger(buf, start, len);
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00
+ // as most significant byte.
+ private static void pushInteger(byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ while (index < (byte) len) {
+ if (buf[(short) (start + index)] != 0) {
+ break;
+ }
+ index++;
+ }
+ if (index == (byte) len) {
+ pushByte((byte) 0x00);
+ } else {
+ pushBytes(buf, (short) (start + index), (short) (len - index));
+ if (buf[(short) (start + index)] < 0) { // MSB is 1
+ pushByte((byte) 0x00); // always unsigned int
+ }
+ }
+ pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // Bytes Tag is a octet string and tag id is added explicitly
+ private static void pushBytesTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ pushBytes(buf, start, len);
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // tag id <= 30 ---> 0xA0 | {tagId}
+ // 30 < tagId < 128 ---> 0xBF 0x{tagId}
+ // tagId >= 128 ---> 0xBF 0x80+(tagId/128) 0x{tagId - (128*(tagId/128))}
+ private static void pushTagIdHeader(short tagId, short len) {
+ pushLength(len);
+ short count = (short) (tagId / 128);
+ if (count > 0) {
+ pushByte((byte) (tagId - (128 * count)));
+ pushByte((byte) (0x80 + count));
+ pushByte((byte) 0xBF);
+ } else if (tagId > 30) {
+ pushByte((byte) tagId);
+ pushByte((byte) 0xBF);
+ } else {
+ pushByte((byte) (0xA0 | (byte) tagId));
+ }
+ }
+
+ // SEQUENCE {ObjId, OCTET STRING{BIT STRING{keyUsage}}}
+ private static void pushKeyUsage(byte keyUsage, byte unusedBits) {
+ short last = indexes[STACK_PTR];
+ pushByte(keyUsage);
+ pushBitStringHeader(unusedBits, (short) (last - indexes[STACK_PTR]));
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushBytes(keyUsageExtn, (short) 0, (short) keyUsageExtn.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushAlgorithmId(byte[] algId) {
+ pushBytes(algId, (short) 0, (short) algId.length);
+ }
+
+ private static void pushIntegerHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x02);
+ }
+
+ private static void pushOctetStringHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x04);
+ }
+
+ private static void pushSequenceHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x30);
+ }
+
+ private static void pushBitStringHeader(byte unusedBits, short len) {
+ pushByte(unusedBits);
+ pushLength((short) (len + 1)); // 1 extra byte for unused bits byte
+ pushByte((byte) 0x03);
+ }
+
+ private static void pushLength(short len) {
+ if (len < 128) {
+ pushByte((byte) len);
+ } else if (len < 256) {
+ pushByte((byte) len);
+ pushByte((byte) 0x81);
+ } else {
+ pushShort(len);
+ pushByte((byte) 0x82);
+ }
+ }
+
+ private static void pushShort(short val) {
+ decrementStackPtr((short) 2);
+ Util.setShort(stack, indexes[STACK_PTR], val);
+ }
+
+ private static void pushByte(byte val) {
+ decrementStackPtr((short) 1);
+ stack[indexes[STACK_PTR]] = val;
+ }
+
+ private static void pushBytes(byte[] buf, short start, short len) {
+ decrementStackPtr(len);
+ if (buf != null) {
+ Util.arrayCopyNonAtomic(buf, start, stack, indexes[STACK_PTR], len);
+ }
+ }
+
+ private static void decrementStackPtr(short cnt) {
+ indexes[STACK_PTR] = (short) (indexes[STACK_PTR] - cnt);
+ if (indexes[BUF_START] > indexes[STACK_PTR]) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+
+ @Override
+ public KMAttestationCert buffer(byte[] buf, short start, short maxLen) {
+ stack = buf;
+ indexes[BUF_START] = start;
+ indexes[BUF_LENGTH] = maxLen;
+ indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+ return this;
+ }
+
+ @Override
+ public short getCertStart() {
+ return indexes[CERT_START];
+ }
+
+ @Override
+ public short getCertLength() {
+ return indexes[CERT_LENGTH];
+ }
+
+ public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) {
+ indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+ short last = indexes[STACK_PTR];
+ short sigLen = 0;
+ if (fakeCert) {
+ rsaSign = true;
+ pushByte((byte) 0);
+ sigLen = 1;
+ }
+ // Push placeholder signature Bit string header
+ // This will potentially change at the end
+ else if (rsaSign) {
+ decrementStackPtr(RSA_SIG_LEN);
+ } else {
+ decrementStackPtr(ECDSA_MAX_SIG_LEN);
+ }
+ short signatureOffset = indexes[STACK_PTR];
+ pushBitStringHeader((byte) 0, (short) (last - indexes[STACK_PTR]));
+ if (rsaSign) {
+ pushAlgorithmId(X509RsaSignAlgIdentifier);
+ } else {
+ pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+ }
+ indexes[TBS_LENGTH] = indexes[STACK_PTR];
+ pushTbsCert((states[RSA_CERT] == 0 ? false : true), rsaSign);
+ indexes[TBS_START] = indexes[STACK_PTR];
+ indexes[TBS_LENGTH] = (short) (indexes[TBS_LENGTH] - indexes[TBS_START]);
+ if (attSecret != KMType.INVALID_VALUE) {
+ // Sign with the attestation key
+ // The pubKey is the modulus.
+ if (rsaSign) {
+ sigLen =
+ seProvider.rsaSign256Pkcs1(
+ KMByteBlob.cast(attSecret).getBuffer(),
+ KMByteBlob.cast(attSecret).getStartOff(),
+ KMByteBlob.cast(attSecret).length(),
+ KMByteBlob.cast(attMod).getBuffer(),
+ KMByteBlob.cast(attMod).getStartOff(),
+ KMByteBlob.cast(attMod).length(),
+ stack,
+ indexes[TBS_START],
+ indexes[TBS_LENGTH],
+ stack,
+ signatureOffset);
+ if (sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+ } else {
+ sigLen =
+ seProvider.ecSign256(
+ KMByteBlob.cast(attSecret).getBuffer(),
+ KMByteBlob.cast(attSecret).getStartOff(),
+ KMByteBlob.cast(attSecret).length(),
+ stack,
+ indexes[TBS_START],
+ indexes[TBS_LENGTH],
+ stack,
+ signatureOffset);
+ if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // Adjust signature length
+ indexes[STACK_PTR] = signatureOffset;
+ pushBitStringHeader((byte) 0, sigLen);
+ } else if (!fakeCert) { // No attestation key provisioned in the factory
+ KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+ }
+ last = (short) (signatureOffset + sigLen);
+ // Add certificate sequence header
+ indexes[STACK_PTR] = indexes[TBS_START];
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ indexes[CERT_START] = indexes[STACK_PTR];
+ indexes[CERT_LENGTH] = (short) (last - indexes[CERT_START]);
+ }
+
+ @Override
+ public void build() {
+ if (states[CERT_MODE] == KMType.FAKE_CERT) {
+ build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true);
+ } else {
+ build(
+ indexes[CERT_ATT_KEY_SECRET],
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD],
+ (states[CERT_RSA_SIGN] == 0 ? false : true),
+ false);
+ }
+ }
+
+ @Override
+ public KMAttestationCert makeUniqueId(
+ byte[] scratchPad,
+ short scratchPadOff,
+ byte[] creationTime,
+ short timeOffset,
+ short creationTimeLen,
+ byte[] attestAppId,
+ short appIdOff,
+ short attestAppIdLen,
+ byte resetSinceIdRotation,
+ KMKey masterKey) {
+ // Concatenate T||C||R
+ // temporal count T
+ short temp =
+ KMUtils.countTemporalCount(
+ creationTime, timeOffset, creationTimeLen, scratchPad, scratchPadOff);
+ Util.setShort(scratchPad, (short) scratchPadOff, temp);
+ temp = scratchPadOff;
+ scratchPadOff += 2;
+
+ // Application Id C
+ Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, attestAppIdLen);
+ scratchPadOff += attestAppIdLen;
+
+ // Reset After Rotation R
+ scratchPad[scratchPadOff] = resetSinceIdRotation;
+ scratchPadOff++;
+
+ // Get the key data from the master key
+ KMAESKey aesKey = (KMAESKey) masterKey;
+ short mKeyData = KMByteBlob.instance((short) (aesKey.aesKey.getSize() / 8));
+ aesKey.aesKey.getKey(
+ KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+ KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/
+ timeOffset = KMByteBlob.instance((short) 32);
+ appIdOff =
+ seProvider.hmacSign(
+ KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+ KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/
+ KMByteBlob.cast(mKeyData).length(), /* Key length*/
+ scratchPad, /* data */
+ temp, /* data start */
+ scratchPadOff, /* data length */
+ KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */
+ KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */
+ if (appIdOff != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return uniqueId(timeOffset);
+ }
+
+ @Override
+ public boolean serialNumber(short number) {
+ // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2
+ short length = KMByteBlob.cast(number).length();
+ if (length > SERIAL_NUM_MAX_LEN) {
+ return false;
+ }
+ // The serial number Must be a positive integer.
+ byte msb = KMByteBlob.cast(number).get((short) 0);
+ if (msb < 0 && length > (SERIAL_NUM_MAX_LEN - 1)) {
+ return false;
+ }
+ indexes[SERIAL_NUMBER] = number;
+ return true;
+ }
+
+ @Override
+ public boolean subjectName(short sub) {
+ if (sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 0) return false;
+ indexes[SUBJECT_NAME] = sub;
+ return true;
+ }
+
+ @Override
+ public KMAttestationCert ecAttestKey(short attestKey, byte mode) {
+ states[CERT_MODE] = mode;
+ indexes[CERT_ATT_KEY_SECRET] = attestKey;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+ states[CERT_RSA_SIGN] = 0;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode) {
+ states[CERT_MODE] = mode;
+ indexes[CERT_ATT_KEY_SECRET] = attestPrivExp;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = attestMod;
+ states[CERT_RSA_SIGN] = 1;
+ return this;
+ }
+}