Merge "Support uint16/uint32 for ENUM Tag." into main
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
index dd05b13..406f707 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
@@ -483,31 +483,51 @@
     return arrPtr;
   }
 
-  private short decodeEnumTag(short exp) {
-    readTagKey(KMEnumTag.cast(exp).getTagType());
+  private short createInstanceEnumType(short instanceType, byte[] buf, short offset, short len) {
+    byte type = KMType.getType(instanceType);
+    short ptr = KMType.INVALID_VALUE;
+    switch (type) {
+      case KMType.ENUM_TYPE:
+        ptr = KMEnum.instance(KMEnum.cast(instanceType).getEnumType(), buf, offset, len);
+        break;
+      case KMType.TAG_TYPE:
+        ptr = KMEnumTag.instance(scratchBuf[TAG_KEY_OFFSET], buf, offset, len);
+        break;
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    return ptr;
+  }
+
+  private short decodeEnumAndCreateInstance(short instanceType) {
     byte[] buffer = (byte[]) bufferRef[0];
-    short startOff = scratchBuf[START_OFFSET];
-    // Enum Tag value will always be integer with max 1 byte length.
-    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+    short offset = scratchBuf[START_OFFSET];
+    if ((buffer[offset] & MAJOR_TYPE_MASK) != UINT_TYPE) {
       ISOException.throwIt(ISO7816.SW_DATA_INVALID);
     }
-    short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
-    byte enumVal = 0;
-    if (len > UINT8_LENGTH) {
+    short addInfo = (short) (buffer[offset] & ADDITIONAL_MASK);
+    if (addInfo > UINT32_LENGTH) {
       ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
     }
-    if (len < UINT8_LENGTH) {
-      enumVal = (byte) (len & ADDITIONAL_MASK);
+    if (addInfo >= UINT8_LENGTH) {
       incrementStartOff((short) 1);
-    } else if (len == UINT8_LENGTH) {
-      incrementStartOff((short) 1);
-      // startOff  is incremented so update the startOff
-      // with latest value before using it.
-      startOff = scratchBuf[START_OFFSET];
-      enumVal = buffer[startOff];
-      incrementStartOff((short) 1);
+      offset = scratchBuf[START_OFFSET];
     }
-    return KMEnumTag.instance(scratchBuf[TAG_KEY_OFFSET], enumVal);
+    byte originalValue = buffer[offset];
+    if (addInfo < UINT8_LENGTH) {
+      // In this case additional info is the actual enum value.
+      buffer[offset] = (byte) addInfo;
+    }
+    short len = (short) ((addInfo > UINT8_LENGTH) ? ((addInfo == UINT32_LENGTH) ? 4 : 2) : 1);
+    short ptr = createInstanceEnumType(instanceType, buffer, offset, len);
+    buffer[offset] = originalValue;
+    incrementStartOff(len);
+    return ptr;
+  }
+
+  private short decodeEnumTag(short exp) {
+    readTagKey(KMEnumTag.cast(exp).getTagType());
+    return decodeEnumAndCreateInstance(exp);
   }
 
   private short decodeBoolTag(short exp) {
@@ -526,29 +546,7 @@
   }
 
   private short decodeEnum(short exp) {
-    byte[] buffer = (byte[]) bufferRef[0];
-    short startOff = scratchBuf[START_OFFSET];
-    // Enum value will always be integer with max 1 byte length.
-    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
-      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
-    }
-    short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
-    byte enumVal;
-    if (len > UINT8_LENGTH) {
-      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
-    }
-    if (len < UINT8_LENGTH) {
-      enumVal = (byte) (len & ADDITIONAL_MASK);
-      incrementStartOff((short) 1);
-    } else {
-      incrementStartOff((short) 1);
-      // startOff  is incremented so update the startOff
-      // with latest value before using it.
-      startOff = scratchBuf[START_OFFSET];
-      enumVal = buffer[startOff];
-      incrementStartOff((short) 1);
-    }
-    return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal);
+    return decodeEnumAndCreateInstance(exp);
   }
 
   private short decodeSimpleValue(short exp) {
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
index 65394bd..2146c82 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
@@ -396,11 +396,19 @@
 
   private void encodeEnumTag(short obj) {
     writeTag(KMEnumTag.cast(obj).getTagType(), KMEnumTag.cast(obj).getKey());
-    writeByteValue(KMEnumTag.cast(obj).getValue());
+    encodeInteger(
+        KMEnumTag.cast(obj).getBuffer(),
+        KMEnumTag.cast(obj).length(),
+        KMEnumTag.cast(obj).getStartOffset(),
+        UINT_TYPE);
   }
 
   private void encodeEnum(short obj) {
-    writeByteValue(KMEnum.cast(obj).getVal());
+    encodeInteger(
+        KMEnum.cast(obj).getBuffer(),
+        KMEnum.cast(obj).length(),
+        KMEnum.cast(obj).getStartOffset(),
+        UINT_TYPE);
   }
 
   private void encodeInteger(byte[] val, short len, short startOff, short majorType) {
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
index 44bf477..7f13853 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
@@ -73,7 +73,7 @@
     if (!validateEnum(enumType, NO_VALUE)) {
       ISOException.throwIt(ISO7816.SW_DATA_INVALID);
     }
-    short ptr = KMType.instance(ENUM_TYPE, (short) 2);
+    short ptr = KMType.instance(ENUM_TYPE, (short) 2 /* TAG_KEY */);
     Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
     return ptr;
   }
@@ -82,12 +82,28 @@
     if (!validateEnum(enumType, val)) {
       ISOException.throwIt(ISO7816.SW_DATA_INVALID);
     }
-    short ptr = KMType.instance(ENUM_TYPE, (short) 3);
+    short ptr = KMType.instance(ENUM_TYPE, (short) (2 /* TAG_KEY */ + 1 /* Byte value */));
     Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
     heap[(short) (ptr + TLV_HEADER_SIZE + 2)] = val;
     return ptr;
   }
 
+  public static short instance(short key, byte[] num, short srcOff, short length) {
+    if (length == 1) {
+      return instance(key, num[srcOff]);
+    }
+    if (!validateEnum(key, num, srcOff, length)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = KMType.instance(ENUM_TYPE, (short) (2 /* TAG_KEY */ + KMInteger.UINT_32));
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), key);
+    short destValOff = (short) (ptr + TLV_HEADER_SIZE + 2);
+    Util.arrayFillNonAtomic(heap, destValOff, KMInteger.UINT_32, (byte) 0);
+    Util.arrayCopyNonAtomic(
+        num, srcOff, heap, (short) (destValOff + KMInteger.UINT_32 - length), length);
+    return ptr;
+  }
+
   private static void create() {
     // The allowed enum values to corresponding enum types in the types array.
     if (enums == null) {
@@ -145,13 +161,21 @@
   }
 
   public short length() {
-    return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1));
+    return (short) (Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1)) - 2);
   }
 
   public byte getVal() {
     return heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)];
   }
 
+  public short value(byte[] dest, short destOff) {
+    return copyToUint32(heap, getStartOffset(), length(), dest, destOff);
+  }
+
+  public short getStartOffset() {
+    return (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2);
+  }
+
   public void setVal(byte val) {
     heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val;
   }
@@ -163,4 +187,20 @@
   public void setEnumType(short type) {
     Util.setShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type);
   }
+
+  public static boolean validateEnum(short key, byte[] buf, short off, short len) {
+    if (len != KMInteger.UINT_32) {
+      return false;
+    }
+    switch (key) {
+      case KMType.USER_AUTH_TYPE:
+        // HardwareAuthenticatorType::ANY - 0xFFFFFFFF
+        short highShort = Util.getShort(buf, off);
+        short lowShort = Util.getShort(buf, (short) (off + 2));
+        return ((short) 0xFFFF == (short) (highShort & lowShort));
+
+      default:
+        return false;
+    }
+  }
 }
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
index a7bcbe6..f4f7e29 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
@@ -73,6 +73,24 @@
     return ptr;
   }
 
+  public static short instance(short key, byte[] val, short valOff, short valLen) {
+    if (valLen == 1) {
+      return instance(key, val[valOff]);
+    }
+    if (!KMEnum.validateEnum(key, val, valOff, valLen)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr =
+        KMType.instance(TAG_TYPE, (short) (2 /* TAG_TYPE */ + 2 /* TAG_KEY */ + KMInteger.UINT_32));
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    short destValueOff = (short) (ptr + TLV_HEADER_SIZE + 4);
+    Util.arrayFillNonAtomic(heap, destValueOff, KMInteger.UINT_32, (byte) 0);
+    Util.arrayCopyNonAtomic(
+        val, valOff, heap, (short) (destValueOff + KMInteger.UINT_32 - valLen), valLen);
+    return ptr;
+  }
+
   public static KMEnumTag cast(short ptr) {
     if (heap[ptr] != TAG_TYPE) {
       ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
@@ -137,6 +155,19 @@
     return KMType.INVALID_VALUE;
   }
 
+  public short length() {
+    return (short)
+        (Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + 1)) - 4);
+  }
+
+  public short getStartOffset() {
+    return (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4);
+  }
+
+  public short value(byte[] dest, short destOff) {
+    return copyToUint32(heap, getStartOffset(), length(), dest, destOff);
+  }
+
   public short getKey() {
     return Util.getShort(
         heap, (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
index 715a119..a410e00 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
@@ -3001,7 +3001,8 @@
       if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) {
         KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
       }
-      if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) {
+      short len = op.getAuthType(scratchPad, (short) 0);
+      if (!authTokenMatches(op.getUserSecureId(), scratchPad, (short) 0, len, scratchPad, len)) {
         KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
       }
     }
@@ -3078,10 +3079,10 @@
       }
       // Now check if the device unlock requires password only authentication and whether
       // auth token is generated through password authentication or not.
+      scratchPad[0] = KMType.PASSWORD;
+      short authTypeLen = 1;
       if (kmDataStore.getDeviceLockPasswordOnly()) {
-        ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
-        ptr = KMEnum.cast(ptr).getVal();
-        if (((byte) ptr & KMType.PASSWORD) == 0) {
+        if (!hwAuthTypeMatches(scratchPad, (short) 0, authTypeLen, scratchPad, authTypeLen)) {
           KMException.throwIt(KMError.DEVICE_LOCKED);
         }
       }
@@ -3975,7 +3976,29 @@
     return false;
   }
 
-  private boolean authTokenMatches(short userSecureIdsPtr, short authType, byte[] scratchPad) {
+  public boolean hwAuthTypeMatches(
+      byte[] buf, short off, short len, byte[] scratchPad, short scratchOff) {
+    Util.arrayFillNonAtomic(scratchPad, scratchOff, (short) (2 * KMInteger.UINT_32), (byte) 0);
+    short enumPtr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+    if (KMInteger.UINT_32 != KMEnum.cast(enumPtr).value(scratchPad, scratchOff)) {
+      return false;
+    }
+    Util.arrayCopyNonAtomic(
+        buf, off, scratchPad, (short) (scratchOff + 2 * KMInteger.UINT_32 - len), len);
+    short highShort = Util.getShort(scratchPad, scratchOff);
+    short lowShort = Util.getShort(scratchPad, (short) (scratchOff + 2));
+    short otherHighShort = Util.getShort(scratchPad, (short) (scratchOff + KMInteger.UINT_32));
+    short otherLowShort = Util.getShort(scratchPad, (short) (scratchOff + KMInteger.UINT_32 + 2));
+    return (0 != (lowShort & otherLowShort) || 0 != (highShort & otherHighShort));
+  }
+
+  private boolean authTokenMatches(
+      short userSecureIdsPtr,
+      byte[] buf,
+      short off,
+      short len,
+      byte[] scratchPad,
+      short scratchOff) {
     if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
       return false;
     }
@@ -3983,12 +4006,7 @@
       return false;
     }
     // check auth type
-    tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
-    tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal();
-    if (((byte) tmpVariables[2] & (byte) authType) == 0) {
-      return false;
-    }
-    return true;
+    return hwAuthTypeMatches(buf, off, len, scratchPad, scratchOff);
   }
 
   private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) {
@@ -4007,16 +4025,19 @@
       }
       // authenticator type must be provided.
       if (KMType.INVALID_VALUE
-          == (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) {
+          == (authType =
+              KMKeyParameters.findTag(
+                  KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) {
         // Authentication required, but no auth type found.
         KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
       }
+      short len = KMEnumTag.cast(authType).value(scratchPad, (short) 0);
 
       short authTimeoutTagPtr =
           KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]);
       if (authTimeoutTagPtr != KMType.INVALID_VALUE) {
         // authenticate user
-        if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) {
+        if (!authTokenMatches(userSecureIdPtr, scratchPad, (short) 0, len, scratchPad, len)) {
           KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
         }
 
@@ -4041,7 +4062,7 @@
         // auth per operation required
         // store user secure id and authType in OperationState.
         op.setUserSecureId(userSecureIdPtr);
-        op.setAuthType((byte) authType);
+        op.setAuthType(scratchPad, (short) 0, len);
         // set flags
         op.setOneTimeAuthReqd(false);
         op.setAuthPerOperationReqd(true);
@@ -4076,7 +4097,7 @@
     len += KMInteger.UINT_64;
     // concatenate authenticator type - 4 bytes
     ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
-    scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+    KMEnum.cast(ptr).value(scratchPad, len);
     len += KMInteger.UINT_32;
     // concatenate timestamp -8 bytes
     ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
@@ -4118,7 +4139,7 @@
     len += KMInteger.UINT_64;
     // concatenate authenticator type - 4 bytes
     ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
-    scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+    KMEnum.cast(ptr).value(scratchPad, len);
     len += KMInteger.UINT_32;
     // concatenate timestamp - 8 bytes
     ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
index 2a53acd..6488577 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
@@ -31,7 +31,7 @@
 
   // sizes
   public static final byte OPERATION_HANDLE_SIZE = 8;
-  public static final byte DATA_SIZE = 11;
+  public static final byte DATA_SIZE = 12;
   public static final byte AUTH_TIME_SIZE = 8;
   // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs)
   // First two bytes are reserved to store number of secure ids. So total 42 bytes.
@@ -47,7 +47,7 @@
   private static final byte MAC_LENGTH = 7;
   private static final byte MGF_DIGEST = 8;
   private static final byte AUTH_TYPE = 9;
-  private static final byte MIN_MAC_LENGTH = 10;
+  private static final byte MIN_MAC_LENGTH = 11;
   private static final byte OPERATION = 0;
   private static final byte HMAC_SIGNER_OPERATION = 1;
   // Flag masks
@@ -187,12 +187,16 @@
     }
   }
 
-  public short getAuthType() {
-    return data[AUTH_TYPE];
+  public short getAuthType(byte[] buf, short offset) {
+    Util.arrayFillNonAtomic(buf, offset, (short) 4, (byte) 0);
+    offset = Util.setShort(buf, offset, data[AUTH_TYPE]);
+    Util.setShort(buf, offset, data[AUTH_TYPE + 1]);
+    return (short) 4;
   }
 
-  public void setAuthType(byte authType) {
-    data[AUTH_TYPE] = authType;
+  public void setAuthType(byte[] buf, short offset, short len) {
+    data[AUTH_TYPE] = Util.getShort(buf, offset);
+    data[(short) (AUTH_TYPE + 1)] = Util.getShort(buf, (short) (offset + 2));
   }
 
   public short getUserSecureId() {
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
index a18cb7f..fbee00b 100644
--- a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
@@ -388,6 +388,21 @@
     return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
   }
 
+  protected static short copyToUint32(
+      byte[] src, short srcOff, short srcLen, byte[] dest, short destOff) {
+    if (srcLen > KMInteger.UINT_32) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    Util.arrayFillNonAtomic(dest, destOff, KMInteger.UINT_32, (byte) 0);
+    Util.arrayCopyNonAtomic(
+        src, srcOff, dest, (short) (destOff + KMInteger.UINT_32 - srcLen), srcLen);
+    return KMInteger.UINT_32;
+  }
+
+  protected byte[] getBuffer() {
+    return heap;
+  }
+
   protected static short instance(byte type, short length) {
     if (length < 0) {
       ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);