Skip to content
This repository was archived by the owner on May 24, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,4 +23,6 @@ public class KMConfigurations {
// If the size of the attestation ids is known and lesser than 64
// then reduce the size here. It reduces the heap memory usage.
public static final byte MAX_ATTESTATION_IDS_SIZE = 64;
// DER subject max length.
public static final short MAX_SUBJECT_DER_LEN = 1095;
}
83 changes: 58 additions & 25 deletions Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,7 @@ public class KMAsn1Parser {
public static final byte ASN1_PRINTABLE_STRING = 0x13;
public static final byte ASN1_UNIVERSAL_STRING = 0x1C;
public static final byte ASN1_BMP_STRING = 0x1E;
public static final byte IA5_STRING = 0x16;

public static final byte[] EC_CURVE = {
0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03,
Expand All@@ -38,6 +39,12 @@ public class KMAsn1Parser {
public byte[] COMMON_OID = new byte[] {
0x06, 0x03, 0x55, 0x04
};

public byte[] EMAIL_ADDRESS_OID = new byte[] {
0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x09, 0x01
};
public static final short MAX_EMAIL_ADD_LEN = 255;

// This array contains the last byte of OID for each oid type.
// The first 4 bytes are common as shown above in COMMON_OID
private static final byte[] attributeOIds = {
Expand All@@ -49,7 +56,8 @@ public class KMAsn1Parser {
0x08, /* stateOrProviince COMMON_OID.8 */
0x0A, /* organizationName COMMON_OID.10 */
0x0B, /* organizationalUnitName COMMON_OID.11 */
0x0C, /* title COMMON_OID.10 */
0x0C, /* title COMMON_OID.10 */
0x29, /* name COMMON_OID.41 */
0x2A, /* givenName COMMON_OID.42 */
0x2B, /* initials COMMON_OID.43 */
0x2C, /* generationQualifier COMMON_OID.44 */
Expand All@@ -59,6 +67,8 @@ public class KMAsn1Parser {
// https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 124
// TODO Specification does not mention about the DN_QUALIFIER_OID max length.
// So the max limit is set at 64.
// For name the RFC 5280 supports up to 32768, as Javacard doesn't support
// that much length, the max limit for name is set to 128.
private static final byte[] attributeValueMaxLen = {
0x40, /* 1-64 commonName */
0x28, /* 1-40 surname */
Expand All@@ -69,6 +79,7 @@ public class KMAsn1Parser {
0x40, /* 1-64 organization */
0x40, /* 1-64 organization unit*/
0x40, /* 1-64 title */
0x29, /* 1-128 name */
0x10, /* 1-16 givenName */
0x05, /* 1-5 initials */
0x03, /* 1-3 gen qualifier */
Expand DownExpand Up@@ -97,7 +108,19 @@ public short decodeEc(short blob){
decodeCommon((short)0, EC_ALGORITHM);
return decodeEcPrivateKey((short)1);
}


/*
Name ::= CHOICE { -- only one possibility for now --
rdnSequence RDNSequence }
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
RelativeDistinguishedName ::=
SET SIZE (1..MAX) OF AttributeTypeAndValue
AttributeTypeAndValue ::= SEQUENCE {
type AttributeType,
value AttributeValue }
AttributeType ::= OBJECT IDENTIFIER
AttributeValue ::= ANY -- DEFINED BY AttributeType
*/
public void validateDerSubject(short blob) {
init(blob);
header(ASN1_SEQUENCE);
Expand DownExpand Up@@ -247,44 +270,54 @@ private void validateTag0IfPresent(){
if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR);
incrementCursor(len);
}

private void validateAttributeTypeAndValue() {
short start = cur;
if (getByte() != OBJECT_IDENTIFIER) {
// First byte should be OBJECT_IDENTIFIER, otherwise it is not well-formed DER Subject.
if (data[cur] != OBJECT_IDENTIFIER) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
short length = getLength();
if (length != 3) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
// Check if the OID matches the email address
if ((Util.arrayCompare(data, cur, EMAIL_ADDRESS_OID, (short) 0,
(short) EMAIL_ADDRESS_OID.length) == 0)) {
incrementCursor((short) EMAIL_ADDRESS_OID.length);
// Validate the length of the attribute value.
if (getByte() != IA5_STRING) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
short emailLength = getLength();
if (emailLength <= 0 && emailLength > MAX_EMAIL_ADD_LEN) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
incrementCursor(emailLength);
return;
}
cur = start;
boolean found = false;
for(short i = 0; i < (short) attributeOIds.length; i++) {
if ((Util.arrayCompare(data, cur, COMMON_OID, (short)0, (short) COMMON_OID.length) == 0) &&
(attributeOIds[i] == data[(short)(cur + COMMON_OID.length)])) {
// Check other OIDs.
for (short i = 0; i < (short) attributeOIds.length; i++) {
if ((Util.arrayCompare(data, cur, COMMON_OID, (short) 0, (short) COMMON_OID.length) == 0) &&
(attributeOIds[i] == data[(short) (cur + COMMON_OID.length)])) {
incrementCursor((short) (COMMON_OID.length + 1));
// Validate the length of the attribute value.
short tag = getByte();
if(tag != ASN1_UTF8_STRING &&
if(tag != ASN1_UTF8_STRING &&
tag != ASN1_TELETEX_STRING &&
tag != ASN1_PRINTABLE_STRING &&
tag != ASN1_UNIVERSAL_STRING &&
tag != ASN1_BMP_STRING) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
length = getLength();
if (length <= 0 && length > attributeValueMaxLen[i]) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
incrementCursor(length);
found = true;
break;
short attrValueLength = getLength();
if (attrValueLength <= 0 && attrValueLength > attributeValueMaxLen[i]) {
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
incrementCursor(attrValueLength);
return;
}
}
if (!found) {
// None of the attributes matches.
KMException.throwIt(KMError.UNKNOWN_ERROR);
}
// If no match is found above then move the cursor to next element.
getByte(); // Move Cursor by one byte (OID)
incrementCursor(getLength()); // Move cursor to AtrributeTag
getByte(); // Move cursor to AttributeValue
incrementCursor(getLength()); // Move cursor to next SET element
}

private short header(short tag){
Expand Down
2 changes: 1 addition & 1 deletion Applet/src/com/android/javacard/keymaster/KMByteTag.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -105,7 +105,7 @@ private static boolean validateKey(short key, short byteBlob) {
break;
case CERTIFICATE_SUBJECT_NAME:
{
if (valueLen > MAX_SUBJECT_DER_LEN) {
if (valueLen > KMConfigurations.MAX_SUBJECT_DER_LEN) {
return false;
}
KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -1459,7 +1459,7 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara
} catch (KMException e) {
KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
}
if (KMByteBlob.cast(issuer).length() > KMType.MAX_SUBJECT_DER_LEN) {
if (KMByteBlob.cast(issuer).length() > KMConfigurations.MAX_SUBJECT_DER_LEN) {
KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
}
// If issuer is not present then it is an error
Expand Down
2 changes: 0 additions & 2 deletions Applet/src/com/android/javacard/keymaster/KMType.java
Original file line numberDiff line numberDiff line change
Expand Up@@ -362,8 +362,6 @@ public abstract class KMType {
public static final short MAX_CERTIFICATE_SERIAL_SIZE = 20;
// Attestation Application ID
public static final short MAX_ATTESTATION_APP_ID_SIZE = 1024;
// DER subject max length.
public static final short MAX_SUBJECT_DER_LEN = 1095;


protected static KMRepository repository;
Expand Down