diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 4505f8d7..39559f70 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -27,7 +27,9 @@ import javacard.framework.APDU; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; +import javacard.security.CryptoException; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { @@ -157,6 +159,14 @@ public void process(APDU apdu) { super.process(apdu); break; } + } catch (KMException exception) { + sendError(apdu, KMException.reason()); + } catch (ISOException exp) { + sendError(apdu, mapISOErrorToKMError(exp.getReason())); + } catch (CryptoException e) { + sendError(apdu, mapCryptoErrorToKMError(e.getReason())); + } catch (Exception e) { + sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); } finally { repository.clean(); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 8f009ae2..f9fca3d0 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -267,9 +267,9 @@ public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratch public KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) { if(!derEncoded) { if (usageExpiryTimeObj != KMType.INVALID_VALUE) { - // compare if the expiry time is greater then 2051 then use generalized + // 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.firstJan2051, (short) 0); + short tmpVar = KMInteger.uint_64(KMUtils.firstJan2050, (short) 0); if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) { usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, false); @@ -1055,11 +1055,4 @@ public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte return this; } - //Check - /* - * private void print(byte[] buf, short start, short length){ StringBuilder sb = - * new StringBuilder(length * 2); for(short i = start; i < (start+length); i - * ++){ sb.append(String.format("%02x", buf[i])); } System.out.println( - * sb.toString()); } - */ } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index f21eb868..54c2d032 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -34,6 +34,8 @@ public class KMUtils { 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec public static final byte[] leapYearMsec = { 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; + public static byte[] thirtyDaysMsec = { + 0, 0, 0, 0, (byte) 0x9A, (byte) 0x7E, (byte) 0xC8, 0}; //2592000000 msec public static final byte[] yearMsec = { 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 //Leap year(366) + 3 * 365 @@ -41,8 +43,8 @@ public class KMUtils { 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 public static final byte[] firstJan2020 = { 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00}; // 1577836800000 msec - public static final byte[] firstJan2051 = { - 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00}; // 2556144000000 + public static final byte[] firstJan2050 = { + 0, 0, 0x02, 0x4b, (byte) 0xCE, 0x5C, (byte)0xF0, 0x00}; //2524608000000 // msec public static final byte[] febMonthLeapMSec = { 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00}; //2505600000 @@ -80,12 +82,12 @@ public static short convertToDate(short time, byte[] scratchPad, KMException.throwIt(KMError.INVALID_ARGUMENT); } if (utcFlag - && KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, + && KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8) >= 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); @@ -94,7 +96,7 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8); } else { from2020 = false; - Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, + Util.arrayCopyNonAtomic(firstJan2050, (short) 0, scratchPad, (short) 8, (short) 8); subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 30860f50..522f4edf 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -134,12 +134,12 @@ public KMAndroidSEProvider() { // Re-usable AES,DES and HMAC keys in persisted memory. aesKeys = new AESKey[2]; aesKeys[KEYSIZE_128_OFFSET] = (AESKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false); aesKeys[KEYSIZE_256_OFFSET] = (AESKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false); + triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false); - hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 512, + hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false); rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); @@ -936,6 +936,8 @@ public short getCertificateChainLength() { @Override public void onSave(Element element) { element.write(certificateChain); + element.write(additionalCertChain); + element.write(bcc); KMAESKey.onSave(element, masterKey); KMECPrivateKey.onSave(element, attestationKey); KMHmacKey.onSave(element, preSharedKey); @@ -944,6 +946,8 @@ public void onSave(Element element) { @Override public void onRestore(Element element) { certificateChain = (byte[]) element.readObject(); + additionalCertChain = (byte[]) element.readObject(); + bcc = (byte[]) element.readObject(); masterKey = KMAESKey.onRestore(element); attestationKey = KMECPrivateKey.onRestore(element); preSharedKey = KMHmacKey.onRestore(element); @@ -1315,7 +1319,7 @@ public void setBootPatchLevel(byte[] buffer, short start, short length) { if (length > 4 || length < 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) 4); + Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) length); } @Override @@ -1468,7 +1472,7 @@ public void persistAdditionalCertChain(byte[] buf, short offset, short len) { // // self-signed cert, leaf contains DK_pu b // ] // Certificate = COSE_Sign1 of a public key - if ((short) (len + 2) >= ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index a468e924..6fa4098e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -137,6 +137,11 @@ public short length() { return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2)); } + public short setLength(short len) { + return Util.setShort(heap, + (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), len); + } + public byte[] getBuffer() { return heap; } diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index e68ecaa7..4cacce96 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -345,6 +345,7 @@ private short decodeKeyParam(short exp) { short tagClass; short allowedType; short obj; + short arrPos = 0; // For each tag in payload ... while (index < payloadLength) { tagFound = false; @@ -360,13 +361,14 @@ private short decodeKeyParam(short exp) { try { tagFound = true; obj = decode(tagClass); - KMArray.cast(vals).add(index, obj); + KMArray.cast(vals).add(arrPos++, obj); break; }catch(KMException e){ if(KMException.reason() == KMError.INVALID_TAG && !ignoreInvalidTags){ KMException.throwIt(KMError.INVALID_TAG); } + break; } } tagInd++; @@ -377,6 +379,7 @@ private short decodeKeyParam(short exp) { index++; } } + KMArray.cast(vals).setLength(arrPos); return KMKeyParameters.instance(vals); } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 3f45c304..75029326 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -103,7 +103,7 @@ public static void create() { enums = new Object[]{ new byte[]{RSA, DES, EC, AES, HMAC}, - new byte[]{P_224, P_256, P_384, P_521}, + new byte[]{P_224, P_256, P_384, P_521, CURVE_25519}, new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY}, new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index c9ed7a3a..24905f01 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -147,6 +147,7 @@ public short value(byte[] dest, short destOff) { Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length()); return length(); } + public short toLittleEndian(byte[] dest, short destOff) { short index = (short) (length() - 1); while (index >= 0) { diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 5311cbd8..d370793e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -195,7 +195,7 @@ public boolean isValidKeySize(byte alg) { } break; case KMType.DES: - if (val == 192 || val == 168) { + if (val == 168) { return true; } break; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 530642d2..295d6e96 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -331,7 +331,7 @@ public void uninstall() { repository.onUninstall(); } - private short mapISOErrorToKMError(short reason) { + protected short mapISOErrorToKMError(short reason) { switch (reason) { case ISO7816.SW_CLA_NOT_SUPPORTED: return KMError.UNSUPPORTED_CLA; @@ -353,7 +353,7 @@ private short mapISOErrorToKMError(short reason) { } } - private short mapCryptoErrorToKMError(short reason) { + protected short mapCryptoErrorToKMError(short reason) { switch (reason) { case CryptoException.ILLEGAL_USE: return KMError.CRYPTO_ILLEGAL_USE; @@ -398,6 +398,7 @@ protected short validateApduHeader(APDU apdu) { @Override public void process(APDU apdu) { try { + resetData(); repository.onProcess(); // If this is select applet apdu which is selecting this applet then return if (apdu.isISOInterindustryCLA()) { @@ -509,7 +510,6 @@ public void process(APDU apdu) { resetWrappingKey(); sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); } finally { - resetData(); repository.clean(); } } @@ -535,10 +535,11 @@ private void processEarlyBootEndedCmd(APDU apdu) { private short deviceLockedCmd(APDU apdu){ short cmd = KMArray.instance((short) 2); + short ptr = KMVerificationToken.exp(); // passwordOnly KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // verification token - KMArray.cast(cmd).add((short) 1, KMVerificationToken.exp()); + KMArray.cast(cmd).add((short) 1, ptr); return receiveIncoming(apdu, cmd); } @@ -712,7 +713,9 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { checkVersionAndPatchLevel(scratchPad); // Remove custom tags from key characteristics short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); - KMKeyParameters.cast(teeParams).deleteCustomTags(); + if(teeParams != KMType.INVALID_VALUE) { + KMKeyParameters.cast(teeParams).deleteCustomTags(); + } // make response. short resp = KMArray.instance((short) 2); KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -2443,7 +2446,7 @@ private void authorizePadding(KMOperationState op) { KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); if (param != KMType.INVALID_VALUE) { if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); } param = KMEnumArrayTag.cast(param).get((short) 0); if (!KMEnumArrayTag.cast(paddings).contains(param)) { @@ -2540,9 +2543,6 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { default: KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); } - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } if (param == KMType.GCM) { if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) { KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); @@ -2592,10 +2592,8 @@ 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])) { - KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } else if (macLen % 8 != 0 || macLen > 256) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); } op.setMacLength(macLen); } @@ -2616,9 +2614,10 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizeKeyUsageForCount(scratchPad); //Validate early boot + //VTS expects error code EARLY_BOOT_ONLY during begin operation if eary boot ended tag is present if (repository.getEarlyBootEndedStatus()) { KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, - KMError.INVALID_KEY_BLOB); + KMError.EARLY_BOOT_ENDED); } //Validate bootloader only @@ -3153,20 +3152,22 @@ private void importECKeys(byte[] scratchPad) { // initialize 256 bit p256 key for given private key and public key. short index = 0; // check whether the key size tag is present in key parameters. + short SecretLen = (short) (KMByteBlob.length(data[SECRET]) * 8); short keySize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keySize != KMType.INVALID_VALUE) { // As per NIST.SP.800-186 page 9, secret for 256 curve should be between // 256-383 - if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ keySize == 256) { + if (((256 <= SecretLen) && (383 >= SecretLen)) ^ keySize == 256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } if (keySize != 256) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } else { + if ((256 > SecretLen) || (383 < SecretLen)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } // add the key size to scratchPad keySize = KMInteger.uint_16((short) 256); keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize); @@ -3178,15 +3179,16 @@ private void importECKeys(byte[] scratchPad) { if (curve != KMType.INVALID_VALUE) { // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between // 256-383 - if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ curve == KMType.P_256) { + if (((256 <= SecretLen) && (383 >= SecretLen)) ^ curve == KMType.P_256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } if (curve != KMType.P_256) { KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); } } else { + if ((256 > SecretLen) || (383 < SecretLen)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } // add the curve to scratchPad curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); Util.setShort(scratchPad, index, curve); @@ -3205,8 +3207,6 @@ private void importECKeys(byte[] scratchPad) { // add scratch pad to key parameters updateKeyParameters(scratchPad, index); - // validate updated key parameters. - validateECKeys(); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3220,12 +3220,19 @@ private void importHmacKey(byte[] scratchPad) { short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keysize != KMType.INVALID_VALUE) { - if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (keysize != (short) (KMByteBlob.length(data[SECRET]) * 8)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { - // add the key size to scratchPad - keysize = KMInteger.uint_16((short) (KMByteBlob.cast(data[SECRET]).length() * 8)); + // add the key size to scratchPad + keysize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + keysize = KMInteger.uint_16(keysize); short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); Util.setShort(scratchPad, index, keySizeTag); index += 2; @@ -3256,13 +3263,23 @@ private void importTDESKey(byte[] scratchPad) { if (keysize != 168) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } + if (192 != (short) (8 * KMByteBlob.length(data[SECRET]))) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } } else { + keysize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (keysize != 192) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } // add the key size to scratchPad keysize = KMInteger.uint_16((short) 168); short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); Util.setShort(scratchPad, index, keysizeTag); index += 2; } + // Read Minimum Mac length - it must not be present + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMError.INVALID_TAG); // Check whether key can be created seProvider.importSymmetricKey( KMType.DES, @@ -3272,8 +3289,6 @@ private void importTDESKey(byte[] scratchPad) { KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list updateKeyParameters(scratchPad, index); - // validate TDES Key parameters - validateTDESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3292,6 +3307,9 @@ private void importAESKey(byte[] scratchPad) { short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keysize != KMType.INVALID_VALUE) { + if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } validateAesKeySize(keysize); } else { // add the key size to scratchPad @@ -3329,7 +3347,7 @@ private void importRSAKey(byte[] scratchPad) { } if(Util.arrayCompare(F4, (short)0, KMByteBlob.cast(pubKeyExp).getBuffer(), KMByteBlob.cast(pubKeyExp).getStartOff(), (short)F4.length) != 0){ - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } short index = 0; // index in scratchPad for update parameters. // validate public exponent if present in key params - it must be 0x010001 @@ -3360,12 +3378,15 @@ private void importRSAKey(byte[] scratchPad) { // check the keysize tag if present in key parameters. short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + short kSize = (short) (KMByteBlob.length(data[SECRET]) * 8); if (keysize != KMType.INVALID_VALUE) { - if (keysize != 2048 - || keysize != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { + if (keysize != 2048 || (keysize != kSize)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { + if (2048 != kSize) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } // add the key size to scratchPad keysize = KMInteger.uint_16((short) 2048); keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); @@ -3805,11 +3826,11 @@ private static void validateHmacKey() { // 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])) { + if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } // Read Minimum Mac length - KMTag.assertPresence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.MISSING_MAC_LENGTH); + KMTag.assertPresence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.MISSING_MIN_MAC_LENGTH); short minMacLength = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); @@ -4090,20 +4111,6 @@ private static void makeAuthData(byte[] scratchPad) { 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; - } - private static short deriveKey(byte[] scratchPad) { // KeyDerivation: // 1. Do HMAC Sign, Auth data. @@ -4277,12 +4284,18 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); encodedLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - + + short signatureLen = + rkp.encodeES256CoseSignSignature( + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), + KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)), + scratchPad, + (short) (keySize + encodedLen)); + if (!seProvider.ecVerify256(scratchPad, (short) 0, keySize, scratchPad, keySize, encodedLen, - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).length())) { - KMException.throwIt(KMError.STATUS_FAILED); + scratchPad, (short) (keySize + encodedLen), signatureLen)) { + KMException.throwIt(KMError.STATUS_FAILED); } prevCoseKey = ptr2; } @@ -4361,6 +4374,8 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { scratchPad, temp ); + len = KMPKCS8Decoder.instance(). + decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp); coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); // construct cose_sign1 diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index eb860e2d..3040d480 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -103,6 +103,34 @@ private void updateModulus(short blob) { KMByteBlob.cast(blob).setLength(--len); } } + + private short readEcdsa256SigIntegerHeader() { + short len = header(ASN1_INTEGER); + if (len == 33) { + if (0 != getByte()) { + KMException.throwIt(KMError.INVALID_DATA); + } + len--; + } else if (len > 33) { + KMException.throwIt(KMError.INVALID_DATA); + } + return len; + } + + // Seq [Int, Int] + public short decodeEcdsa256Signature(short blob, byte[] scratchPad, short scratchPadOff) { + init(blob); + short len = header(ASN1_SEQUENCE); + len = readEcdsa256SigIntegerHeader(); + // concatenate r and s in the buffer (r||s) + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 64, (byte) 0); + // read r + getBytes(scratchPad, (short) (scratchPadOff + 32 - len), len); + len = readEcdsa256SigIntegerHeader(); + // read s + getBytes(scratchPad, (short) (scratchPadOff + 64 - len), len); + return (short) 64; + } // Seq [Int, Blob] public void decodeCommon(short version, byte[] alg){ @@ -180,6 +208,11 @@ private void getBytes(short blob){ incrementCursor(len); } + private void getBytes(byte[] buffer, short offset, short len) { + Util.arrayCopyNonAtomic(data, cur, buffer, offset, len); + incrementCursor(len); + } + private short getLength(){ byte len = getByte(); if(len >= 0) return len; diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index a9c3f0a2..7aab6c7d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -92,6 +92,7 @@ public abstract class KMType { public static final byte P_256 = 0x01; public static final byte P_384 = 0x02; public static final byte P_521 = 0x03; + public static final byte CURVE_25519 = 0x04; // KeyBlobUsageRequirements Enum Tag key and values. public static final short BLOB_USAGE_REQ = 0x012D; diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index adc1684a..4a68b718 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -763,33 +763,6 @@ private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLeng ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); } - private static short getAttestKeyParameters() { - short tagIndex = 0; - short arrPtr = KMArray.instance((short) 6); - // Key size - 256 - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); - // Digest - SHA256 - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - // Purpose - Attest - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - - KMArray.cast(arrPtr).add(tagIndex++, purpose); - // Algorithm - EC - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - // Curve - P256 - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); - // No Authentication is required to use this key. - KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); - return KMKeyParameters.instance(arrPtr); - } - private short createSignedMac(KMDeviceUniqueKey deviceUniqueKey, byte[] scratchPad, short deviceMapPtr, short pubKeysToSign) { // Challenge @@ -833,6 +806,8 @@ private short createSignedMac(KMDeviceUniqueKey deviceUniqueKey, byte[] scratchP scratchPad, signStructure ); + len = KMPKCS8Decoder.instance(). + decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, signStructure, len), scratchPad, signStructure); signStructure = KMByteBlob.instance(scratchPad, signStructure, len); /* Construct unprotected headers */ @@ -926,7 +901,9 @@ private short createDeviceInfo(byte[] scratchpad) { updateItem(deviceIds, out, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION)); updateItem(deviceIds, out, SECURITY_LEVEL, KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); - //TODO Add attest_id_state + byte[] attestIdState = seProvider.isProvisionLocked() ? ATTEST_ID_LOCKED : ATTEST_ID_OPEN; + updateItem(deviceIds, out, ATTEST_ID_STATE, + KMTextString.instance(attestIdState, (short) 0, (short) attestIdState.length)); // Create device info map. short map = KMMap.instance(out[1]); short mapIndex = 0; @@ -1043,6 +1020,11 @@ private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKe pubKeyBLen, scratchPad, (short) 0); key = KMByteBlob.instance(scratchPad, (short) 0, key); + // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04. + pubKeyAOff += 1; + pubKeyALen -= 1; + pubKeyBOff += 1; + pubKeyBLen -= 1; short kdfContext = KMCose.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, true); @@ -1329,27 +1311,6 @@ private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { .constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); } - private short constructCoseEncryptHeaders(byte[] scratchPad, short nonce) { - // CoseEncrypt protected headers. - short protectedHeader = KMCose.constructHeaders( - KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); - /* CoseEncrypt unprotected headers */ - short unprotectedHeader = - KMCose.constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, - KMType.INVALID_VALUE); - // Construct partial CoseEncrypt - return KMCose.constructCoseEncrypt(protectedHeader, unprotectedHeader, KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - } - - private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) { // prepare cosekey short coseKey = @@ -1427,4 +1388,71 @@ private short getEcAttestKeyParameters() { KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); return KMKeyParameters.instance(arrPtr); } + + private boolean isSignedByte(byte b) { + return ((b & 0x0080) != 0); + } + + private short writeIntegerHeader(short valueLen, byte[] data, short offset) { + // write length + data[offset] = (byte) valueLen; + // write INTEGER tag + offset--; + data[offset] = 0x02; + return offset; + } + + private short writeSequenceHeader(short valueLen, byte[] data, short offset) { + // write length + data[offset] = (byte) valueLen; + // write INTEGER tag + offset--; + data[offset] = 0x30; + return offset; + } + + private short writeSignatureData(byte[] input, short inputOff, short inputlen, byte[] output, short offset) { + Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen); + if (isSignedByte(input[inputOff])) { + offset--; + output[offset] = (byte) 0; + } + return offset; + } + + public short encodeES256CoseSignSignature(byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) { + // SEQ [ INTEGER(r), INTEGER(s)] + // write from bottom to the top + if (len != 64) { + KMException.throwIt(KMError.INVALID_DATA); + } + short maxTotalLen = 72; + short end = (short) (scratchPadOff + maxTotalLen); + // write s. + short start = (short) (end - 32); + start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start); + // write length and header + short length = (short) (end - start); + start--; + start = writeIntegerHeader(length, scratchPad, start); + // write r + short rEnd = start; + start = (short) (start - 32); + start = writeSignatureData(input, offset, (short) 32, scratchPad, start); + // write length and header + length = (short) (rEnd - start); + start--; + start = writeIntegerHeader(length, scratchPad, start); + // write length and sequence header + length = (short) (end - start); + start--; + start = writeSequenceHeader(length, scratchPad, start); + length = (short) (end - start); + if (start > scratchPadOff) { + // re adjust the buffer + Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length); + } + return length; + } + } diff --git a/ProvisioningTool/src/cppcose/cppcose.cpp b/ProvisioningTool/src/cppcose/cppcose.cpp index e0c5aaab..29bb2015 100644 --- a/ProvisioningTool/src/cppcose/cppcose.cpp +++ b/ProvisioningTool/src/cppcose/cppcose.cpp @@ -27,9 +27,30 @@ #include #include #include +#include namespace cppcose { +ErrMsgOr ecdsaDerSignatureToCose(const bytevec& ecdsaSignature) { + const unsigned char* p = ecdsaSignature.data(); + ECDSA_SIG *sig = d2i_ECDSA_SIG(nullptr, &p, ecdsaSignature.size()); + if (sig == nullptr) { + return "Error decoding DER signature"; + } + + bytevec ecdsaCoseSignature(64, 0); + if (BN_bn2binpad(ECDSA_SIG_get0_r(sig), ecdsaCoseSignature.data(), 32) != 32) { + ECDSA_SIG_free(sig); + return "Error encoding r"; + } + if (BN_bn2binpad(ECDSA_SIG_get0_s(sig), ecdsaCoseSignature.data() + 32, 32) != 32) { + ECDSA_SIG_free(sig); + return "Error encoding s"; + } + ECDSA_SIG_free(sig); + return ecdsaCoseSignature; +} + ErrMsgOr ECDSA_sign(const bytevec& key, bytevec& input) { EVP_PKEY_CTX* pkeyCtx = NULL; EVP_MD_CTX_Ptr digestCtx(EVP_MD_CTX_new()); @@ -132,7 +153,7 @@ ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& pr .encode(); auto signature = ECDSA_sign(key, signatureInput); if (!signature) return "Signing failed"; - return signature; + return ecdsaDerSignatureToCose(*signature); } ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index e70e5ce8..fb50dce6 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -40,8 +40,8 @@ enum ProvisionStatus { }; // TODO keymint provision status and lock -std::string provisionStatusApdu = hex2str("80084000000000"); -std::string lockProvisionApdu = hex2str("80074000000000"); +std::string provisionStatusApdu = hex2str("80045000000000"); +std::string lockProvisionApdu = hex2str("80035000000000"); Json::Value root; static std::string inputFileName;