From 32b716deefa84f9bb7a3c625413eb9dbc150ac7b Mon Sep 17 00:00:00 2001 From: cpathak Date: Tue, 23 Jun 2020 21:56:06 -0700 Subject: [PATCH] Added deviceLocked command Also added some bug fixes related to EC key import. --- .../android/javacard/keymaster/KMEnum.java | 5 +- .../android/javacard/keymaster/KMInteger.java | 5 + .../javacard/keymaster/KMKeyParameters.java | 3 +- .../javacard/keymaster/KMKeymasterApplet.java | 150 +++++++++++---- .../javacard/keymaster/KMRepository.java | 11 +- .../javacard/test/KMFrameworkTest.java | 3 +- .../com/android/javacard/test/KMVTSTest.java | 175 +++++++++++++++++- 7 files changed, 298 insertions(+), 54 deletions(-) diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index d5a69270..b58a9a15 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -25,7 +25,7 @@ public class KMEnum extends KMType { private static short instPtr; private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, - VERIFIED_BOOT_STATE, DEVICE_LOCKED, USER_AUTH_TYPE, PURPOSE}; + VERIFIED_BOOT_STATE, DEVICE_LOCKED, USER_AUTH_TYPE, PURPOSE,ECCURVE}; private static Object[] enums = null; @@ -92,7 +92,8 @@ private static void create() { new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT}, new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, new byte[] {USER_AUTH_NONE,PASSWORD,FINGERPRINT, BOTH}, - new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY} + new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, + new byte[] {P_224, P_256, P_384, P_521} }; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 4d7e33b1..e7f91ebf 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -119,6 +119,11 @@ public short getStartOff() { } public void getValue(byte[] dest, short destOff, short length){ + if(length < length()) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(length > length()) { + length = length(); + destOff +=length; + } Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); } public void setValue(byte[] src, short srcOff){ diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 69164fce..d4c4f9fc 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -133,7 +133,8 @@ public static short makeHwEnforced( KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, - KMType.ENUM_TAG, KMType.USER_AUTH_TYPE + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, + KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED }; byte index = 0; short tagInd = 0; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 3027fe4c..20a8a7a8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -97,6 +97,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_ABORT_OPERATION_CMD = 0x22; private static final byte INS_PROVISION_CMD = 0x23; private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; + private static final byte INS_DEVICE_LOCKED_CMD = 0x25; + private static final byte INS_EARLY_BOOT_ENDED_CMD = 0x26; + // Data Dictionary items public static final byte DATA_ARRAY_SIZE = 30; public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; @@ -276,7 +279,7 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } // Validate whether INS can be supported - if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_SET_BOOT_PARAMS_CMD)) { + if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_EARLY_BOOT_ENDED_CMD)) { ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. @@ -358,6 +361,12 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); break; + case INS_DEVICE_LOCKED_CMD: + processDeviceLockedCmd(apdu); + break; + case INS_EARLY_BOOT_ENDED_CMD: + processEarlyBootEndedCmd(apdu); + break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } @@ -375,6 +384,37 @@ public void process(APDU apdu) { repository.clean(); } } + + private void processEarlyBootEndedCmd(APDU apdu) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + + private void processDeviceLockedCmd(APDU apdu) { + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.exp()); + tmpVariables[1] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); + // Decode the arguments + tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 0); + tmpVariables[1] = KMInteger.cast(tmpVariables[1]).getByte(); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[0]).get((short)1); + validateVerificationToken(data[VERIFICATION_TOKEN],scratchPad); + short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + short lastDeviceLockedTime = KMInteger.uint_64(repository.deviceLockedTimestamp, (short)0); + if(KMInteger.compare(verTime,lastDeviceLockedTime) > 0){ + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)8, (byte)0); + KMInteger.cast(verTime).getValue(scratchPad,(short)0,(short)8); + repository.deviceLockedFlag = true; + if(tmpVariables[1] == 0x01) repository.deviceUnlockPasswordOnly = true; + else repository.deviceUnlockPasswordOnly = false; + Util.arrayCopy(scratchPad,(short)0,repository.deviceLockedTimestamp,(short)0,(short)repository.deviceLockedTimestamp.length); + } + sendError(apdu,KMError.OK); + } + private void resetData(){ short index = 0; while (index < data.length){ @@ -1460,7 +1500,7 @@ private void finishTrustedConfirmationOperation(KMOperationState op) { } private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) { - // User Authentication + // If one time user Authentication is required if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); @@ -1472,12 +1512,41 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } op.setAuthTimeoutValidated(true); - } else if(op.isAuthPerOperationReqd()){ // Auth per operation + } else if(op.isAuthPerOperationReqd()){ // If Auth per operation is required tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authorizeHwAuthToken(scratchPad); + authenticateUser(scratchPad); + } + } + + private void authorizeDeviceUnlock(short hwToken) { + // If device is locked and key characteristics requires unlocked device then check whether + // HW auth token has correct timestamp. + short ptr = + KMKeyParameters.findTag( + KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]); + if (ptr != KMType.INVALID_VALUE && repository.deviceLockedFlag) { + if (hwToken == KMType.INVALID_VALUE) KMException.throwIt(KMError.DEVICE_LOCKED); + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + // Check if the current auth time stamp is greater then device locked time stamp + if (KMInteger.compare(ptr, KMInteger.uint_64(repository.deviceLockedTimestamp, (short) 0)) + <= 0) { + KMException.throwIt(KMError.DEVICE_LOCKED); + } + // Now check if the device unlock requires password only authentication and whether + // auth token is generated through password authentication or not. + if (repository.deviceUnlockPasswordOnly) { + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + ptr = KMEnum.cast(ptr).getVal(); + if (((byte) ptr & KMType.PASSWORD) == 0) { + KMException.throwIt(KMError.DEVICE_LOCKED); + } + } + // Unlock the device + repository.deviceLockedFlag = false; + Util.arrayFillNonAtomic(repository.deviceLockedTimestamp, (short) 0, (short) 8, (byte) 0); } } @@ -1489,15 +1558,25 @@ private void validateVerificationToken(KMOperationState op, short verToken, byte if (KMByteBlob.cast(ptr).length() == 0) { return; } + validateVerificationToken(verToken, scratchPad); // validate operation handle. ptr = KMVerificationToken.cast(verToken).getChallenge(); if(op.getHandle() != KMInteger.cast(ptr).getShort()){ KMException.throwIt(KMError.VERIFICATION_FAILED); } - // concatenation length will be 37 + length of verified parameters list. + } + + private void validateVerificationToken(short verToken, byte[] scratchPad) { + short ptr = KMVerificationToken.cast(verToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // concatenation length will be 37 + length of verified parameters list - which is typically empty + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); short params = KMVerificationToken.cast(verToken).getParametersVerified(); - Util.arrayFillNonAtomic(scratchPad, (short) 0, - (short) (37+KMByteBlob.cast(params).length()), (byte) 0); + // Add "Auth Verification" - 17 bytes. Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); len = (short)authVerification.length; @@ -1517,8 +1596,9 @@ private void validateVerificationToken(KMOperationState op, short verToken, byte len += 4; // concatenate Parameters verified - blob of encoded data. ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short)0); - len += 4; + if (KMByteBlob.cast(ptr).length() != 0) { + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); + } // hmac the data HMACKey key = cryptoProvider.createHMACKey( @@ -1875,7 +1955,11 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizePadding(op); authorizeBlockModeAndMacLength(op); authorizeKeyUsageForCount(); + if(!validateHwToken(data[HW_TOKEN],scratchPad)){ + data[HW_TOKEN] = KMType.INVALID_VALUE; + } authorizeUserSecureIdAuthTimeout(op, scratchPad); + authorizeDeviceUnlock(data[HW_TOKEN]); // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in // key params then fail if it is not a Decrypt operation data[IV] = KMType.INVALID_VALUE; @@ -2136,17 +2220,19 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { // check if hw token is empty - mac should not be empty. - tmpVariables[1] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getMac(); - if (KMByteBlob.cast(tmpVariables[1]).length() == 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } + if(data[HW_TOKEN] == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_MAC_LENGTH); authTime = KMIntegerTag.cast(tmpVariables[0]).getValue(); - authorizeHwAuthToken(scratchPad); + // authenticate user + authenticateUser(scratchPad); + // set the one time auth op.setOneTimeAuthReqd(true); + // set the authentication time stamp in operation state authTime = addIntegers(authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp()); op.setAuthTime(KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff()); - op.setAuthTimeoutValidated(false); // auth time validation will happen in update or finish - } else { // auth per operation required + // auth time validation will happen in update or finish + op.setAuthTimeoutValidated(false); + } else { + // auth per operation required op.setOneTimeAuthReqd(false); op.setAuthPerOperationReqd(true); } @@ -2154,8 +2240,7 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc } - private void authorizeHwAuthToken(byte[] scratchPad) { - validateHwToken(data[HW_TOKEN], scratchPad); + private void authenticateUser(byte[] scratchPad) { tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); if (KMInteger.cast(tmpVariables[0]).isZero()) { tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); @@ -2176,16 +2261,16 @@ private void authorizeHwAuthToken(byte[] scratchPad) { } } - private void validateHwToken(short hwToken, byte[] scratchPad) { + private boolean validateHwToken(short hwToken, byte[] scratchPad) { // CBOR Encoding is always big endian short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); short len = 0; // If mac length is zero then token is empty. if (KMByteBlob.cast(ptr).length() == 0) { - return; + return false; } // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 37, (byte) 0); + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); len = 1; // concatenate challenge - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); @@ -2194,12 +2279,12 @@ private void validateHwToken(short hwToken, byte[] scratchPad) { len += 8; // concatenate user id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(tmpVariables[0]) + KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; // concatenate authenticator id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(tmpVariables[0]) + KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; // concatenate authenticator type - 4 bytes @@ -2208,7 +2293,7 @@ private void validateHwToken(short hwToken, byte[] scratchPad) { len += 4; // concatenate timestamp -8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(tmpVariables[0]) + KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; // hmac the data @@ -2223,9 +2308,7 @@ private void validateHwToken(short hwToken, byte[] scratchPad) { KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - if(!verified){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } + return verified; /* len = cryptoProvider.hmac(key, scratchPad, (short) 0, len, scratchPad, (short) (len + 1) ); @@ -2371,10 +2454,9 @@ private void importKey(APDU apdu, byte[] scratchPad) { private void importECKeys(byte[] scratchPad) { // Decode key material - tmpVariables[0] = KMArray.instance((short) 3); + tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key - KMArray.cast(tmpVariables[0]).add((short) 2, KMEnumTag.exp()); // curve tmpVariables[0] = decoder.decode( tmpVariables[0], @@ -2383,12 +2465,6 @@ private void importECKeys(byte[] scratchPad) { KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); - tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 2); - tmpVariables[1] = KMEnumTag.cast(tmpVariables[1]).getValue(); - // curve must be P_256 - if (tmpVariables[1] != KMType.P_256) { - KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); - } // initialize 256 bit p256 key for given private key and public key. ECPrivateKey ecKey = cryptoProvider.createEcKey( @@ -2413,8 +2489,8 @@ private void importECKeys(byte[] scratchPad) { // check the curve if present in key parameters. tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); if (tmpVariables[3] != KMType.INVALID_VALUE) { - if (tmpVariables[3] != tmpVariables[1]) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + if (tmpVariables[3] != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); } } else { // add the curve to scratchPad diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 350bd97a..d926cfa0 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -36,8 +36,8 @@ public class KMRepository { public static final byte BOOT_HASH_MAX_SIZE = 32; // Repository attributes private static KMRepository repository; + public boolean deviceUnlockPasswordOnly; private byte[] masterKey; - private byte[] hmacSeed; private byte[] sharedKey; private byte[] computedHmacKey; private byte[] hmacNonce; @@ -56,6 +56,7 @@ public class KMRepository { public boolean verifiedBootFlag; public boolean selfSignedBootFlag; public boolean deviceLockedFlag; + public byte[] deviceLockedTimestamp; public static KMRepository instance() { return repository; @@ -83,6 +84,10 @@ public KMRepository() { ((KMOperationState)operationStateTable[index]).reset(); index++; } + deviceLockedFlag = false; + deviceLockedTimestamp = new byte[8]; + deviceUnlockPasswordOnly = false; + Util.arrayFillNonAtomic(deviceLockedTimestamp,(short)0,(short)8,(byte)0); repository = this; } @@ -177,10 +182,6 @@ public byte[] getHeap() { return heap; } - public byte[] getHmacSeed() { - return hmacSeed; - } - public byte[] getSharedKey() { return sharedKey; } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 52bcf435..8850b683 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -658,14 +658,13 @@ private short makeImportKeyEcCmd() { ec192KeyPair.genKeyPair(); byte[] secret = new byte[24]; byte[] pubKey = new byte[52]; - short keyBlob = KMArray.instance((short)3); + short keyBlob = KMArray.instance((short)2); ECPrivateKey key1 = (ECPrivateKey) ec192KeyPair.getPrivate(); ECPublicKey key2 = (ECPublicKey) ec192KeyPair.getPublic(); short len = key1.getS(secret, (short)0); KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); len = key2.getW(pubKey, (short)0); KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(pubKey,(short)0,len)); - KMArray.cast(keyBlob).add((short)2, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); KMEncoder encoder = new KMEncoder(); byte[] blob = new byte[256]; len = encoder.encode(keyBlob,blob,(short)0); diff --git a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java index c6038ecd..800a0b98 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java @@ -35,6 +35,7 @@ import com.android.javacard.keymaster.KMKeyCharacteristics; import com.android.javacard.keymaster.KMKeyParameters; import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMRepository; import com.android.javacard.keymaster.KMType; import com.android.javacard.keymaster.KMVerificationToken; import com.licel.jcardsim.smartcardio.CardSimulator; @@ -44,6 +45,7 @@ import javacard.security.AESKey; import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; +import javacard.security.HMACKey; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; @@ -357,6 +359,159 @@ public void testRsaImportKeySuccess() { cleanUp(); } + @Test + public void testDeviceLocked(){ + init(); + byte[] hmacKey = new byte[32]; + cryptoProvider.newRandomNumber(hmacKey,(short)0,(short)32); + KMRepository.instance().initComputedHmac(hmacKey,(short)0,(short)32); + // generate aes key with unlocked_device_required + short aesKey = generateAesDesKey(KMType.AES,(short)128,null,null, true); + short keyBlobPtr = KMArray.cast(aesKey).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + // encrypt something + short inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + byte[] plainData= "Hello World 123!".getBytes(); + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false, false + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData,(short)0, (short)cipherData.length); + // create verification token + short verToken = KMVerificationToken.instance(); + KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short)1)); + verToken = signVerificationToken(verToken); + // device locked request + deviceLock(verToken); + // decrypt should fail + inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + short beginResp = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), KMKeyParameters.instance(inParams), (short)0); + Assert.assertEquals(beginResp,KMError.DEVICE_LOCKED); + short hwToken = KMHardwareAuthToken.instance(); + KMHardwareAuthToken.cast(hwToken).setTimestamp(KMInteger.uint_16((byte)2)); + KMHardwareAuthToken.cast(hwToken).setHwAuthenticatorType(KMEnum.instance(KMType.USER_AUTH_TYPE, (byte)KMType.PASSWORD)); + inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + hwToken = signHwToken(hwToken); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams),hwToken,null,false, false + ); + ret = KMArray.cast(ret).get((short)0); + Assert.assertEquals(KMInteger.cast(ret).getShort(), KMError.OK); + cleanUp(); + } + + private short signHwToken(short hwToken){ + short len = 0; + byte[] scratchPad = new byte[256]; + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + KMRepository.instance().getComputedHmacKey(), + (short) 0, + (short) KMRepository.instance().getComputedHmacKey().length); + byte[] mac = new byte[32]; + len = + cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, + mac, + (short)0); + KMHardwareAuthToken.cast(hwToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); + return hwToken; + } + private void deviceLock(short verToken) { + short req = KMArray.instance((short)2); + KMArray.cast(req).add((short)0, KMInteger.uint_8((byte)1)); + KMArray.cast(req).add((short)1, verToken); + CommandAPDU apdu = encodeApdu((byte)0x25,req); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 1); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + byte[] respBuf = response.getBytes(); + Assert.assertEquals(respBuf[0],KMError.OK); + } + + private short signVerificationToken(short verToken) { + byte[] scratchPad = new byte[256]; + byte[] authVer = "Auth Verification".getBytes(); + print(authVer,(short)0,(short)authVer.length); + // concatenation length will be 37 + length of verified parameters list - which is typically empty + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + short params = KMVerificationToken.cast(verToken).getParametersVerified(); + // Add "Auth Verification" - 17 bytes. + Util.arrayCopy(authVer,(short)0, scratchPad, (short)0, (short)authVer.length); + short len = (short)authVer.length; + // concatenate challenge - 8 bytes + short ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate security level - 4 bytes + ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate Parameters verified - blob of encoded data. + ptr = KMVerificationToken.cast(verToken).getParametersVerified(); + if (KMByteBlob.cast(ptr).length() != 0) { + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); + } + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + KMRepository.instance().getComputedHmacKey(), + (short) 0, + (short) KMRepository.instance().getComputedHmacKey().length); + ptr = KMVerificationToken.cast(verToken).getMac(); + byte[] mac = new byte[32]; + len = + cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, + mac, + (short)0); + KMVerificationToken.cast(verToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); + return verToken; + } + @Test public void testEcImportKeySuccess() { init(); @@ -381,10 +536,9 @@ public void testEcImportKeySuccess() { KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); short keyParams = KMKeyParameters.instance(arrPtr); short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 - short keyBlob = KMArray.instance((short)3); + short keyBlob = KMArray.instance((short)2); KMArray.cast(keyBlob).add((short)0, privBlob); KMArray.cast(keyBlob).add((short)1, pubBlob); - KMArray.cast(keyBlob).add((short)2, ecCurve); byte[] blob = new byte[128]; len = encoder.encode(keyBlob,blob,(short)0); keyBlob = KMByteBlob.instance(blob, (short)0, len); @@ -642,10 +796,11 @@ public short generateHmacKey(byte[] clientId, byte[] appData){ Assert.assertEquals(error, KMError.OK); return ret; } - public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData) { + public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData, boolean unlockReqd) { short tagCount = 7; if(clientId != null) tagCount++; if(appData != null) tagCount++; + if(unlockReqd)tagCount++; short arrPtr = KMArray.instance(tagCount); short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); @@ -669,6 +824,7 @@ public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, alg)); KMArray.cast(arrPtr).add(tagIndex++, purpose); KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + if(unlockReqd)KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.UNLOCKED_DEVICE_REQUIRED)); if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, @@ -1335,7 +1491,7 @@ public void testSignVerifyWithRsaSHA256PssWithUpdate(){ @Test public void testAbortOperation(){ init(); - short aesDesKeyArr = generateAesDesKey(KMType.AES, (short)128,null, null);; + short aesDesKeyArr = generateAesDesKey(KMType.AES, (short)128,null, null, false);; short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), @@ -1363,10 +1519,10 @@ public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, aesDesKeyArr = generateAesGcmKey((short)128,null,null); aesGcmFlag = true; } else { - aesDesKeyArr = generateAesDesKey(alg, (short) 128, null, null); + aesDesKeyArr = generateAesDesKey(alg, (short) 128, null, null, false); } } else{ - aesDesKeyArr = generateAesDesKey(alg, (short)168,null, null); + aesDesKeyArr = generateAesDesKey(alg, (short)168,null, null, false); } short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; @@ -1682,10 +1838,15 @@ public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToke KMArray.cast(ret).add((short)2, KMInteger.exp()); byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; + if(len > 5){ ret = decoder.decode(ret, respBuf, (short) 0, len); short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); Assert.assertEquals(error, KMError.OK); - return ret; + return ret;}else{ + if(len == 3) return respBuf[0]; + if(len == 4) return respBuf[1]; + return Util.getShort(respBuf,(short)0); + } } public short finish(short operationHandle, short data, byte[] signature, short inParams, short hwToken, short verToken) {