From c8e71b1ebdf6eb38109a4fff06e12747eb17749e Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Mon, 13 Dec 2021 07:10:49 +0000 Subject: [PATCH 01/15] Allow SIGN or ATTEST_KEY for generating a self signed cert --- .../javacard/seprovider/KMPoolManager.java | 1 + .../javacard/keymaster/KMKeymasterApplet.java | 10 +- HAL/Android.bp | 10 +- .../device_google_cuttlefish.patch | 8 +- .../hardware_interfaces.patch | 189 +++++++----------- .../system_sepolicy.patch | 24 ++- 6 files changed, 101 insertions(+), 141 deletions(-) diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java index 9938cd59..1606ce4e 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -14,6 +14,7 @@ * limitations under the License. */ package com.android.javacard.seprovider; +import javacard.framework.JCSystem; import javacard.security.KeyAgreement; import javacard.security.Signature; import javacardx.crypto.AEADCipher; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b8483275..1816e60e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1558,7 +1558,7 @@ private KMAttestationCert makeSelfSignedCert(short attPrivKey, short attPubKey, protected short getBootKey(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_KEY_SIZE, (byte)0); - short len = seProvider.getVerifiedBootHash(scratchPad,(short)0); + short len = seProvider.getBootKey(scratchPad, (short) 0); if(len != VERIFIED_BOOT_KEY_SIZE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -1567,7 +1567,7 @@ protected short getBootKey(byte[] scratchPad){ protected short getVerifiedBootHash(byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_HASH_SIZE, (byte)0); - short len = seProvider.getVerifiedBootHash(scratchPad,(short)0); + short len = seProvider.getVerifiedBootHash(scratchPad, (short) 0); if(len != VERIFIED_BOOT_HASH_SIZE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -3436,7 +3436,8 @@ private short getAttestationMode(short attKeyBlob, short attChallenge){ // Attestation challenge present then it is an error because no factory provisioned attest key if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) { KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); - } else if(KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[KEY_PARAMETERS])) { + } else if(KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[KEY_PARAMETERS]) || + KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[KEY_PARAMETERS])) { mode = KMType.SELF_SIGNED_CERT; }else{ mode = KMType.FAKE_CERT; @@ -3829,8 +3830,7 @@ public static short readROT(byte[] scratchPad) { short len = seProvider.getBootKey(scratchPad, (short)0); len += seProvider.getVerifiedBootHash(scratchPad, (short)len); short bootState = seProvider.getBootState(); - Util.setShort(scratchPad, (short)0,bootState); - len +=2; + len = Util.setShort(scratchPad, len, bootState); if(seProvider.isDeviceBootLocked()){ scratchPad[len] = (byte)1; }else{ diff --git a/HAL/Android.bp b/HAL/Android.bp index bf842f8b..22c1d878 100644 --- a/HAL/Android.bp +++ b/HAL/Android.bp @@ -29,9 +29,9 @@ cc_library { ], cflags:["-O0",], shared_libs: [ - "android.hardware.security.keymint-V1-ndk_platform", - "android.hardware.security.secureclock-V1-ndk_platform", - "android.hardware.security.sharedsecret-V1-ndk_platform", + "android.hardware.security.keymint-V1-ndk", + "android.hardware.security.secureclock-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", "lib_android_keymaster_keymint_utils", "libbase", "libcppbor_external", @@ -80,8 +80,8 @@ cc_binary { "-Wextra", ], shared_libs: [ - "android.hardware.security.keymint-V1-ndk_platform", - "android.hardware.security.sharedsecret-V1-ndk_platform", + "android.hardware.security.keymint-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", "libbase", "libbinder_ndk", "libcppbor_external", diff --git a/aosp_integration_patches/device_google_cuttlefish.patch b/aosp_integration_patches/device_google_cuttlefish.patch index 825676a9..1f5e0765 100644 --- a/aosp_integration_patches/device_google_cuttlefish.patch +++ b/aosp_integration_patches/device_google_cuttlefish.patch @@ -1,8 +1,8 @@ diff --git a/shared/device.mk b/shared/device.mk -index c86840539..e61e5276b 100644 +index 54042c107..622c6a549 100644 --- a/shared/device.mk +++ b/shared/device.mk -@@ -558,6 +558,9 @@ endif +@@ -607,6 +607,9 @@ endif PRODUCT_PACKAGES += \ $(LOCAL_KEYMINT_PRODUCT_PACKAGE) @@ -13,10 +13,10 @@ index c86840539..e61e5276b 100644 ifneq ($(LOCAL_PREFER_VENDOR_APEX),true) PRODUCT_COPY_FILES += \ diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts -index cebac258e..42d3ad6f9 100644 +index 55b8d964e..a0e88fb2e 100644 --- a/shared/sepolicy/vendor/file_contexts +++ b/shared/sepolicy/vendor/file_contexts -@@ -93,6 +93,7 @@ +@@ -85,6 +85,7 @@ /vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0 /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0 /vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0 diff --git a/aosp_integration_patches/hardware_interfaces.patch b/aosp_integration_patches/hardware_interfaces.patch index e76fa74c..bd06e4cb 100644 --- a/aosp_integration_patches/hardware_interfaces.patch +++ b/aosp_integration_patches/hardware_interfaces.patch @@ -1,38 +1,17 @@ diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml -index 0b779eee4..33bd0d97d 100644 +index c39db36ae..39cce859b 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml -@@ -330,6 +330,13 @@ +@@ -355,6 +355,7 @@ + + IRemotelyProvisionedComponent default - - -+ -+ android.hardware.security.keymint -+ -+ IRemotelyProvisionedComponent + strongbox -+ -+ - - android.hardware.light - 1 -@@ -491,6 +498,14 @@ - default -+ -+ android.hardware.security.sharedsecret -+ 1 -+ -+ ISharedSecret -+ strongbox -+ -+ - - android.hardware.sensors - 1.0 + diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp -index 26ed34427..2d5bc9575 100644 +index 64550eff2..49283e8e3 100644 --- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp @@ -198,7 +198,7 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { @@ -42,9 +21,9 @@ index 26ed34427..2d5bc9575 100644 - ASSERT_EQ(ErrorCode::OK, + auto result = GenerateKey(AuthorizationSetBuilder() - .RsaSigningKey(2048, 65537) + .RsaKey(2048, 65537) .AttestKey() -@@ -209,7 +209,13 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { +@@ -209,7 +209,14 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { .Authorization(TAG_NO_AUTH_REQUIRED) .SetDefaultValidity(), {} /* attestation signing key */, &attest_key.keyBlob, @@ -56,24 +35,24 @@ index 26ed34427..2d5bc9575 100644 + return; + } + ASSERT_EQ(ErrorCode::OK, result); ++ EXPECT_GT(attest_key_cert_chain.size(), 1); verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false); -@@ -297,7 +303,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { +@@ -297,7 +304,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { attest_key_opt = attest_key; } - EXPECT_EQ(ErrorCode::OK, + auto result = GenerateKey(AuthorizationSetBuilder() - .RsaSigningKey(2048, 65537) + .RsaKey(2048, 65537) .AttestKey() -@@ -308,8 +314,13 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { +@@ -308,7 +315,14 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), attest_key_opt, &key_blob_list[i], &attested_key_characteristics, - &cert_chain_list[i])); -- + &cert_chain_list[i]); + // Strongbox does not support Factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { @@ -81,106 +60,78 @@ index 26ed34427..2d5bc9575 100644 + return; + } + ASSERT_EQ(ErrorCode::OK, result); ++ + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - ASSERT_GT(cert_chain_list[i].size(), 0); -@@ -369,7 +380,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { +@@ -369,7 +383,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { attest_key_opt = attest_key; } - EXPECT_EQ(ErrorCode::OK, + auto result = GenerateKey(AuthorizationSetBuilder() - .EcdsaSigningKey(EcCurve::P_256) + .EcdsaKey(EcCurve::P_256) .AttestKey() -@@ -380,8 +391,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { +@@ -380,7 +394,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { .Authorization(TAG_NO_AUTH_REQUIRED) .SetDefaultValidity(), attest_key_opt, &key_blob_list[i], &attested_key_characteristics, - &cert_chain_list[i])); -- + &cert_chain_list[i]); -+ // Strongbox does not support Factory provisioned attestation key. ++ // Strongbox does not support Factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); + return; + } + ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - ASSERT_GT(cert_chain_list[i].size(), 0); -@@ -442,35 +458,40 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { - attest_key.keyBlob = key_blob_list[i - 1]; +@@ -443,8 +463,9 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { attest_key_opt = attest_key; } -- + + ErrorCode result; if ((i & 0x1) == 1) { - EXPECT_EQ(ErrorCode::OK, -- GenerateKey(AuthorizationSetBuilder() -- .EcdsaSigningKey(EcCurve::P_256) -- .AttestKey() -- .AttestationChallenge("foo") -- .AttestationApplicationId("bar") -- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -- .Authorization(TAG_NO_AUTH_REQUIRED) -- .SetDefaultValidity(), -- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -- &cert_chain_list[i])); + result = -+ GenerateKey(AuthorizationSetBuilder() -+ .EcdsaSigningKey(EcCurve::P_256) -+ .AttestKey() -+ .AttestationChallenge("foo") -+ .AttestationApplicationId("bar") -+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .SetDefaultValidity(), -+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -+ &cert_chain_list[i]); + GenerateKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() +@@ -455,9 +476,9 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ &cert_chain_list[i]); } else { - EXPECT_EQ(ErrorCode::OK, -- GenerateKey(AuthorizationSetBuilder() -- .RsaSigningKey(2048, 65537) -- .AttestKey() -- .AttestationChallenge("foo") -- .AttestationApplicationId("bar") -- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -- .Authorization(TAG_NO_AUTH_REQUIRED) -- .SetDefaultValidity(), -- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -- &cert_chain_list[i])); + result = -+ GenerateKey(AuthorizationSetBuilder() -+ .RsaSigningKey(2048, 65537) -+ .AttestKey() -+ .AttestationChallenge("foo") -+ .AttestationApplicationId("bar") -+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) -+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) -+ .Authorization(TAG_NO_AUTH_REQUIRED) -+ .SetDefaultValidity(), -+ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, -+ &cert_chain_list[i]); - } -- + GenerateKey(AuthorizationSetBuilder() + .RsaKey(2048, 65537) + .AttestKey() +@@ -468,8 +489,14 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ &cert_chain_list[i]); ++ } + // Strongbox does not support Factory provisioned attestation key. + if (SecLevel() == SecurityLevel::STRONGBOX) { + ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); + return; -+ } + } + ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); - ASSERT_GT(cert_chain_list[i].size(), 0); diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp -index 20324117b..741bcf8f6 100644 +index 6140df135..b73d325c6 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp -@@ -1145,6 +1145,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { +@@ -1156,6 +1156,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { } } @@ -197,7 +148,7 @@ index 20324117b..741bcf8f6 100644 switch (SecLevel()) { case SecurityLevel::SOFTWARE: diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h -index ec3fcf6a3..0561a9b94 100644 +index 7b3b9d4b4..c564d509a 100644 --- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h @@ -250,7 +250,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { @@ -232,10 +183,10 @@ index ec3fcf6a3..0561a9b94 100644 static vector build_params() { auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp -index 651b21f19..0e6e882d3 100644 +index 2a7911cc3..639b2a5dc 100644 --- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp +++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp -@@ -908,8 +908,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { +@@ -912,8 +912,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { for (auto key_size : ValidKeySizes(Algorithm::RSA)) { vector key_blob; vector key_characteristics; @@ -246,7 +197,7 @@ index 651b21f19..0e6e882d3 100644 .RsaSigningKey(key_size, 65537) .Digest(Digest::NONE) .Padding(PaddingMode::NONE) -@@ -919,8 +919,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { +@@ -923,8 +923,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -262,7 +213,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); -@@ -1037,8 +1043,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { +@@ -1045,8 +1051,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { vector key_blob; vector key_characteristics; @@ -272,7 +223,7 @@ index 651b21f19..0e6e882d3 100644 .RsaEncryptionKey(key_size, 65537) .Padding(PaddingMode::NONE) .AttestationChallenge(challenge) -@@ -1047,8 +1052,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { +@@ -1055,8 +1060,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -288,7 +239,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); AuthorizationSet auths; for (auto& entry : key_characteristics) { -@@ -1149,15 +1160,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { +@@ -1157,15 +1168,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { vector key_blob; vector key_characteristics; @@ -313,7 +264,7 @@ index 651b21f19..0e6e882d3 100644 } /* -@@ -1267,8 +1284,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { +@@ -1275,8 +1292,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { for (auto key_size : ValidKeySizes(Algorithm::RSA)) { vector key_blob; vector key_characteristics; @@ -324,7 +275,7 @@ index 651b21f19..0e6e882d3 100644 .RsaSigningKey(key_size, 65537) .Digest(Digest::NONE) .Padding(PaddingMode::NONE) -@@ -1279,7 +1296,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { +@@ -1287,7 +1304,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -340,7 +291,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); -@@ -1410,8 +1434,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { +@@ -1418,8 +1442,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { for (auto curve : ValidCurves()) { vector key_blob; vector key_characteristics; @@ -351,7 +302,7 @@ index 651b21f19..0e6e882d3 100644 .Authorization(TAG_NO_AUTH_REQUIRED) .EcdsaSigningKey(curve) .Digest(Digest::NONE) -@@ -1420,7 +1444,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { +@@ -1428,7 +1452,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), @@ -368,7 +319,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); -@@ -1497,6 +1529,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { +@@ -1506,6 +1538,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { // Tag not required to be supported by all KeyMint implementations. continue; } @@ -381,7 +332,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_EQ(result, ErrorCode::OK); ASSERT_GT(key_blob.size(), 0U); -@@ -1546,8 +1584,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { +@@ -1555,8 +1593,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(); builder.push_back(tag); @@ -398,7 +349,7 @@ index 651b21f19..0e6e882d3 100644 } } -@@ -1583,6 +1627,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { +@@ -1756,6 +1800,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) .SetDefaultValidity(), &key_blob, &key_characteristics); @@ -412,7 +363,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_EQ(result, ErrorCode::OK); ASSERT_GT(key_blob.size(), 0U); -@@ -1661,13 +1712,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { +@@ -1834,13 +1885,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { vector key_blob; vector key_characteristics; @@ -435,7 +386,7 @@ index 651b21f19..0e6e882d3 100644 } /* -@@ -1724,14 +1781,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { +@@ -1897,14 +1954,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { const string app_id(length, 'a'); vector key_blob; vector key_characteristics; @@ -459,7 +410,7 @@ index 651b21f19..0e6e882d3 100644 ASSERT_GT(key_blob.size(), 0U); CheckBaseParams(key_characteristics); CheckCharacteristics(key_blob, key_characteristics); -@@ -3762,25 +3826,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; +@@ -3945,25 +4009,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; * Verifies that raw RSA decryption works. */ TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { @@ -502,7 +453,7 @@ index 651b21f19..0e6e882d3 100644 } } -@@ -6297,7 +6363,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { +@@ -6503,7 +6569,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { size_t i; for (i = 0; i < max_operations; i++) { @@ -511,7 +462,7 @@ index 651b21f19..0e6e882d3 100644 if (ErrorCode::OK != result) { break; } -@@ -6305,12 +6371,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { +@@ -6511,12 +6577,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result); // Try again just in case there's a weird overflow bug EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, @@ -526,7 +477,7 @@ index 651b21f19..0e6e882d3 100644 AbortIfNeeded(); } -@@ -6409,7 +6475,6 @@ TEST_P(KeyAgreementTest, Ecdh) { +@@ -6615,7 +6681,6 @@ TEST_P(KeyAgreementTest, Ecdh) { OPENSSL_free(p); // Generate EC key in KeyMint (only access to public key material) @@ -534,7 +485,7 @@ index 651b21f19..0e6e882d3 100644 EXPECT_EQ( ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() -@@ -6418,7 +6483,6 @@ TEST_P(KeyAgreementTest, Ecdh) { +@@ -6624,7 +6689,6 @@ TEST_P(KeyAgreementTest, Ecdh) { .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) .Authorization(TAG_ALGORITHM, Algorithm::EC) .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) @@ -542,7 +493,7 @@ index 651b21f19..0e6e882d3 100644 .SetDefaultValidity())) << "Failed to generate key"; ASSERT_GT(cert_chain_.size(), 0); -@@ -6498,14 +6562,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { +@@ -6704,14 +6768,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { @@ -569,7 +520,7 @@ index 651b21f19..0e6e882d3 100644 } /* -@@ -6521,14 +6595,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { +@@ -6727,14 +6801,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { }); for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { @@ -594,7 +545,7 @@ index 651b21f19..0e6e882d3 100644 /* diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp -index 38f358686..74e44c7b4 100644 +index 76fb79b61..6e57d913b 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -164,6 +164,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam Date: Tue, 14 Dec 2021 04:24:02 +0000 Subject: [PATCH 02/15] No Buffering for block ciphers except AES/DES PKC7 padding. A block size is buffered for AESGCM Decryption operation. --- .../javacard/keymaster/KMKeymasterApplet.java | 82 +++++------- .../javacard/keymaster/KMOperationState.java | 119 +++--------------- .../android/javacard/keymaster/KMType.java | 8 +- HAL/JavacardKeyMintOperation.cpp | 16 +-- HAL/JavacardKeyMintOperation.h | 4 +- 5 files changed, 59 insertions(+), 170 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 1816e60e..f8554e24 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1770,10 +1770,6 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { break; case KMType.AES: case KMType.DES: - if(op.getBlockMode() == KMType.GCM && (len < (short) (op.getMacLength() / 8))){ - // Check if there is at least MAC Length bytes of input data - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } finishAesDesOperation(op); break; } @@ -1786,13 +1782,6 @@ private void finishAesDesOperation(KMOperationState op){ blockSize= DES_BLOCK_SIZE; } - // If no padding then data length must be block aligned - if(op.getPadding() == KMType.PADDING_NONE && op.getPurpose() == KMType.ENCRYPT - && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) - && ((short) (len % blockSize) != 0)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - if(op.getPurpose() == KMType.DECRYPT && len > 0 && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) && ((short) (len % blockSize) != 0)){ @@ -1800,26 +1789,32 @@ private void finishAesDesOperation(KMOperationState op){ } if (op.getBlockMode() == KMType.GCM) { + if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } if(op.isAesGcmUpdateAllowed()){ op.setAesGcmUpdateComplete(); } - //Get output size as it can be more then input size in case of GCM + // Get the output size len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); } // If padding i.e. pkcs7 then add padding to right // Output data can at most one block size more the input data in case of pkcs7 encryption // In case of gcm we will allocate extra memory of the size equal to blocksize. - short outData = KMByteBlob.instance((short) (len + blockSize)); - len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(outData).getBuffer(), - KMByteBlob.cast(outData).getStartOff()); - KMByteBlob.cast(outData).setLength(len); - data[OUTPUT_DATA] = outData; + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); + try { + len = op.getOperation().finish( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + } catch (CryptoException e) { + if (e.getReason() == CryptoException.ILLEGAL_USE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len); } private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) { @@ -2084,7 +2079,6 @@ private void processUpdateOperationCmd(APDU apdu) { authorizeUpdateFinishOperation(op, scratchPad); if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); // update the data. op.getOperation() .update( @@ -2099,28 +2093,21 @@ private void processUpdateOperationCmd(APDU apdu) { KMException.throwIt(KMError.OPERATION_CANCELLED); } short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - short additionalExpOutLen = 0; + short blockSize = DES_BLOCK_SIZE; if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.GCM || op.getBlockMode() == KMType.CTR) { - if(op.isAesGcmUpdateAllowed()){ - op.setAesGcmUpdateComplete(); - } - additionalExpOutLen = 16; - } else { - // input data must be block aligned. - // 128 bit block size - HAL must send block aligned data - if (len % AES_BLOCK_SIZE != 0 || len <= 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + blockSize = AES_BLOCK_SIZE; + if (op.getBlockMode() == KMType.GCM) { + // if input data present + if (len > 0) { + // no more future updateAAD allowed if input data present. + if (op.isAesGcmUpdateAllowed()) { + op.setAesGcmUpdateComplete(); + } + } } - } - } else if (op.getAlgorithm() == KMType.DES) { - // 64 bit block size - HAL must send block aligned data - if (len % DES_BLOCK_SIZE != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } } // Allocate output buffer as input data is already block aligned - data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + additionalExpOutLen)); + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); // Otherwise just update the data. // HAL consumes all the input and maintains a buffered data inside it. So the // applet sends the inputConsumed length as same as the input length. @@ -2139,16 +2126,7 @@ private void processUpdateOperationCmd(APDU apdu) { // Adjust the Output data if it is not equal to input data. // This happens in case of JCardSim provider. - if (len != KMByteBlob.cast(data[OUTPUT_DATA]).length()) { - data[INPUT_DATA] = data[OUTPUT_DATA]; - data[OUTPUT_DATA] = KMByteBlob.instance(len); - Util.arrayCopy( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), - len); - } + KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len); } if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index df2bcbb3..f9c8c68a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -46,10 +46,6 @@ public class KMOperationState { public static final byte DATA_SIZE = 9; public static final byte AUTH_TIME_SIZE = 8; - // short type - - //private static final byte KEY_SIZE = 6; - //private static final byte MAC_LENGTH = 8; // Handle - currently this is short private static final byte OP_HANDLE = 10; // Auth time 64 bits @@ -69,73 +65,15 @@ public class KMOperationState { private short[] data; private Object[] operation; - /* - private static final byte OPERATION = 0; - private static KMOperation op; - private static Object[] slot; - private static KMOperationState prototype; - private static boolean dFlag; -*/ public KMOperationState() { - // data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET); authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET); data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); operation = JCSystem.makeTransientObjectArray((short)1, JCSystem.CLEAR_ON_RESET); reset(); } -/* - private static KMOperationState proto() { - if (prototype == null) { - prototype = new KMOperationState(); - } - return prototype; - } - - public static KMOperationState instance(short opHandle, Object[] slot) { - KMOperationState opState = proto(); - opState.reset(); - Util.setShort(data, OP_HANDLE, opHandle); - KMOperationState.slot = slot; - return opState; - } - - public static KMOperationState read(byte[] oprHandle, short off, Object[] slot) { - KMOperationState opState = proto(); - opState.reset(); - Util.arrayCopy((byte[]) slot[DATA], (short) 0, data, (short) 0, (short) data.length); - Object[] ops = ((Object[]) slot[REFS]); - op = (KMOperation) ops[OPERATION]; - Util.setShort(data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); - KMOperationState.slot = slot; - return opState; - } - - public void persist() { - if (!dFlag) { - return; - } - KMRepository.instance().persistOperation(data, Util.getShort(data, OP_HANDLE), op); - dFlag = false; - } - - private void dataUpdated() { - dFlag = true; - } - public void release() { - Object[] ops = ((Object[]) slot[REFS]); - ((KMOperation) ops[OPERATION]).abort(); - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic( - (byte[]) slot[0], (short) 0, (short) ((byte[]) slot[0]).length, (byte) 0); - ops[OPERATION] = null; - JCSystem.commitTransaction(); - reset(); - } - -*/ public void reset() { byte index = 0; while(index < DATA_SIZE){ @@ -149,62 +87,49 @@ public void reset() { ((KMOperation)operation[0]).abort(); operation[0] = null; - /* - dFlag = false; - op = null; - slot = null; - - Util.arrayFillNonAtomic( - data, (short) 0, (short) data.length, (byte) 0); + } - */ -} public short compare(byte[] handle, short start, short len){ return Util.arrayCompare(handle, start, opHandle, (short)0, (short)opHandle.length); } + public void setKeySize(short keySize) { - // Util.setShort(data, KEY_SIZE, keySize); data[KEY_SIZE] = keySize; } public short getKeySize() { - // return Util.getShort(data, KEY_SIZE); return data[KEY_SIZE]; } public short getHandle(){ return KMInteger.uint_64(opHandle,(short)0); } + public void setHandle(short handle){ setHandle(KMInteger.cast(handle).getBuffer(), KMInteger.cast(handle).getStartOff(), KMInteger.cast(handle).length()); } + public short getHandle(byte[] buf, short start) { - //return Util.getShort(KMOperationState.data, OP_HANDLE); Util.arrayCopyNonAtomic(opHandle,(short)0, buf, start, (short)opHandle.length); return (short) opHandle.length; } public void setHandle(byte[] buf, short start, short len) { Util.arrayCopyNonAtomic(buf,start, opHandle, (short)0, (short) opHandle.length); - //return KMByteBlob.instance(opHandle,(short)0, (short)8); } + public short getPurpose() { return data[PURPOSE]; } public void setPurpose(short purpose) { data[PURPOSE] = purpose; -// dataUpdated(); } public void setOperation(KMOperation op) { operation[0] = op; - /*(op = operation; - dataUpdated(); - persist(); - */ } public KMOperation getOperation() { @@ -225,17 +150,14 @@ public boolean isSecureUserIdReqd() { public short getAuthTime() { return KMInteger.uint_64(authTime,(short) 0); - // return KMInteger.uint_64(data, (short) AUTH_TIME); } public void setAuthTime(short time){ setAuthTime(KMInteger.cast(time).getBuffer(), KMInteger.cast(time).getStartOff()); } + public void setAuthTime(byte[] timeBuf, short start) { Util.arrayCopyNonAtomic(timeBuf,start,authTime,(short)0, AUTH_TIME_SIZE); - /*Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); - dataUpdated(); - */ } public void setOneTimeAuthReqd(boolean flag) { @@ -244,7 +166,6 @@ public void setOneTimeAuthReqd(boolean flag) { } else { data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD)); } - // dataUpdated(); } public void setAuthTimeoutValidated(boolean flag) { @@ -253,7 +174,6 @@ public void setAuthTimeoutValidated(boolean flag) { } else { data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED)); } - //dataUpdated(); } public void setAuthPerOperationReqd(boolean flag) { @@ -262,7 +182,6 @@ public void setAuthPerOperationReqd(boolean flag) { } else { data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD)); } - //dataUpdated(); } public short getAlgorithm() { @@ -271,7 +190,6 @@ public short getAlgorithm() { public void setAlgorithm(short algorithm) { data[ALG] = algorithm; - //dataUpdated(); } public short getPadding() { @@ -280,7 +198,6 @@ public short getPadding() { public void setPadding(short padding) { data[PADDING] = padding; - //dataUpdated(); } public short getBlockMode() { @@ -289,7 +206,6 @@ public short getBlockMode() { public void setBlockMode(short blockMode) { data[BLOCK_MODE] = blockMode; - // dataUpdated(); } public short getDigest() { @@ -302,7 +218,6 @@ public short getMgfDigest() { public void setDigest(byte digest) { data[DIGEST] = digest; - // dataUpdated(); } public void setMgfDigest(byte mgfDigest) { @@ -315,24 +230,20 @@ public boolean isAesGcmUpdateAllowed() { public void setAesGcmUpdateComplete() { data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED)); - //dataUpdated(); } public void setAesGcmUpdateStart() { data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED); - //dataUpdated(); } public void setMacLength(short length) { data[MAC_LENGTH] = length; - //Util.setShort(data, MAC_LENGTH, length); - //dataUpdated(); } public short getMacLength() { return data[MAC_LENGTH]; - //return Util.getShort(data, MAC_LENGTH); } + public byte getBufferingMode(){ short alg = getAlgorithm(); short purpose = getPurpose(); @@ -350,19 +261,19 @@ public byte getBufferingMode(){ switch(alg) { case KMType.AES: - if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { - return KMType.BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN; + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN; } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; - } else if (blockMode == KMType.CBC || blockMode == KMType.ECB) { - return KMType.BUF_AES_BLOCK_ALIGN; } break; case KMType.DES: - if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { - return KMType.BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN; - } else { - return KMType.BUF_DES_BLOCK_ALIGN; + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN; } } return KMType.BUF_NONE; diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index 499c7636..f12d6114 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -349,10 +349,10 @@ public abstract class KMType { public static final byte BUF_NONE = 0; public static final byte BUF_RSA_NO_DIGEST = 1; public static final byte BUF_EC_NO_DIGEST = 2; - public static final byte BUF_AES_BLOCK_ALIGN = 3; - public static final byte BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN = 4; - public static final byte BUF_DES_BLOCK_ALIGN = 5; - public static final byte BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN = 6; + public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3; + public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4; + public static final byte BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN = 5; + public static final byte BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN = 6; public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7; protected static KMRepository repository; diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp index d4952aa0..a1882266 100644 --- a/HAL/JavacardKeyMintOperation.cpp +++ b/HAL/JavacardKeyMintOperation.cpp @@ -130,18 +130,18 @@ uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t bl uint16_t offset = 0; uint16_t remaining = 0; switch(bufferingMode_) { - case BufferingMode::BUF_AES_BLOCK_ALIGNED: - case BufferingMode::BUF_DES_BLOCK_ALIGNED: - offset = ((view.length / blockSize)) * blockSize; - break; - case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: offset = ((view.length / blockSize)) * blockSize; remaining = (view.length % blockSize); if (offset >= blockSize && remaining == 0) { offset -= blockSize; } break; + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + break; case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: if (view.length > macLength_) { offset = (view.length - macLength_); @@ -176,14 +176,14 @@ keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { view.start = 0; view.length = 0; break; - case BufferingMode::BUF_AES_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: blockAlign(view, AES_BLOCK_SIZE); break; case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: blockAlign(view, macLength_); break; - case BufferingMode::BUF_DES_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: blockAlign(view, DES_BLOCK_SIZE); break; @@ -193,7 +193,7 @@ keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { return KM_ERROR_OK; } -// Incermentally send the request using multiple updates. +// Incrementally send the request using multiple updates. keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view, HardwareAuthToken& authToken, TimeStampToken& timestampToken, diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h index 4f5fc046..642d65ee 100644 --- a/HAL/JavacardKeyMintOperation.h +++ b/HAL/JavacardKeyMintOperation.h @@ -46,9 +46,9 @@ enum class BufferingMode : int32_t { // will further check according to exact key size and crypto provider. EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate // upto exact keysize. - BUF_AES_BLOCK_ALIGNED = 3, // Buffer 15 bytes and reminder to make input data block aligned. + BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes. BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes. - BUF_DES_BLOCK_ALIGNED = 5, // Buffer 7 bytes and reminder to make input data block aligned. + BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 5, // Buffer 8 bytes. BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes. BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes. From 546143cfc54428ea6faa90452687f7370b8d900b Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Wed, 15 Dec 2021 01:02:15 +0000 Subject: [PATCH 03/15] Add MAX_USES_PER_BOOT support --- .../javacard/keymaster/KMAndroidSEApplet.java | 3 + .../javacard/keymaster/KMJCardSimApplet.java | 6 +- .../android/javacard/keymaster/KMError.java | 1 + .../javacard/keymaster/KMKeyParameters.java | 4 +- .../javacard/keymaster/KMKeymasterApplet.java | 52 +++++- .../javacard/keymaster/KMRepository.java | 171 +++++++++++++++--- 6 files changed, 206 insertions(+), 31 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 642775f1..16e40fc5 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -100,6 +100,9 @@ public void process(APDU apdu) { switch (apduIns) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); + // set boot params event is propagated to KMKeymasterApplet to handle any + // necessary cleanup after device reboot. + super.process(apdu); break; default: super.process(apdu); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 63c00331..55b38d7e 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -58,9 +58,6 @@ public class KMJCardSimApplet extends KMKeymasterApplet { KMJCardSimApplet() { super(new KMJCardSimulator()); - // setDummyBootParams(); - // setDummyPresharedKey(); - // setDummyAttestationIds(); } /** @@ -91,6 +88,9 @@ public void process(APDU apdu) { switch (apduIns) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); + // set boot params event is propagated to KMKeymasterApplet to handle any + // necessary cleanup after device reboot. + super.process(apdu); break; default: super.process(apdu); diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 0c5db5b5..992d804a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -59,6 +59,7 @@ public class KMError { public static final short INVALID_NONCE = 52; public static final short MISSING_MAC_LENGTH = 53; public static final short CALLER_NONCE_PROHIBITED = 55; + public static final short KEY_MAX_OPS_EXCEEDED = 56; public static final short INVALID_MAC_LENGTH = 57; public static final short MISSING_MIN_MAC_LENGTH = 58; public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 21dd7f5c..577a94d4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -133,8 +133,7 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, - KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS, - KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS }; byte index = 0; short tagInd; @@ -185,6 +184,7 @@ public static short makeSbEnforced(short keyParamsPtr, byte origin, KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, }; byte index = 0; short tagInd; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index f8554e24..2e1a036f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -97,8 +97,10 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x6E }; - public static final short MAX_COSE_BUF_SIZE = (short) 1024; + // TODO INS_SET_BOOT_PARAMS_CMD is duplicated again in this class. + // TODO Is there a better way to handle the cleanup when device reboots ? + private static final byte INS_SET_BOOT_PARAMS_CMD = 5; // 0x05 // Top 32 commands are reserved for provisioning. private static final byte KEYMINT_CMD_APDU_START = 0x20; @@ -400,6 +402,10 @@ public void process(APDU apdu) { processInitStrongBoxCmd(apdu); sendError(apdu, KMError.OK); return; + case INS_SET_BOOT_PARAMS_CMD: + // Clear all auth tags. + repository.removeAllAuthTags(); + break; case INS_GENERATE_KEY_CMD: processGenerateKey(apdu); break; @@ -1952,6 +1958,49 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP } } + private void authorizeKeyUsageForCount(byte[] scratchPad) { + short scratchPadOff = 0; + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 12, (byte) 0); + + short usageLimitBufLen = KMIntegerTag.getValue(scratchPad, scratchPadOff, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); + + if (usageLimitBufLen == KMType.INVALID_VALUE) { + return; + } + + if (usageLimitBufLen > 4) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + if (repository.isAuthTagPersisted(data[AUTH_TAG])) { + // Get current counter, update and increment it. + short len = repository + .getRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4)); + if (len != 4) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (0 >= KMInteger.unsignedByteArrayCompare(scratchPad, scratchPadOff, scratchPad, + (short) (scratchPadOff + 4), (short) 4)) { + KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); + } + // Increment the counter. + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0); + Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1); + KMUtils.add(scratchPad, scratchPadOff, (short) (scratchPadOff + len), + (short) (scratchPadOff + len * 2)); + + repository + .setRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), + len); + } else { + // Persist auth tag. + if (!repository.persistAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + } + } + private void authorizeDeviceUnlock(short hwToken) { // If device is locked and key characteristics requires unlocked device then check whether // HW auth token has correct timestamp. @@ -2579,6 +2628,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) } authorizeUserSecureIdAuthTimeout(op); authorizeDeviceUnlock(data[HW_TOKEN]); + authorizeKeyUsageForCount(scratchPad); // 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; diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 39340daf..ba0bb0f3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -34,7 +34,7 @@ public class KMRepository implements KMUpgradable { public static final short HEAP_SIZE = 15000; // Data table configuration - public static final short DATA_INDEX_SIZE = 12; + public static final short DATA_INDEX_SIZE = 16; public static final short DATA_INDEX_ENTRY_SIZE = 4; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -46,10 +46,14 @@ public class KMRepository implements KMUpgradable { public static final byte COMPUTED_HMAC_KEY = 0; public static final byte HMAC_NONCE = 1; public static final byte BOOT_OS_VERSION = 2; - public static final byte BOOT_OS_PATCH = 3; + public static final byte BOOT_OS_PATCH_LEVEL = 3; public static final byte VENDOR_PATCH_LEVEL = 4; public static final byte DEVICE_LOCKED_TIME = 5; public static final byte DEVICE_LOCKED = 6; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + public static final byte AUTH_TAG_1 = 8; + // Data Item sizes public static final short HMAC_SEED_NONCE_SIZE = 32; @@ -58,7 +62,10 @@ public class KMRepository implements KMUpgradable { public static final short OS_PATCH_SIZE = 4; public static final short VENDOR_PATCH_SIZE = 4; public static final short DEVICE_LOCK_TS_SIZE = 8; - public static final short DEVICE_LOCK_FLAG_SIZE = 1; + public static final short MAX_BLOB_STORAGE = 8; + public static final short AUTH_TAG_LENGTH = 16; + public static final short AUTH_TAG_COUNTER_SIZE = 4; + public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); // Class Attributes private byte[] heap; @@ -186,34 +193,36 @@ private void newDataTable(boolean isUpgrading) { } private void clearDataEntry(short id) { - JCSystem.beginTransaction(); id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen != 0) { short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0); + JCSystem.commitTransaction(); } - JCSystem.commitTransaction(); } private void writeDataEntry(short id, byte[] buf, short offset, short len) { - JCSystem.beginTransaction(); short dataPtr; id = (short) (id * DATA_INDEX_ENTRY_SIZE); short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); if (dataLen == 0) { dataPtr = dataAlloc(len); + JCSystem.beginTransaction(); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len); Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); } else { if (len != dataLen) { KMException.throwIt(KMError.UNKNOWN_ERROR); } dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); + JCSystem.commitTransaction(); } - JCSystem.commitTransaction(); } private short readDataEntry(short id, byte[] buf, short offset) { @@ -280,7 +289,7 @@ public short getVendorPatchLevel() { public short getOsPatch() { - short blob = readData(BOOT_OS_PATCH); + short blob = readData(BOOT_OS_PATCH_LEVEL); if (blob != 0) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); @@ -289,14 +298,17 @@ public short getOsPatch() { } } + private boolean readBoolean(short id) { + short blob = readData(id); + return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; + } + public boolean getDeviceLock() { - short blob = readData(DEVICE_LOCKED); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; + return readBoolean(DEVICE_LOCKED); } public boolean getDeviceLockPasswordOnly() { - short blob = readData(DEVICE_LOCKED); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFD) != 0; + return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY); } public short getDeviceTimeStamp() { @@ -323,24 +335,22 @@ public void setVendorPatchLevel(byte[] buf, short start, short len) { writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len); } - public void setDeviceLock(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); + private void writeBoolean(short id, boolean flag) { + short start = alloc((short) 1); if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x01); + (getHeap())[start] = (byte) 0x01; } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFE); + (getHeap())[start] = (byte) 0x00; } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(id, getHeap(), start, (short) 1); + } + + public void setDeviceLock(boolean flag) { + writeBoolean(DEVICE_LOCKED, flag); } public void setDeviceLockPasswordOnly(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); - if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x02); - } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFD); - } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); + writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag); } public void setDeviceLockTimestamp(byte[] buf, short start, short len) { @@ -358,8 +368,119 @@ public void setOsPatch(byte[] buf, short start, short len) { if (len != OS_PATCH_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - writeDataEntry(BOOT_OS_PATCH, buf, start, len); + writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len); + } + + private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) { + readDataEntry(tagId, buf, offset); + return (0 == buf[offset]); + } + + private void writeAuthTagState(byte[] buf, short offset, byte state) { + buf[offset] = state; + } + + public boolean persistAuthTag(short authTag) { + + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + + short authTagEntry = alloc(AUTH_TAG_ENTRY_SIZE); + short scratchPadOff = alloc(AUTH_TAG_ENTRY_SIZE); + byte[] scratchPad = getHeap(); + writeAuthTagState(getHeap(), authTagEntry, (byte) 1); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + getHeap(), (short) (authTagEntry + 1), AUTH_TAG_LENGTH); + Util.setShort(getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), + (short) 1); + short index = 0; + while (index < MAX_BLOB_STORAGE) { + if ((dataLength((short) (index + AUTH_TAG_1)) == 0) || + isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) { + + writeDataEntry((short) (index + AUTH_TAG_1), getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE); + return true; + } + index++; + } + return false; + } + + public void removeAllAuthTags() { + short index = 0; + while (index < MAX_BLOB_STORAGE) { + clearDataEntry((short) (index + AUTH_TAG_1)); + index++; + } + } + + public boolean isAuthTagPersisted(short authTag) { + return (KMType.INVALID_VALUE != findTag(authTag)); + } + + private short findTag(short authTag) { + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + short index = 0; + short found; + short offset = alloc(AUTH_TAG_ENTRY_SIZE); + while (index < MAX_BLOB_STORAGE) { + if (dataLength((short) (index + AUTH_TAG_1)) != 0) { + readDataEntry((short) (index + AUTH_TAG_1), + getHeap(), offset); + found = + Util.arrayCompare( + getHeap(), + (short) (offset + 1), + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AUTH_TAG_LENGTH); + if (found == 0) { + return (short) (index + AUTH_TAG_1); + } + } + index++; + } + return KMType.INVALID_VALUE; } + + public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) { + short tag = findTag(authTag); + short blob; + if (tag != KMType.INVALID_VALUE) { + blob = readData(tag); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(blob).getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1), + out, + outOff, + AUTH_TAG_COUNTER_SIZE); + return AUTH_TAG_COUNTER_SIZE; + } + return (short) 0; + } + + public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) { + short tag = findTag(authTag); + if (tag != KMType.INVALID_VALUE) { + short dataPtr = readData(tag); + Util.arrayCopyNonAtomic( + buf, + off, + KMByteBlob.cast(dataPtr).getBuffer(), + (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1), + len); + writeDataEntry(tag, + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + KMByteBlob.cast(dataPtr).length()); + } + } + @Override public void onSave(Element ele) { ele.write(dataIndex); From 743cdd85e619572b1138b7c26b17eb5b0542463f Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Wed, 15 Dec 2021 02:31:26 +0000 Subject: [PATCH 04/15] Read data from the repository's datatable shall return INVALID_VALUE if read fails --- .../android/javacard/keymaster/KMUtils.java | 6 ++--- .../android/javacard/keymaster/KMUtils.java | 6 ++--- .../javacard/keymaster/KMRepository.java | 22 ++++++++++--------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index 82093450..e707e823 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -103,10 +103,8 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient - // is - // multiple - // of 4 + // quotient is multiple of 4 + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); yrsCount = (short) (yrsCount * 4); // number of yrs. // copy reminder as new dividend Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index 06c77ff6..b9bdd1f5 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -102,10 +102,8 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient - // is - // multiple - // of 4 + // quotient is multiple of 4. + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); yrsCount = (short) (yrsCount * 4); // number of yrs. // copy reminder as new dividend Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index ba0bb0f3..db1a5ebc 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -257,19 +257,19 @@ public short getComputedHmacKey() { } public short readData(short id) { - short blob = KMByteBlob.instance(dataLength(id)); - if (readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()) - == 0) { - return 0; + short len = dataLength(id); + if (len != 0) { + short blob = KMByteBlob.instance(dataLength(id)); + readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } - return blob; + return KMType.INVALID_VALUE; } private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; public short getOsVersion() { short blob = readData(BOOT_OS_VERSION); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -279,7 +279,7 @@ public short getOsVersion() { public short getVendorPatchLevel() { short blob = readData(VENDOR_PATCH_LEVEL); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -287,10 +287,9 @@ public short getVendorPatchLevel() { } } - public short getOsPatch() { short blob = readData(BOOT_OS_PATCH_LEVEL); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { @@ -300,6 +299,9 @@ public short getOsPatch() { private boolean readBoolean(short id) { short blob = readData(id); + if (blob == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_DATA); + } return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; } @@ -313,7 +315,7 @@ public boolean getDeviceLockPasswordOnly() { public short getDeviceTimeStamp() { short blob = readData(DEVICE_LOCKED_TIME); - if (blob != 0) { + if (blob != KMType.INVALID_VALUE) { return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); } else { From 866552904bad2736383c6416aa6d51048d01ebaa Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Wed, 15 Dec 2021 23:20:47 +0000 Subject: [PATCH 05/15] Fixed the issue in UpgradeKey command. Took the change from commit 24a4375ea2d10efcd6fa69e71d3839c97b823294 of jc_keymaster_41_android_11 branch. --- .../javacard/keymaster/KMKeymasterApplet.java | 98 +++++++------------ 1 file changed, 38 insertions(+), 60 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 2e1a036f..dfe96f67 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -904,6 +904,31 @@ private short upgradeKeyCmd(APDU apdu){ return receiveIncoming(apdu, cmd); } + private boolean isKeyUpgradeRequired(short tag, short systemParam) { + // validate the tag and check if key needs upgrade. + short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); + tagValue = KMIntegerTag.cast(tagValue).getValue(); + short zero = KMInteger.uint_8((byte) 0); + if (tagValue != KMType.INVALID_VALUE) { + // OS version in key characteristics must be less the OS version stored in Javacard or the + // stored version must be zero. Then only upgrade is allowed else it is invalid argument. + if ((tag == KMType.OS_VERSION + && KMInteger.compare(tagValue, systemParam) == 1 + && KMInteger.compare(systemParam, zero) == 0)) { + // Key needs upgrade. + return true; + } else if ((KMInteger.compare(tagValue, systemParam) == -1)) { + // Each os version or patch level associated with the key must be less than it's + // corresponding value stored in Javacard, then only upgrade is allowed otherwise it + // is invalid argument. + return true; + } else if (KMInteger.compare(tagValue, systemParam) == 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + return false; + } + private void processUpgradeKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. short cmd = upgradeKeyCmd(apdu); @@ -912,7 +937,7 @@ private void processUpgradeKeyCmd(APDU apdu) { data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); //tmpVariables[0] - short appId = + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); if (appId != KMTag.INVALID_VALUE) { data[APP_ID] = KMByteTag.cast(appId).getValue(); @@ -924,67 +949,20 @@ private void processUpgradeKeyCmd(APDU apdu) { } // parse existing key blob parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); - // validate characteristics to be upgraded. - short osVersion = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - short osPatchLvl = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - osPatchLvl = KMIntegerTag.cast(osPatchLvl).getValue(); - short provOsVersion = repository.getOsVersion(); - short provOsPatch = repository.getOsPatch(); - short zero = KMInteger.uint_8((byte) 0); - short error = KMError.OK; - if (osVersion != KMType.INVALID_VALUE) { - // os version in key characteristics must be less the os version stored in javacard or the - // stored version must be zero. Then only upgrade is allowed else it is invalid argument. - if (KMInteger.compare(osVersion, provOsVersion) != -1 - && KMInteger.compare(provOsVersion, zero) != 0) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - if (osPatchLvl != KMType.INVALID_VALUE) { - // The key characteristics should have had os patch level < os patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(osPatchLvl, provOsPatch) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - - //Compare vendor patch levels - short vendorPatchLvl = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, data[HW_PARAMETERS]); - vendorPatchLvl = KMIntegerTag.cast(vendorPatchLvl).getValue(); - short provVendorPatch = repository.getVendorPatchLevel(); - if (vendorPatchLvl != KMType.INVALID_VALUE) { - // The key characteristics should have had vendor patch level < vendor patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(vendorPatchLvl, provVendorPatch) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - - //Compare boot patch levels - short bootPatch = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, data[HW_PARAMETERS]); - bootPatch = KMIntegerTag.cast(bootPatch).getValue(); - short provBootPatch = getBootPatchLevel(scratchPad); - if (bootPatch != KMType.INVALID_VALUE) { - // The key characteristics should have had boot patch level < boot patch level stored in javacard - // then only upgrade is allowed. - if (KMInteger.compare(bootPatch, provBootPatch) != -1) { - // Key Should not be upgraded, but error code should be OK, As per VTS. - error = KMError.INVALID_ARGUMENT; - } - } - - if (error != KMError.INVALID_ARGUMENT) { + boolean isKeyUpgradeRequired = false; + // Check if key requires upgrade. + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); + isKeyUpgradeRequired |= + isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); + // Get boot patch level. + short len = seProvider.getBootPatchLevel(scratchPad, (short) 0); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, + KMByteBlob.instance(scratchPad, (short) 0, len)); + + if (isKeyUpgradeRequired) { // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); - makeKeyCharacteristics(scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); } else { From 5447cc27c3a16fe654ee2e3951570330c3655314 Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Thu, 16 Dec 2021 03:10:24 +0000 Subject: [PATCH 06/15] Fixed the issue with readData in Repository class --- Applet/src/com/android/javacard/keymaster/KMRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index db1a5ebc..9ea9dfab 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -261,6 +261,7 @@ public short readData(short id) { if (len != 0) { short blob = KMByteBlob.instance(dataLength(id)); readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + return blob; } return KMType.INVALID_VALUE; } From a1612a9a5a9a266f905f7dcf3c8a37ffbdc5783d Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Thu, 16 Dec 2021 03:45:54 +0000 Subject: [PATCH 07/15] Fixed the issue with UpgradeKey --- .../src/com/android/javacard/keymaster/KMKeymasterApplet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index dfe96f67..3687efa2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -956,9 +956,9 @@ private void processUpgradeKeyCmd(APDU apdu) { isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); // Get boot patch level. - short len = seProvider.getBootPatchLevel(scratchPad, (short) 0); + seProvider.getBootPatchLevel(scratchPad, (short) 0); isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, - KMByteBlob.instance(scratchPad, (short) 0, len)); + KMInteger.uint_32(scratchPad, (short) 0)); if (isKeyUpgradeRequired) { // copy origin From 3d8435c312b6a24d0c6271d3136517eb6a4469dc Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Thu, 16 Dec 2021 03:49:05 +0000 Subject: [PATCH 08/15] Added support for EARLY_BOOT_ENDED --- .../javacard/keymaster/KMKeymasterApplet.java | 15 +++++++++++++-- .../javacard/keymaster/KMRepository.java | 11 ++++++++++- HAL/JavacardKeyMintDevice.cpp | 17 +++++++++++++++-- HAL/JavacardKeyMintDevice.h | 6 +++++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 3687efa2..ae097b47 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -405,6 +405,8 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: // Clear all auth tags. repository.removeAllAuthTags(); + // clear early boot ended status. + repository.setEarlyBootEndedStatus(false); break; case INS_GENERATE_KEY_CMD: processGenerateKey(apdu); @@ -525,7 +527,7 @@ private void freeOperations() { } private void processEarlyBootEndedCmd(APDU apdu) { - KMException.throwIt(KMError.UNIMPLEMENTED); + repository.setEarlyBootEndedStatus(true); } private short deviceLockedCmd(APDU apdu){ @@ -2607,6 +2609,13 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizeUserSecureIdAuthTimeout(op); authorizeDeviceUnlock(data[HW_TOKEN]); authorizeKeyUsageForCount(scratchPad); + + //Validate early boot + if (repository.getEarlyBootEndedStatus()) { + KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMError.INVALID_KEY_BLOB); + } + // 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; @@ -2961,7 +2970,8 @@ private void processImportKeyCmd(APDU apdu) { private void validateImportKey(short params, short keyFmt){ // Rollback protection not supported KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - // vts disallows importing EARLY_BOOT keys + // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is + // provided to IKeyMintDevice::importKey KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); // Importing Bootloader only keys not supported. KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); @@ -3384,6 +3394,7 @@ private void processGenerateKey(APDU apdu) { KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); // BOOTLOADER_ONLY keys not supported. KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + // As per specification Early boot keys may be created after early boot ended. // Algorithm must be present KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 9ea9dfab..b4158b9c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -34,7 +34,7 @@ public class KMRepository implements KMUpgradable { public static final short HEAP_SIZE = 15000; // Data table configuration - public static final short DATA_INDEX_SIZE = 16; + public static final short DATA_INDEX_SIZE = 17; public static final short DATA_INDEX_ENTRY_SIZE = 4; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; @@ -53,6 +53,7 @@ public class KMRepository implements KMUpgradable { public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 public static final byte AUTH_TAG_1 = 8; + public static final byte EARLY_BOOT_ENDED_FLAG = 16; // Data Item sizes @@ -314,6 +315,10 @@ public boolean getDeviceLockPasswordOnly() { return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY); } + public boolean getEarlyBootEndedStatus() { + return readBoolean(EARLY_BOOT_ENDED_FLAG); + } + public short getDeviceTimeStamp() { short blob = readData(DEVICE_LOCKED_TIME); if (blob != KMType.INVALID_VALUE) { @@ -363,6 +368,10 @@ public void setDeviceLockTimestamp(byte[] buf, short start, short len) { writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len); } + public void setEarlyBootEndedStatus(boolean flag) { + writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); + } + public void clearDeviceLockTimeStamp() { clearDataEntry(DEVICE_LOCKED_TIME); } diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp index 402504b3..6b6932cf 100644 --- a/HAL/JavacardKeyMintDevice.cpp +++ b/HAL/JavacardKeyMintDevice.cpp @@ -116,7 +116,6 @@ ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyPa cppbor::Array request; vector updatedParams(keyParams); - // Add CREATION_DATETIME if required, as secure element is not having clock. if (!findTag(keyParams, Tag::CREATION_DATETIME) && !findTag(keyParams, Tag::ACTIVE_DATETIME)) { updatedParams.push_back(km_utils::kmParam2Aidl( @@ -164,7 +163,6 @@ ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector& wra vector authList; KeyFormat keyFormat; std::vector wrappedKeyDescription; - keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, authList, keyFormat, wrappedKeyDescription); if (errorCode != KM_ERROR_OK) { @@ -288,6 +286,10 @@ ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector cbor_.addKeyparameters(array, params); HardwareAuthToken token = authToken.value_or(HardwareAuthToken()); cbor_.addHardwareAuthToken(array, token); + + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array); if (err != KM_ERROR_OK) { LOG(ERROR) << "Error in sending in begin."; @@ -329,9 +331,20 @@ JavacardKeyMintDevice::deviceLocked(bool passwordOnly, return ScopedAStatus::ok(); } +void JavacardKeyMintDevice::handleSendEarlyBootEndedEvent() { + if (isEarlyBootEventPending) { + LOG(INFO) << "JavacardKeyMintDevice::handleSendEarlyBootEndedEvent send earlyBootEnded Event."; + if (earlyBootEnded().isOk()) { + isEarlyBootEventPending = false; + } + } +} + ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() { auto [item, err] = card_->sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); if (err != KM_ERROR_OK) { + // Incase of failure cache the event and send in the next immediate request to Applet. + isEarlyBootEventPending = true; return km_utils::kmError2ScopedAStatus(err); } return ScopedAStatus::ok(); diff --git a/HAL/JavacardKeyMintDevice.h b/HAL/JavacardKeyMintDevice.h index 51ee0c14..4bbbaa87 100644 --- a/HAL/JavacardKeyMintDevice.h +++ b/HAL/JavacardKeyMintDevice.h @@ -35,7 +35,8 @@ using std::vector; class JavacardKeyMintDevice : public BnKeyMintDevice { public: explicit JavacardKeyMintDevice(shared_ptr card) - : securitylevel_(SecurityLevel::STRONGBOX), card_(card) { + : securitylevel_(SecurityLevel::STRONGBOX), card_(card), + isEarlyBootEventPending(false) { card_->initializeJavacard(); } virtual ~JavacardKeyMintDevice() {} @@ -106,9 +107,12 @@ class JavacardKeyMintDevice : public BnKeyMintDevice { ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info); + void handleSendEarlyBootEndedEvent(); + const SecurityLevel securitylevel_; const shared_ptr card_; CborConverter cbor_; + bool isEarlyBootEventPending; }; } // namespace aidl::android::hardware::security::keymint From 12d39bd1d8522e1d07742af797f228d127b6d425 Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Mon, 20 Dec 2021 03:11:55 +0000 Subject: [PATCH 09/15] Added USER_SECURE_ID changes from Keymaster4.1 master branch --- .../javacard/keymaster/KMAndroidSEApplet.java | 7 +- .../keymaster/KMAttestationCertImpl.java | 57 +-- .../javacard/keymaster/KMConfigurations.java | 23 ++ .../android/javacard/keymaster/KMUtils.java | 26 ++ .../javacard/keymaster/KMConfigurations.java | 23 ++ .../javacard/keymaster/KMJCardSimApplet.java | 9 +- .../android/javacard/keymaster/KMUtils.java | 26 ++ .../android/javacard/keymaster/KMArray.java | 5 + .../android/javacard/keymaster/KMEnumTag.java | 3 +- .../android/javacard/keymaster/KMInteger.java | 8 + .../javacard/keymaster/KMIntegerArrayTag.java | 12 +- .../javacard/keymaster/KMIntegerTag.java | 2 + .../javacard/keymaster/KMKeyParameters.java | 59 +++ .../javacard/keymaster/KMKeymasterApplet.java | 355 +++++++++++------- .../javacard/keymaster/KMOperationState.java | 154 +++++--- .../android/javacard/keymaster/KMType.java | 2 + HAL/CborConverter.cpp | 38 +- 17 files changed, 565 insertions(+), 244 deletions(-) create mode 100644 Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java create mode 100644 Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 16e40fc5..8da8b8ed 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -59,8 +59,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; private static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; - private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; - private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -100,9 +99,6 @@ public void process(APDU apdu) { switch (apduIns) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - // set boot params event is propagated to KMKeymasterApplet to handle any - // necessary cleanup after device reboot. - super.process(apdu); break; default: super.process(apdu); @@ -138,7 +134,6 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - provisionStatus |= PROVISION_STATUS_BOOT_PARAM; break; case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index c1cdb91d..8f009ae2 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -89,6 +89,32 @@ public class KMAttestationCertImpl implements KMAttestationCert { 0x05, 0x00 }; + + + // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. + private static final short[] swTagIds = { + KMType.ATTESTATION_APPLICATION_ID, + KMType.CREATION_DATETIME, + KMType.USAGE_EXPIRE_DATETIME, + KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.ACTIVE_DATETIME, + KMType.UNLOCKED_DEVICE_REQUIRED + }; + + // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. + private static final short[] hwTagIds = { + KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, + KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, + KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, + KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, + KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, + KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, + KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, + KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, + KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, + KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, + KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; + // Validity is not fixed field // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys private static final byte[] X509Subject = { @@ -505,44 +531,27 @@ private static void pushKeyDescription() { private static void pushSWParams() { short last = stackPtr; - // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. - short[] tagIds = { - KMType.ATTESTATION_APPLICATION_ID, KMType.CREATION_DATETIME, - KMType.USAGE_EXPIRE_DATETIME, KMType.ORIGINATION_EXPIRE_DATETIME, - KMType.ACTIVE_DATETIME, KMType.UNLOCKED_DEVICE_REQUIRED}; byte index = 0; + short length = (short) swTagIds.length; do { - pushParams(swParams, swParamsIndex, tagIds[index]); - } while (++index < tagIds.length); + pushParams(swParams, swParamsIndex, swTagIds[index]); + } while (++index < length); pushSequenceHeader((short) (last - stackPtr)); } private static void pushHWParams() { short last = stackPtr; - // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. - short[] tagIds = { - KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, - KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, - KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, - KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, - KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, - KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, - KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, - KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, - KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, - KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, - KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; - byte index = 0; + short length = (short) hwTagIds.length; do { - if (tagIds[index] == KMType.ROOT_OF_TRUST) { + if (hwTagIds[index] == KMType.ROOT_OF_TRUST) { pushRoT(); continue; } - if (pushParams(hwParams, hwParamsIndex, tagIds[index])) { + if (pushParams(hwParams, hwParamsIndex, hwTagIds[index])) { continue; } - } while (++index < tagIds.length); + } while (++index < length); pushSequenceHeader((short) (last - stackPtr)); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java new file mode 100644 index 00000000..3eb4a2e4 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -0,0 +1,23 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.keymaster; + +public class KMConfigurations { + // Machine types + public static final byte LITTLE_ENDIAN = 0x00; + public static final byte BIG_ENDIAN = 0x01; + public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index e707e823..f21eb868 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -54,6 +54,8 @@ public class KMUtils { 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 public static final short year2051 = 2051; public static final short year2020 = 2020; + // Convert to milliseconds constants + public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3}; // -------------------------------------- public static short convertToDate(short time, byte[] scratchPad, @@ -303,6 +305,14 @@ public static byte compare(byte[] buf, short lhs, short rhs) { return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); } + public static void shiftLeft(byte[] buf, short start, short count) { + short index = 0; + while (index < count) { + shiftLeft(buf, start); + index++; + } + } + public static void shiftLeft(byte[] buf, short start) { byte index = 7; byte carry = 0; @@ -417,4 +427,20 @@ public static void computeOnesCompliment(byte[] buf, short offset, short len) { index++; } } + + // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) + public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, + short scratchPadOff) { + short index = 0; + short length = (short) SEC_TO_MILLIS_SHIFT_POS.length; + while (index < length) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]); + Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8); + add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff)); + Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8); + Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0); + index++; + } + } } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java new file mode 100644 index 00000000..3eb4a2e4 --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -0,0 +1,23 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.keymaster; + +public class KMConfigurations { + // Machine types + public static final byte LITTLE_ENDIAN = 0x00; + public static final byte BIG_ENDIAN = 0x01; + public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 55b38d7e..a2c81488 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -45,9 +45,8 @@ public class KMJCardSimApplet extends KMKeymasterApplet { private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; private static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; - private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; - private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; - private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x60; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; + private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x40; private static final byte PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = (byte) 0x80; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -88,9 +87,6 @@ public void process(APDU apdu) { switch (apduIns) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - // set boot params event is propagated to KMKeymasterApplet to handle any - // necessary cleanup after device reboot. - super.process(apdu); break; default: super.process(apdu); @@ -120,7 +116,6 @@ public void process(APDU apdu) { break; case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - provisionStatus |= PROVISION_STATUS_BOOT_PARAM; break; case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: processProvisionDeviceUniqueKey(apdu); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index b9bdd1f5..3e9002e2 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -53,6 +53,8 @@ public class KMUtils { 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 public static final short year2051 = 2051; public static final short year2020 = 2020; + // Convert to milliseconds constants + public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3}; // -------------------------------------- public static short convertToDate(short time, byte[] scratchPad, @@ -302,6 +304,14 @@ public static byte compare(byte[] buf, short lhs, short rhs) { return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); } + public static void shiftLeft(byte[] buf, short start, short count) { + short index = 0; + while (index < count) { + shiftLeft(buf, start); + index++; + } + } + public static void shiftLeft(byte[] buf, short start) { byte index = 7; byte carry = 0; @@ -416,4 +426,20 @@ public static void computeOnesCompliment(byte[] buf, short offset, short len) { index++; } } + + // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) + public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, + short scratchPadOff) { + short index = 0; + short length = (short) SEC_TO_MILLIS_SHIFT_POS.length; + while (index < length) { + Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); + shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]); + Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8); + add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff)); + Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8); + Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0); + index++; + } + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index c1540fa7..a468e924 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -141,4 +141,9 @@ public byte[] getBuffer() { return heap; } + public void deleteLastEntry() { + short len = length(); + Util.setShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), + (short) (len - 1)); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 447b1b06..3f45c304 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -105,8 +105,7 @@ public static void create() { new byte[]{RSA, DES, EC, AES, HMAC}, new byte[]{P_224, P_256, P_384, P_521}, new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte) (PASSWORD & FINGERPRINT), - ANY}, + new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY}, new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index 8ae8c8c9..c9ed7a3a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -147,6 +147,14 @@ 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) { + dest[destOff++] = heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + index)]; + index--; + } + return length(); + } public short getShort() { return Util.getShort(heap, (short) (getStartOff() + 2)); diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 6f7cc2ff..e62ce9fe 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -46,7 +46,7 @@ public static short exp(short tagType) { if (!validateTagType(tagType)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - short arrPtr = KMArray.exp(KMType.INTEGER_TYPE); + short arrPtr = KMArray.exp(KMInteger.exp()); short ptr = instance(TAG_TYPE, (short) 6); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG); @@ -150,4 +150,14 @@ public static boolean contains(short tagId, short tagValue, short params) { return false; } + public boolean contains(short tagValue) { + short index = 0; + while (index < length()) { + if (KMInteger.compare(tagValue, get(index)) == 0) { + return true; + } + index++; + } + return false; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 2e22f183..5311cbd8 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -53,6 +53,8 @@ public class KMIntegerTag extends KMTag { CERTIFICATE_NOT_BEFORE, CERTIFICATE_NOT_AFTER, USAGE_COUNT_LIMIT, + // custom tag + AUTH_TIMEOUT_MILLIS, }; private KMIntegerTag() { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 577a94d4..7fd115ca 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -30,6 +30,10 @@ public class KMKeyParameters extends KMType { private static KMKeyParameters prototype; + private static final short[] customTags = { + KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + }; + private KMKeyParameters() { } @@ -389,6 +393,9 @@ public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) { } index++; } + // Add custom tags at the end of the array. So it becomes easy to + // delete them when sending key characteristics back to HAL. + arrInd = addCustomTags(keyParamsPtr, scratchPad, arrInd); return createKeyParameters(scratchPad, (short) (arrInd / 2)); } @@ -457,4 +464,56 @@ public static short createKeyParameters(byte[] ptrArr, short len) { } return KMKeyParameters.instance(arrPtr); } + + public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { + short index = 0; + short tagPtr; + short len = (short) customTags.length; + short tagType; + while (index < len) { + tagType = customTags[(short) (index + 1)]; + switch(tagType) { + case KMType.AUTH_TIMEOUT_MILLIS: + short authTimeOutTag = + KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT); + if (authTimeOutTag != KMType.INVALID_VALUE) { + tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset); + Util.setShort(scratchPad, offset, tagPtr); + offset += 2; + } + break; + default: + break; + } + index += 2; + } + return offset; + } + + public void deleteCustomTags() { + short arrPtr = getVals(); + short index = (short) (customTags.length - 1); + short obj; + while (index >= 0) { + obj = findTag(customTags[(short) (index - 1)], customTags[index]); + if (obj != KMType.INVALID_VALUE) { + KMArray.cast(arrPtr).deleteLastEntry(); + } + index -= 2; + } + } + + public static short createAuthTimeOutMillisTag(short authTimeOutTag, byte[] scratchPad, short offset) { + short authTime = KMIntegerTag.cast(authTimeOutTag).getValue(); + Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0); + Util.arrayCopyNonAtomic( + KMInteger.cast(authTime).getBuffer(), + KMInteger.cast(authTime).getStartOff(), + scratchPad, + (short) (offset + 8 - KMInteger.cast(authTime).length()), + KMInteger.cast(authTime).length()); + KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16)); + return KMIntegerTag.instance(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + KMInteger.uint_64(scratchPad, (short) (offset + 8))); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index ae097b47..7f365513 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -53,6 +53,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static final short KM_HAL_VERSION = (short) 0x5000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; + public static final byte TRUSTED_ENVIRONMENT = 1; // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys private static final byte[] defaultSubject = { @@ -98,9 +99,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe }; public static final short MAX_COSE_BUF_SIZE = (short) 1024; - // TODO INS_SET_BOOT_PARAMS_CMD is duplicated again in this class. - // TODO Is there a better way to handle the cleanup when device reboots ? - private static final byte INS_SET_BOOT_PARAMS_CMD = 5; // 0x05 // Top 32 commands are reserved for provisioning. private static final byte KEYMINT_CMD_APDU_START = 0x20; @@ -261,6 +259,7 @@ private KMOperationState reserveOperation(short algorithm, short opHandle){ short index = 0; while(index < MAX_OPERATIONS_COUNT) { if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) { + opTable[index].reset(); opTable[index].setAlgorithm(algorithm); opTable[index].setHandle(KMInteger.cast(opHandle).getBuffer(), KMInteger.cast(opHandle).getStartOff(), @@ -402,12 +401,6 @@ public void process(APDU apdu) { processInitStrongBoxCmd(apdu); sendError(apdu, KMError.OK); return; - case INS_SET_BOOT_PARAMS_CMD: - // Clear all auth tags. - repository.removeAllAuthTags(); - // clear early boot ended status. - repository.setEarlyBootEndedStatus(false); - break; case INS_GENERATE_KEY_CMD: processGenerateKey(apdu); break; @@ -707,6 +700,9 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { parseEncryptedKeyBlob(data[KEY_BLOB],data[APP_ID], data[APP_DATA], scratchPad); // Check Version and Patch Level checkVersionAndPatchLevel(scratchPad); + // Remove custom tags from key characteristics + short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); + KMKeyParameters.cast(teeParams).deleteCustomTags(); // make response. short resp = KMArray.instance((short) 2); KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -1919,22 +1915,30 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratch private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) { // If one time user Authentication is required if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { - validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); - short authTime = op.getAuthTime(); - short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - if (verTime == KMType.INVALID_VALUE) { + // Validate Verification Token. + validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); + // validate operation handle. + short ptr = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getChallenge(); + if (KMInteger.compare(ptr, op.getHandle()) != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + tmpVariables[0] = op.getAuthTime(); + tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + if (tmpVariables[2] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.VERIFICATION_FAILED); } - if (KMInteger.compare(authTime, verTime) < 0) { + if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } op.setAuthTimeoutValidated(true); } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required - short challenge = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); - if (KMInteger.compare(data[OP_HANDLE], challenge) != 0) { + tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); + if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authenticateUser(); } } @@ -1981,7 +1985,7 @@ private void authorizeKeyUsageForCount(byte[] scratchPad) { } } - private void authorizeDeviceUnlock(short hwToken) { + private void authorizeDeviceUnlock(byte[] scratchPad) { // If device is locked and key characteristics requires unlocked device then check whether // HW auth token has correct timestamp. short ptr = @@ -1989,11 +1993,11 @@ private void authorizeDeviceUnlock(short hwToken) { KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]); if (ptr != KMType.INVALID_VALUE && repository.getDeviceLock()) { - if (hwToken == KMType.INVALID_VALUE) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { KMException.throwIt(KMError.DEVICE_LOCKED); } - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - // Check if the current auth time stamp is greater then device locked time stamp + ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(); + // Check if the current auth time stamp is greater than device locked time stamp short ts = repository.getDeviceTimeStamp(); if (KMInteger.compare(ptr, ts) <= 0) { KMException.throwIt(KMError.DEVICE_LOCKED); @@ -2001,7 +2005,7 @@ private void authorizeDeviceUnlock(short hwToken) { // Now check if the device unlock requires password only authentication and whether // auth token is generated through password authentication or not. if (repository.getDeviceLockPasswordOnly()) { - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); ptr = KMEnum.cast(ptr).getVal(); if (((byte) ptr & KMType.PASSWORD) == 0) { KMException.throwIt(KMError.DEVICE_LOCKED); @@ -2014,37 +2018,16 @@ private void authorizeDeviceUnlock(short hwToken) { } } - private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { - // CBOR Encoding is always big endian and Java is big endian - short ptr = KMVerificationToken.cast(verToken).getMac(); - // If mac length is zero then token is empty. - 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); - } - } - - private void validateVerificationToken(short verToken, byte[] scratchPad) { - short ptr = KMVerificationToken.cast(verToken).getMac(); - short len; - // 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 + private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) { + // concatenation length will be 37 + length of verified parameters list - which + // is typically empty Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); // Add "Auth Verification" - 17 bytes. - Util.arrayCopy( - authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); - len = (short) authVerification.length; + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, + (short) authVerification.length); + short len = (short) authVerification.length; // concatenate challenge - 8 bytes - ptr = KMVerificationToken.cast(verToken).getChallenge(); + short ptr = KMVerificationToken.cast(verToken).getChallenge(); KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; @@ -2053,22 +2036,34 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; + // concatenate security level - 4 bytes + scratchPad[(short) (len + 3)] = TRUSTED_ENVIRONMENT; + len += 4; // hmac the data ptr = KMVerificationToken.cast(verToken).getMac(); short key = repository.getComputedHmacKey(); - boolean verified = - seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); + byte[] dummykey = new byte[32]; + key = KMByteBlob.instance(dummykey, (short) 0, (short) 32); + return seProvider.hmacVerify( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, + (short) 0, + len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + } - if (!verified) { + private void validateVerificationToken(short verToken, byte[] scratchPad) { + short ptr = KMVerificationToken.cast(verToken).getMac(); + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (!verifyVerificationTokenMacInBigEndian(verToken, scratchPad)) { + // Throw Exception if none of the combination works. KMException.throwIt(KMError.VERIFICATION_FAILED); } } @@ -2603,11 +2598,8 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizeDigest(op); authorizePadding(op); authorizeBlockModeAndMacLength(op); - if (!validateHwToken(data[HW_TOKEN], scratchPad)) { - data[HW_TOKEN] = KMType.INVALID_VALUE; - } - authorizeUserSecureIdAuthTimeout(op); - authorizeDeviceUnlock(data[HW_TOKEN]); + authorizeUserSecureIdAuthTimeout(op, scratchPad); + authorizeDeviceUnlock(scratchPad); authorizeKeyUsageForCount(scratchPad); //Validate early boot @@ -2837,70 +2829,104 @@ private void beginSignVerifyOperation(KMOperationState op) { } } - private void authorizeUserSecureIdAuthTimeout(KMOperationState op) { + private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, + short secureUserIdsObj) { + short secureUserId = KMHardwareAuthToken.cast(hwAuthToken).getUserId(); + if (!KMInteger.cast(secureUserId).isZero()) { + if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(secureUserId)) + return true; + } + + short authenticatorId = KMHardwareAuthToken.cast(hwAuthToken).getAuthenticatorId(); + if (!KMInteger.cast(authenticatorId).isZero()) { + if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(authenticatorId)) + return true; + } + return false; + } + + private boolean authTokenMatches(short userSecureIdsPtr, short authType, + byte[] scratchPad) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { + return false; + } + if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { + 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; + } + + private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { short authTime; + short authType; // Authorize User Secure Id and Auth timeout - if (KMTag.isPresent(data[HW_PARAMETERS], KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID)) { - authTime = + short userSecureIdPtr = + KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); + if (userSecureIdPtr != KMType.INVALID_VALUE) { + // Authentication required. + if (KMType.INVALID_VALUE != + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, data[HW_PARAMETERS])) { + // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // authenticator type must be provided. + if(KMType.INVALID_VALUE == + (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) { + // Authentication required, but no auth type found. + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + + short authTimeoutTagPtr = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); - if (authTime != KMType.INVALID_VALUE) { - // check if hw token is empty - mac should not be empty. - if (data[HW_TOKEN] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - authTime = KMIntegerTag.cast(authTime).getValue(); + if (authTimeoutTagPtr != KMType.INVALID_VALUE) { // authenticate user - authenticateUser(); + if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + + authTimeoutTagPtr = + KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[HW_PARAMETERS]); + if (authTimeoutTagPtr == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + authTime = KMIntegerTag.cast(authTimeoutTagPtr).getValue(); // 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()); + authTime = + addIntegers(authTime, + KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad); op.setAuthTime( KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff()); // auth time validation will happen in update or finish op.setAuthTimeoutValidated(false); } else { // auth per operation required + // store user secure id and authType in OperationState. + op.setUserSecureId(userSecureIdPtr); + op.setAuthType((byte) authType); + // set flags op.setOneTimeAuthReqd(false); op.setAuthPerOperationReqd(true); } } } - private void authenticateUser() { - short id = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); - if (KMInteger.cast(id).isZero()) { - id = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); - if (KMInteger.cast(id).isZero()) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - // check user secure id - if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, id, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - // check auth type - short authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); - short authenticatorType = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); - authenticatorType = KMEnum.cast(authenticatorType).getVal(); - if (((byte) authenticatorType & (byte) authType) == 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - - private boolean validateHwToken(short hwToken, byte[] scratchPad) { - // CBOR Encoding is always big endian - short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short len; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return false; - } + private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { + // The challenge, userId and authenticatorId, authenticatorType and timestamp + // are in network order (big-endian). + short len = 0; // add 0 Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); len = 1; // concatenate challenge - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; @@ -2923,9 +2949,56 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; - // hmac the data + + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + short key = repository.getComputedHmacKey(); + byte[] dummykey = new byte[32]; + key = KMByteBlob.instance(dummykey, (short) 0, (short) 32); + return seProvider.hmacVerify( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, + (short) 0, + len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + } + + private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) { + // The challenge, userId and authenticatorId values are in little endian order, + // but authenticatorType and timestamp are in network order (big-endian). + short len = 0; + // 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).toLittleEndian(scratchPad, len); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(ptr).toLittleEndian(scratchPad, len); + 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; + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); short key = repository.getComputedHmacKey(); + byte[] dummykey = new byte[32]; + key = KMByteBlob.instance(dummykey, (short) 0, (short) 32); return seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), @@ -2938,6 +3011,20 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMByteBlob.cast(ptr).length()); } + private boolean validateHwToken(short hwToken, byte[] scratchPad) { + // CBOR Encoding is always big endian + short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return false; + } + if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { + return verifyHwTokenMacInLittleEndian(hwToken, scratchPad); + } else { + return verifyHwTokenMacInBigEndian(hwToken, scratchPad); + } + } + private short importKeyCmd(APDU apdu){ short cmd = KMArray.instance((short) 6); // Arguments @@ -3327,14 +3414,18 @@ private void processInitStrongBoxCmd(APDU apdu) { setVendorPatchLevel(vendorPatchLevel); } - public void reboot(){ - // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - repository.clearComputedHmac(); - repository.clearHmacNonce(); - //Clear all the operation state. - releaseAllOperations(); - // Hmac is cleared, so generate a new Hmac nonce. - initHmacNonceAndSeed(); + public void reboot() { + // Clear the Computed SharedHmac and Hmac nonce from persistent memory. + repository.clearComputedHmac(); + repository.clearHmacNonce(); + //Clear all the operation state. + releaseAllOperations(); + // Hmac is cleared, so generate a new Hmac nonce. + initHmacNonceAndSeed(); + // Clear all auth tags. + repository.removeAllAuthTags(); + // clear early boot ended status. + repository.setEarlyBootEndedStatus(false); } protected void initSystemBootParams(short osVersion, @@ -4016,24 +4107,26 @@ public static void sendError(APDU apdu, short err) { sendOutgoing(apdu, resp); } - private short addIntegers(short num1, short num2) { - short buf = repository.alloc((short) 24); - byte[] scratchPad = repository.getHeap(); - Util.arrayFillNonAtomic(scratchPad, buf, (short) 24, (byte) 0); + private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0); Util.arrayCopyNonAtomic( - KMInteger.cast(num1).getBuffer(), - KMInteger.cast(num1).getStartOff(), + KMInteger.cast(authTime).getBuffer(), + KMInteger.cast(authTime).getStartOff(), scratchPad, - (short) (buf + 8 - KMInteger.cast(num1).length()), - KMInteger.cast(num1).length()); + (short) (8 - KMInteger.cast(timeStamp).length()), + KMInteger.cast(timeStamp).length()); + + // Copy timestamp to scratchpad Util.arrayCopyNonAtomic( - KMInteger.cast(num2).getBuffer(), - KMInteger.cast(num2).getStartOff(), + KMInteger.cast(timeStamp).getBuffer(), + KMInteger.cast(timeStamp).getStartOff(), scratchPad, - (short) (buf + 16 - KMInteger.cast(num2).length()), - KMInteger.cast(num2).length()); - add(scratchPad, buf, (short) (buf + 8), (short) (buf + 16)); - return KMInteger.uint_64(scratchPad, (short) (buf + 16)); + (short) (16 - KMInteger.cast(timeStamp).length()), + KMInteger.cast(timeStamp).length()); + + // add authTime in millis to timestamp. + KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); + return KMInteger.uint_64(scratchPad, (short) 16); } private void add(byte[] buf, short op1, short op2, short result) { diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index f9c8c68a..99715232 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMOperation; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -28,10 +29,6 @@ */ public class KMOperationState { - public static final byte MAX_DATA = 20; - public static final byte MAX_REFS = 1; - private static final byte DATA = 0; - private static final byte REFS = 1; // byte type private static final byte ALG = 0; private static final byte PURPOSE = 1; @@ -40,28 +37,29 @@ public class KMOperationState { private static final byte DIGEST = 4; private static final byte FLAGS = 5; private static final byte KEY_SIZE = 6; - private static final byte MAC_LENGTH = 7 ; + private static final byte MAC_LENGTH = 7; private static final byte MGF_DIGEST = 8; + private static final byte AUTH_TYPE = 9; + // sizes public static final byte OPERATION_HANDLE_SIZE = 8; - public static final byte DATA_SIZE = 9; + public static final byte DATA_SIZE = 10; public static final byte AUTH_TIME_SIZE = 8; - - // Handle - currently this is short - private static final byte OP_HANDLE = 10; - // Auth time 64 bits - private static final byte AUTH_TIME = 12; + // 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. + public static final byte USER_SECURE_IDS_SIZE = 42; // Flag masks private static final short AUTH_PER_OP_REQD = 1; private static final short SECURE_USER_ID_REQD = 2; private static final short AUTH_TIMEOUT_VALIDATED = 4; private static final short AES_GCM_UPDATE_ALLOWED = 8; - private static final byte ACTIVE = 1; - private static final byte INACTIVE = 0; + // Max user secure ids. + private static final byte MAX_SECURE_USER_IDS = 5; // Object References private byte[] opHandle; private byte[] authTime; + private byte[] userSecureIds; private short[] data; private Object[] operation; @@ -70,27 +68,29 @@ public KMOperationState() { opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET); authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET); data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); - operation = JCSystem.makeTransientObjectArray((short)1, JCSystem.CLEAR_ON_RESET); + operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET); reset(); } public void reset() { byte index = 0; - while(index < DATA_SIZE){ + while (index < DATA_SIZE) { data[index] = KMType.INVALID_VALUE; index++; } Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0); Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0); - - if(null != operation[0]) - ((KMOperation)operation[0]).abort(); - + + if (null != operation[0]) { + ((KMOperation) operation[0]).abort(); + } + operation[0] = null; } - public short compare(byte[] handle, short start, short len){ - return Util.arrayCompare(handle, start, opHandle, (short)0, (short)opHandle.length); + public short compare(byte[] handle, short start, short len) { + return Util.arrayCompare(handle, start, opHandle, (short) 0, (short) opHandle.length); } public void setKeySize(short keySize) { @@ -101,23 +101,12 @@ public short getKeySize() { return data[KEY_SIZE]; } - public short getHandle(){ - return KMInteger.uint_64(opHandle,(short)0); - } - - public void setHandle(short handle){ - setHandle(KMInteger.cast(handle).getBuffer(), - KMInteger.cast(handle).getStartOff(), - KMInteger.cast(handle).length()); - } - - public short getHandle(byte[] buf, short start) { - Util.arrayCopyNonAtomic(opHandle,(short)0, buf, start, (short)opHandle.length); - return (short) opHandle.length; + public short getHandle() { + return KMInteger.uint_64(opHandle, (short) 0); } public void setHandle(byte[] buf, short start, short len) { - Util.arrayCopyNonAtomic(buf,start, opHandle, (short)0, (short) opHandle.length); + Util.arrayCopyNonAtomic(buf, start, opHandle, (short) 0, (short) opHandle.length); } public short getPurpose() { @@ -149,15 +138,11 @@ public boolean isSecureUserIdReqd() { } public short getAuthTime() { - return KMInteger.uint_64(authTime,(short) 0); - } - - public void setAuthTime(short time){ - setAuthTime(KMInteger.cast(time).getBuffer(), KMInteger.cast(time).getStartOff()); + return KMInteger.uint_64(authTime, (short) 0); } public void setAuthTime(byte[] timeBuf, short start) { - Util.arrayCopyNonAtomic(timeBuf,start,authTime,(short)0, AUTH_TIME_SIZE); + Util.arrayCopyNonAtomic(timeBuf, start, authTime, (short) 0, AUTH_TIME_SIZE); } public void setOneTimeAuthReqd(boolean flag) { @@ -176,6 +161,55 @@ public void setAuthTimeoutValidated(boolean flag) { } } + public void setAuthType(byte authType) { + data[AUTH_TYPE] = authType; + } + + public short getAuthType() { + return data[AUTH_TYPE]; + } + + public short getUserSecureId() { + short offset = 0; + short length = Util.getShort(userSecureIds, offset); + offset += 2; + if (length == 0) { + return KMType.INVALID_VALUE; + } + short arrObj = KMArray.instance(length); + short index = 0; + short obj; + while (index < length) { + obj = KMInteger.instance(userSecureIds, (short) (offset + index * 8), (short) 8); + KMArray.cast(arrObj).add(index, obj); + index++; + } + return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj); + } + + public void setUserSecureId(short integerArrayPtr) { + short length = KMIntegerArrayTag.cast(integerArrayPtr).length(); + if (length > MAX_SECURE_USER_IDS) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + Util.arrayFillNonAtomic(userSecureIds, (short) 0, USER_SECURE_IDS_SIZE, (byte) 0); + short index = 0; + short obj; + short offset = 0; + offset = Util.setShort(userSecureIds, offset, length); + while (index < length) { + obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index); + Util.arrayCopyNonAtomic( + KMInteger.cast(obj).getBuffer(), + KMInteger.cast(obj).getStartOff(), + userSecureIds, + (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index), + KMInteger.cast(obj).length() + ); + index++; + } + } + public void setAuthPerOperationReqd(boolean flag) { if (flag) { data[FLAGS] = (short) (data[FLAGS] | AUTH_PER_OP_REQD); @@ -244,37 +278,37 @@ public short getMacLength() { return data[MAC_LENGTH]; } - public byte getBufferingMode(){ + public byte getBufferingMode() { short alg = getAlgorithm(); short purpose = getPurpose(); short digest = getDigest(); short padding = getPadding(); short blockMode = getBlockMode(); - if(alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN){ + if (alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { return KMType.BUF_RSA_NO_DIGEST; } - if(alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN){ + if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { return KMType.BUF_EC_NO_DIGEST; } - switch(alg) { - case KMType.AES: - if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { - return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN; - } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { - return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN; - } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { - return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; - } - break; - case KMType.DES: - if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { - return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN; - } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { - return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN; - } + switch (alg) { + case KMType.AES: + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { + return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; + } + break; + case KMType.DES: + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN; + } } return KMType.BUF_NONE; } diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index f12d6114..a9c3f0a2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -209,6 +209,8 @@ public abstract class KMType { public static final short USERID = 0x01F5; // Auth Timeout public static final short AUTH_TIMEOUT = 0x01F9; + // Auth Timeout in Milliseconds + public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF; // OS Version public static final short OS_VERSION = 0x02C1; // OS Patch Level diff --git a/HAL/CborConverter.cpp b/HAL/CborConverter.cpp index a97d12ad..485fd8ee 100644 --- a/HAL/CborConverter.cpp +++ b/HAL/CborConverter.cpp @@ -331,8 +331,8 @@ bool CborConverter::addSharedSecretParameters(Array& array, bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) { Array vToken; - vToken.add(token.challenge); - vToken.add(token.timestamp.milliSeconds); + vToken.add(static_cast(token.challenge)); + vToken.add(static_cast(token.timestamp.milliSeconds)); vToken.add((std::vector(token.mac))); array.add(std::move(vToken)); return true; @@ -341,11 +341,11 @@ bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) { Array hwAuthToken; - hwAuthToken.add(authToken.challenge); - hwAuthToken.add(authToken.userId); - hwAuthToken.add(authToken.authenticatorId); + hwAuthToken.add(static_cast(authToken.challenge)); + hwAuthToken.add(static_cast(authToken.userId)); + hwAuthToken.add(static_cast(authToken.authenticatorId)); hwAuthToken.add(static_cast(authToken.authenticatorType)); - hwAuthToken.add(authToken.timestamp.milliSeconds); + hwAuthToken.add(static_cast(authToken.timestamp.milliSeconds)); hwAuthToken.add((std::vector(authToken.mac))); array.add(std::move(hwAuthToken)); return true; @@ -354,27 +354,39 @@ bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& bool CborConverter::getHardwareAuthToken(const unique_ptr& item, const uint32_t pos, HardwareAuthToken& token) { uint64_t authType; + uint64_t challenge; + uint64_t userId; + uint64_t authenticatorId; + uint64_t timestampMillis; // challenge, userId, AuthenticatorId, AuthType, Timestamp, MAC - if (!getUint64(item, pos, token.challenge) || - !getUint64(item, pos + 1, token.userId) || - !getUint64(item, pos + 2, token.authenticatorId) || + if (!getUint64(item, pos, challenge) || + !getUint64(item, pos + 1, userId) || + !getUint64(item, pos + 2, authenticatorId) || !getUint64(item, pos + 3, authType) || - !getUint64(item, pos + 4, token.timestamp.milliSeconds) || + !getUint64(item, pos + 4, timestampMillis) || !getBinaryArray(item, pos + 5, token.mac)) { return false; } + token.challenge = static_cast(challenge); + token.userId = static_cast(userId); + token.authenticatorId = static_cast(authenticatorId); token.authenticatorType = static_cast(authType); + token.timestamp.milliSeconds = static_cast(timestampMillis); return true; } bool CborConverter::getTimeStampToken(const unique_ptr& item, const uint32_t pos, TimeStampToken& token) { // {challenge, timestamp, Mac} - if (!getUint64(item, pos, token.challenge) || - !getUint64(item, pos + 1, token.timestamp.milliSeconds) || - !getBinaryArray(item, pos + 4, token.mac)) { + uint64_t challenge; + uint64_t timestampMillis; + if (!getUint64(item, pos, challenge) || + !getUint64(item, pos + 1, timestampMillis) || + !getBinaryArray(item, pos + 2, token.mac)) { return false; } + token.challenge = static_cast(challenge); + token.timestamp.milliSeconds = static_cast(timestampMillis); return true; } From f96f1755a2b0fab1488c44ef3b0c300206ef7ed5 Mon Sep 17 00:00:00 2001 From: subrahmanyaman Date: Mon, 20 Dec 2021 05:12:47 +0000 Subject: [PATCH 10/15] Corrected the computedHmacKey bytes --- .../com/android/javacard/keymaster/KMKeymasterApplet.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 7f365513..04c947ba 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -2042,8 +2042,6 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scr // hmac the data ptr = KMVerificationToken.cast(verToken).getMac(); short key = repository.getComputedHmacKey(); - byte[] dummykey = new byte[32]; - key = KMByteBlob.instance(dummykey, (short) 0, (short) 32); return seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), @@ -2952,8 +2950,6 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { ptr = KMHardwareAuthToken.cast(hwToken).getMac(); short key = repository.getComputedHmacKey(); - byte[] dummykey = new byte[32]; - key = KMByteBlob.instance(dummykey, (short) 0, (short) 32); return seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), @@ -2997,8 +2993,6 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) ptr = KMHardwareAuthToken.cast(hwToken).getMac(); short key = repository.getComputedHmacKey(); - byte[] dummykey = new byte[32]; - key = KMByteBlob.instance(dummykey, (short) 0, (short) 32); return seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), From 289ba686fe430b827ac47a72225a90272a868567 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Thu, 23 Dec 2021 07:10:36 +0000 Subject: [PATCH 11/15] Trusted confirmation tag, boot loader only, early boot only and Computed HMAC key changes --- .../javacard/keymaster/KMAndroidSEApplet.java | 17 +- .../seprovider/KMAndroidSEProvider.java | 103 ++++++++-- .../seprovider/KMComputedHmacKey.java | 5 + .../javacard/seprovider/KMHmacKey.java | 8 +- .../javacard/seprovider/KMOperationImpl.java | 19 ++ .../javacard/seprovider/KMPoolManager.java | 42 +++- .../javacard/seprovider/KMSEProvider.java | 42 +++- .../android/javacard/keymaster/KMError.java | 1 + .../javacard/keymaster/KMKeyParameters.java | 10 +- .../javacard/keymaster/KMKeymasterApplet.java | 193 +++++++++++------- .../javacard/keymaster/KMOperationState.java | 40 ++-- .../javacard/keymaster/KMRepository.java | 27 +-- 12 files changed, 365 insertions(+), 142 deletions(-) create mode 100644 Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 8da8b8ed..4505f8d7 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -47,6 +47,8 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis INS_KEYMINT_PROVIDER_APDU_START + 6; private static final byte INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; + private static final byte INS_SET_BOOT_ENDED_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 8; private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; public static final byte BOOT_KEY_MAX_SIZE = 32; @@ -100,6 +102,13 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); break; + + case INS_SET_BOOT_ENDED_CMD: + //set the flag to mark boot ended + repository.setBootEndedStatus(true); + sendError(apdu, KMError.OK); + break; + default: super.process(apdu); break; @@ -308,7 +317,8 @@ private void processGetProvisionStatusCmd(APDU apdu) { private void processSetBootParamsCmd(APDU apdu) { short argsProto = KMArray.instance((short) 5); - + + byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments // Argument 0 Boot Patch level KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); @@ -353,6 +363,11 @@ private void processSetBootParamsCmd(APDU apdu) { enumVal = KMEnum.cast(bootParam).getVal(); ((KMAndroidSEProvider) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + // Clear the Computed SharedHmac and Hmac nonce from persistent memory. + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE, (byte) 0); + seProvider.createComputedHmacKey(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE); + super.reboot(); sendError(apdu, KMError.OK); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index aaf93834..30860f50 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -65,6 +65,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final short SHARED_SECRET_KEY_SIZE = 32; public static final byte POWER_RESET_FALSE = (byte) 0xAA; public static final byte POWER_RESET_TRUE = (byte) 0x00; + private static final short COMPUTED_HMAC_KEY_SIZE = 32; private static KeyAgreement keyAgreement; @@ -117,6 +118,7 @@ public class KMAndroidSEProvider implements KMSEProvider { private KMECDeviceUniqueKey testKey; private KMECDeviceUniqueKey deviceUniqueKey; private KMHmacKey preSharedKey; + private KMHmacKey computedHmacKey; private byte[] additionalCertChain; private byte[] bcc; private boolean isProvisionLocked; @@ -167,6 +169,8 @@ public KMAndroidSEProvider() { createAttestationKey(tmpArray, (short) 0, (short) 32); // Pre-shared secret key length is 32 bytes. createPresharedKey(tmpArray, (short) 0, (short) SHARED_SECRET_KEY_SIZE); + // Initialize the Computed Hmac Key object. + createComputedHmacKey(tmpArray, (short)0, (short) 32); } androidSEProvider = this; resetFlag = JCSystem.makeTransientByteArray((short) 1, @@ -593,13 +597,6 @@ public short hmacSign(HMACKey key, byte[] data, short dataStart, return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); } - public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, - short dataLength, byte[] mac, short macStart, short macLength) { - hmacSignature.init(key, Signature.MODE_VERIFY); - return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, - macLength); - } - @Override public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { @@ -623,12 +620,12 @@ public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, } @Override - public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, - byte[] data, short dataStart, short dataLength, byte[] mac, - short macStart, short macLength) { - HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacVerify(key, data, dataStart, dataLength, mac, macStart, - macLength); + public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, + macLength); } @Override @@ -739,7 +736,7 @@ public KMOperation createSymmetricCipher(short alg, short purpose, short macLeng } short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0); KMOperation operation = - poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength); + poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength, false); ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength); return operation; } @@ -751,11 +748,23 @@ public KMOperation createHmacSignerVerifier(short purpose, short digest, } KMOperation operation = poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, - KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); HMACKey key = createHMACKey(secret, secretStart, secretLength); ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); return operation; } + + private KMOperation createHmacSignerVerifier(short purpose, short digest, HMACKey key, boolean isTrustedConf) { + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + KMOperation operation = + poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, isTrustedConf); + + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } @Override public KMOperation initSymmetricOperation(byte purpose, byte alg, @@ -781,12 +790,18 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, return opr; } + @Override + public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { + KMHmacKey key = (KMHmacKey) computedHmacKey; + return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey(), true); + } + public KMOperation createRsaSigner(short digest, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); KMOperation operation = poolMgr.getOperationImpl(KMType.SIGN, alg, KMType.RSA, padding, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); @@ -799,7 +814,7 @@ public KMOperation createRsaDecipher(short padding, short mgfDigest, byte[] secr short modLength) { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest); KMOperation operation = poolMgr.getOperationImpl(KMType.DECRYPT, cipherAlg, KMType.RSA, padding, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); @@ -814,7 +829,7 @@ public KMOperation createEcSigner(short digest, byte[] secret, key.setS(secret, secretStart, secretLength); KMOperation operation = poolMgr .getOperationImpl(KMType.SIGN, alg, KMType.EC, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); return operation; } @@ -825,7 +840,7 @@ public KMOperation createKeyAgreement(byte[] secret, short secretStart, key.setS(secret, secretStart, secretLength); KMOperation operation = poolMgr .getOperationImpl(KMType.AGREE_KEY, KeyAgreement.ALG_EC_SVDP_DH_PLAIN, - KMType.EC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); + KMType.EC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); return operation; } @@ -1001,6 +1016,20 @@ public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short len return (KMPreSharedKey) preSharedKey; } + @Override + public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (computedHmacKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), + false); + computedHmacKey = new KMHmacKey(key); + } + computedHmacKey.setKey(keyData, offset, length); + return (KMComputedHmacKey) computedHmacKey; + } + @Override public KMMasterKey getMasterKey() { return (KMMasterKey) masterKey; @@ -1486,4 +1515,40 @@ public void setProvisionLocked(boolean locked) { public boolean isProvisionLocked() { return isProvisionLocked; } + + @Override + public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest.OneShot mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } finally { + if (mDigest != null) { + mDigest.close(); + mDigest = null; + } + } + return len; + } + + @Override + public KMComputedHmacKey getComputedHmacKey() { + return computedHmacKey; + } + + private byte mapPurpose(short purpose) { + switch (purpose) { + case KMType.ENCRYPT: + return Cipher.MODE_ENCRYPT; + case KMType.DECRYPT: + return Cipher.MODE_DECRYPT; + case KMType.SIGN: + return Signature.MODE_SIGN; + case KMType.VERIFY: + return Signature.MODE_VERIFY; + } + return -1; + } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java new file mode 100644 index 00000000..3b0aa405 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java @@ -0,0 +1,5 @@ +package com.android.javacard.seprovider; + + +public interface KMComputedHmacKey { +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index 2eaeeeb9..a863bf88 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -19,7 +19,7 @@ import javacard.security.HMACKey; -public class KMHmacKey implements KMPreSharedKey { +public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { private HMACKey hmacKey; @@ -34,7 +34,11 @@ public void setKey(byte[] keyData, short kOff, short length) { public byte getKey(byte[] keyData, short kOff) { return hmacKey.getKey(keyData, kOff); } - + + public HMACKey getKey() { + return hmacKey; + } + public short getKeySizeBits() { return hmacKey.getSize(); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java index 2c447fd4..baa8a0f9 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -35,6 +35,7 @@ public class KMOperationImpl implements KMOperation { private static final short PURPOSE_OFFSET = 0x02; private static final short BLOCK_MODE_OFFSET = 0x03; private static final short MAC_LENGTH_OFFSET = 0x04; + private final byte[] EMPTY = {}; //This will hold the length of the buffer stored inside the //Java Card after the GCM update operation. private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; @@ -340,6 +341,24 @@ public boolean verify(byte[] inputDataBuf, short inputDataStart, @Override public void abort() { + // Few simulators does not reset the Hmac signer instance on init so as + // a workaround to reset the hmac signer instance in case of abort/failure of the operation + // the corresponding sign / verify function is called. + if (operationInst[0] != null) { + if ((parameters[PURPOSE_OFFSET] == KMType.SIGN || parameters[PURPOSE_OFFSET] == KMType.VERIFY) && + (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { + Signature signer = (Signature) operationInst[0]; + try { + if (parameters[PURPOSE_OFFSET] == KMType.SIGN) { + signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); + } else { + signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0); + } + } catch(Exception e) { + // Ignore. + } + } + } reset(); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java index 1606ce4e..e3baf610 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -26,6 +26,7 @@ public class KMPoolManager { public static final short MAX_OPERATION_INSTANCES = 4; + private static final short HMAC_MAX_OPERATION_INSTANCES = 8; // Cipher pool private Object[] cipherPool; // Signature pool @@ -34,6 +35,8 @@ public class KMPoolManager { private Object[] keyAgreementPool; // KMOperationImpl pool private Object[] operationPool; + // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. + private Object[] hmacSignOperationPool; final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, @@ -70,11 +73,14 @@ public static KMPoolManager getInstance() { private KMPoolManager() { cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; - signerPool = new Object[(short) (SIG_ALGS.length * 4)]; + // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. + signerPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * 4)]; operationPool = new Object[4]; + hmacSignOperationPool = new Object[4]; /* Initialize pools */ initializeOperationPool(); + initializeHmacSignOperationPool(); initializeSignerPool(); initializeCipherPool(); initializeKeyAgreementPool(); @@ -88,6 +94,14 @@ private void initializeOperationPool() { } } + private void initializeHmacSignOperationPool() { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + hmacSignOperationPool[index] = new KMOperationImpl(); + index++; + } + } + // Create a signature instance of each algorithm once. private void initializeSignerPool() { short index = 0; @@ -181,11 +195,17 @@ private Cipher getCipherInstance(byte alg) { * * @return instance of the available resource or null if no resource is available. */ - public KMOperation getResourceFromOperationPool() { + public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) { short index = 0; KMOperationImpl impl; - while (index < operationPool.length) { - impl = (KMOperationImpl) operationPool[index]; + Object[] oprPool; + if(isTrustedConfOpr) { + oprPool = hmacSignOperationPool; + } else { + oprPool = operationPool; + } + while (index < oprPool.length) { + impl = (KMOperationImpl) oprPool[index]; // Mode is always set. so compare using mode value. if (impl.getPurpose() == KMType.INVALID_VALUE) { return impl; @@ -217,7 +237,8 @@ private byte getAlgorithm(short purpose, Object object) { private boolean isResourceBusy(Object obj) { short index = 0; while (index < MAX_OPERATION_INSTANCES) { - if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj)) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj) + || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj)) { return true; } index++; @@ -256,19 +277,23 @@ private void reserveOperation(KMOperation operation, short purpose, short strong public KMOperation getOperationImpl(short purpose, short alg, short strongboxAlgType, short padding, - short blockMode, short macLength) { + short blockMode, short macLength, boolean isTrustedConfOpr) { KMOperation operation; // Throw exception if no resource from operation pool is available. - if (null == (operation = getResourceFromOperationPool())) { + if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); } // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose. Object[] pool = getCryptoPoolInstance(purpose); short index = 0; short usageCount = 0; + short maxOperations = MAX_OPERATION_INSTANCES; + if (Signature.ALG_HMAC_SHA_256 == alg) { + maxOperations = HMAC_MAX_OPERATION_INSTANCES; + } while (index < pool.length) { - if (usageCount >= MAX_OPERATION_INSTANCES) { + if (usageCount >= maxOperations) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); } if (pool[index] == null) { @@ -298,6 +323,7 @@ public void powerReset() { short index = 0; while (index < operationPool.length) { ((KMOperationImpl) operationPool[index]).abort(); + ((KMOperationImpl) hmacSignOperationPool[index]).abort(); index++; } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 0c3051d1..18932ee5 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -63,6 +63,14 @@ void createAsymmetricKey( short pubModMaxLength, short[] lengths); + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then * it should throw a CryptoException. @@ -301,9 +309,7 @@ short hmacKDF( * @return true if the signature matches. */ boolean hmacVerify( - byte[] keyBuf, - short keyStart, - short keyLength, + KMComputedHmacKey hmacKey, byte[] data, short dataStart, short dataLength, @@ -576,6 +582,16 @@ KMOperation initAsymmetricOperation( */ KMMasterKey createMasterKey(short keySizeBits); + /** + * This function creates an HMACKey and initializes the key with the provided input key data. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMComputedHmacKey. + */ + KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length); + /** * Returns the master key. * @@ -697,6 +713,26 @@ KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, */ byte[] getBootCertificateChain(); + /** + * Returns the computed Hmac key. + * + * @return Instance of the computed hmac key. + */ + KMComputedHmacKey getComputedHmacKey(); + + /** + * This is a one-shot operation the does digest of the input mesage. + * + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. + */ + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, + short outOffset); + public boolean isProvisionLocked(); diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 992d804a..52398824 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -71,6 +71,7 @@ public class KMError { public static final short CANNOT_ATTEST_IDS = 66; public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + public static final short NO_USER_CONFIRMATION = 71; public static final short DEVICE_LOCKED = 72; public static final short EARLY_BOOT_ENDED = 73; public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 7fd115ca..d4cbc323 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -134,9 +134,7 @@ public short findTag(short tagType, short tagKey) { public static boolean hasUnsupportedTags(short keyParamsPtr) { final short[] tagArr = { // Unsupported tags. - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS }; byte index = 0; @@ -188,7 +186,9 @@ public static short makeSbEnforced(short keyParamsPtr, byte origin, KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, }; byte index = 0; short tagInd; @@ -263,6 +263,8 @@ public static short makeSbEnforced(short keyParamsPtr, byte[] scratchPad) { KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, }; byte index = 0; short tagInd; @@ -328,7 +330,8 @@ public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, KMType.UINT_TAG, KMType.USERID, KMType.DATE_TAG, KMType.CREATION_DATETIME, - KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT + KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT, + KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY }; byte index = 0; short tagInd; @@ -438,7 +441,6 @@ public static boolean isValidTag(short tagType, short tagKey) { KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, KMType.BYTES_TAG, KMType.UNIQUE_ID, KMType.UINT_TAG, KMType.MAC_LENGTH, - KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY }; short index = 0; if (tagKey == KMType.INVALID_TAG) { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 04c947ba..15ebae13 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -17,8 +17,10 @@ package com.android.javacard.keymaster; import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMComputedHmacKey; import com.android.javacard.seprovider.KMDeviceUniqueKey; import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMHmacKey; import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.APDU; import javacard.framework.Applet; @@ -28,6 +30,8 @@ import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; +import javacard.security.HMACKey; +import javacard.security.KeyBuilder; import javacardx.apdu.ExtendedLength; /** @@ -98,6 +102,13 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x6E }; + // "confirmation token" + public static final byte[] confirmationToken = { + 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, + 0x6B, + 0x65, 0x6E + }; + public static final short MAX_COSE_BUF_SIZE = (short) 1024; // Top 32 commands are reserved for provisioning. private static final byte KEYMINT_CMD_APDU_START = 0x20; @@ -181,6 +192,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte PLAIN_SECRET = 33; public static final byte TEE_PARAMETERS = 34; public static final byte SB_PARAMETERS = 35; + public static final byte CONFIRMATION_TOKEN = 36; // Constant // AddRngEntropy @@ -494,7 +506,6 @@ public void process(APDU apdu) { resetWrappingKey(); sendError(apdu, mapCryptoErrorToKMError(e.getReason())); } catch (Exception e) { - sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); freeOperations(); resetWrappingKey(); sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); @@ -869,9 +880,9 @@ private void processComputeSharedHmacCmd(APDU apdu) { bufferIndex, scratchPad, (short) 0); - // persist the computed hmac key. - repository.initComputedHmac(scratchPad, (short) 0, keyLen); + // persist the computed hmac key. + seProvider.createComputedHmacKey(scratchPad, (short) 0, keyLen); // Generate sharingKey verification signature and store that in scratch pad. //tmpVariables[5] short signLen = @@ -961,6 +972,7 @@ private void processUpgradeKeyCmd(APDU apdu) { if (isKeyUpgradeRequired) { // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + makeKeyCharacteristics(scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); } else { @@ -1673,7 +1685,7 @@ private void processAbortOperationCmd(APDU apdu) { } private short finishOperationCmd(APDU apdu){ - short cmd = KMArray.instance((short) 5); + short cmd = KMArray.instance((short) 6); KMArray.cast(cmd).add((short) 0, KMInteger.exp());//op handle KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());// input data KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // signature @@ -1681,6 +1693,7 @@ private short finishOperationCmd(APDU apdu){ KMArray.cast(cmd).add((short) 3, authToken); // auth token short verToken = KMVerificationToken.exp(); KMArray.cast(cmd).add((short) 4, verToken); // time stamp token + KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); //confirmation token return receiveIncoming(apdu, cmd); } @@ -1692,6 +1705,7 @@ private void processFinishOperationCmd(APDU apdu) { data[SIGNATURE] = KMArray.cast(cmd).get((short) 2); data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3); data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 4); + data[CONFIRMATION_TOKEN] = KMArray.cast(cmd).get((short) 5); // Check Operation Handle KMOperationState op = findOperation(data[OP_HANDLE]); if (op == null) { @@ -1701,6 +1715,7 @@ private void processFinishOperationCmd(APDU apdu) { authorizeUpdateFinishOperation(op, scratchPad); switch (op.getPurpose()) { case KMType.SIGN: + finishTrustedConfirmationOperation(op); case KMType.VERIFY: finishSigningVerifyingOperation(op, scratchPad); break; @@ -2041,11 +2056,9 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scr len += 4; // hmac the data ptr = KMVerificationToken.cast(verToken).getMac(); - short key = repository.getComputedHmacKey(); + return seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), + seProvider.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -2107,6 +2120,9 @@ private void processUpdateOperationCmd(APDU apdu) { KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length()); + // update trusted confirmation operation + updateTrustedConfirmationOperation(op); + data[OUTPUT_DATA] = KMType.INVALID_VALUE; } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { // Update for encrypt/decrypt using RSA will not be supported because to do this op state @@ -2255,7 +2271,6 @@ private void processBeginOperationCmd(APDU apdu) { } // Parse the encrypted blob and decrypt it. parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); - KMTag.assertAbsence(data[SB_PARAMETERS],KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); KMTag.assertPresence(data[SB_PARAMETERS],KMType.ENUM_TAG,KMType.ALGORITHM,KMError.UNSUPPORTED_ALGORITHM); short algorithm = KMEnumTag.getValue(KMType.ALGORITHM,data[SB_PARAMETERS]); // If Blob usage tag is present in key characteristics then it should be standalone. @@ -2286,6 +2301,7 @@ private void processBeginOperationCmd(APDU apdu) { authorizeAndBeginOperation(op, scratchPad); switch (op.getPurpose()) { case KMType.SIGN: + beginTrustedConfirmationOperation(op); case KMType.VERIFY: beginSignVerifyOperation(op); break; @@ -2605,7 +2621,13 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.INVALID_KEY_BLOB); } - + + //Validate bootloader only + if (repository.getBootEndedStatus()) { + KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMError.INVALID_KEY_BLOB); + } + // 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; @@ -2741,6 +2763,22 @@ private void beginCipherOperation(KMOperationState op) { } } + private void beginTrustedConfirmationOperation(KMOperationState op) { + // Check for trusted confirmation - if required then set the signer in op state. + if (KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + data[HW_PARAMETERS]) != KMType.INVALID_VALUE) { + + op.setTrustedConfirmationSigner( + seProvider.initTrustedConfirmationSymmetricOperation(seProvider.getComputedHmacKey())); + + op.getTrustedConfirmationSigner().update( + confirmationToken, + (short) 0, + (short) confirmationToken.length); + } + + } + private void beginSignVerifyOperation(KMOperationState op) { switch (op.getAlgorithm()) { case KMType.RSA: @@ -2949,11 +2987,9 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { len += 8; ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short key = repository.getComputedHmacKey(); + return seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), + seProvider.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -2992,11 +3028,9 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) len += 8; ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short key = repository.getComputedHmacKey(); + return seProvider.hmacVerify( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), + seProvider.getComputedHmacKey(), scratchPad, (short) 0, len, @@ -3054,8 +3088,7 @@ private void validateImportKey(short params, short keyFmt){ // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is // provided to IKeyMintDevice::importKey KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); - // Importing Bootloader only keys not supported. - KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + // Algorithm must be present KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); @@ -3409,17 +3442,17 @@ private void processInitStrongBoxCmd(APDU apdu) { } public void reboot() { - // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - repository.clearComputedHmac(); repository.clearHmacNonce(); + //flag to maintain the boot state + repository.setBootEndedStatus(false); + //flag to maintain early boot ended state + repository.setEarlyBootEndedStatus(false); //Clear all the operation state. releaseAllOperations(); // Hmac is cleared, so generate a new Hmac nonce. initHmacNonceAndSeed(); // Clear all auth tags. repository.removeAllAuthTags(); - // clear early boot ended status. - repository.setEarlyBootEndedStatus(false); } protected void initSystemBootParams(short osVersion, @@ -3477,8 +3510,7 @@ private void processGenerateKey(APDU apdu) { data[CERTIFICATE] = KMArray.instance((short)0); //by default the cert is empty. // ROLLBACK_RESISTANCE not supported. KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - // BOOTLOADER_ONLY keys not supported. - KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + // As per specification Early boot keys may be created after early boot ended. // Algorithm must be present KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); @@ -4009,31 +4041,36 @@ private static void encryptSecret(byte[] scratchPad) { } private static void makeAuthData(byte[] scratchPad) { - short len = - addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); - len += - addPtrToAAD( - KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, len); - short authData; + short arrayLen = 2; if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - authData = KMArray.instance((short) (len + 1)); - } else { - authData = KMArray.instance(len); + arrayLen = 3; } - // convert scratch pad to KMArray + short params = KMArray.instance((short) arrayLen); + KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + // KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (3 == arrayLen) { + KMArray.cast(params).add((short) 2, data[PUB_KEY]); + } + + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); short index = 0; - short objPtr; - while (index < len) { - objPtr = Util.getShort(scratchPad, (short) (index * 2)); - KMArray.cast(authData).add(index, objPtr); + short len = 0; + short paramsLen = KMArray.cast(params).length(); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + while (index < paramsLen) { + short tag = KMArray.cast(params).get(index); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + (short) (authIndex + len + 32), (short) 32); + len = seProvider.messageDigest256(repository.getHeap(), + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + if (len != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } index++; } - //TODO change the code below - implicitly adds the pub key. - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - KMArray.cast(authData).add(index, data[PUB_KEY]); - } - data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); - len = encoder.encode(authData, repository.getHeap(), data[AUTH_DATA]); + data[AUTH_DATA] = authIndex; data[AUTH_DATA_LENGTH] = len; } @@ -4052,45 +4089,24 @@ private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) } private static short deriveKey(byte[] scratchPad) { - short hiddenParams = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); - short derivationData = repository.alloc(DERIVE_KEY_INPUT_SIZE); - // generate derivation material from hidden parameters - short len = encoder.encode(hiddenParams, repository.getHeap(), derivationData); - if (DERIVE_KEY_INPUT_SIZE > len) { - short start = (short)(derivationData+len); - len = (short)(DERIVE_KEY_INPUT_SIZE - len); - // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE - // if the data[AUTH_DATA] length is less then DERIVE_KEY_INPUT_SIZE - len - // then add complete key characteristics. - if(data[AUTH_DATA_LENGTH] < len){ - len = data[AUTH_DATA_LENGTH]; - } - Util.arrayCopyNonAtomic(repository.getHeap(), data[AUTH_DATA], - repository.getHeap(), start, len); - } // KeyDerivation: - // 1. Do HMAC Sign, with below input parameters. - // Key - 128 bit master key - // Input data - HIDDEN_PARAMETERS + KeyCharacateristics - // - Truncate beyond 256 bytes. + // 1. Do HMAC Sign, Auth data. // 2. HMAC Sign generates an output of 32 bytes length. - // Consume only first 16 bytes as derived key. + // Consume only first 16 bytes as derived key. // Hmac sign. - short signLen = seProvider.hmacKDF( + short len = seProvider.hmacKDF( seProvider.getMasterKey(), repository.getHeap(), - derivationData, - DERIVE_KEY_INPUT_SIZE, + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], scratchPad, (short) 0); - if (signLen < 16) { + if (len < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - signLen = 16; - // store the derived secret in data dictionary - data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short)0, signLen); - //TODO do we need to return this len? - return signLen; + len = 16; + data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short)0, len); + return len; } public static void sendError(APDU apdu, short err) { @@ -4341,4 +4357,29 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { KMArray.cast(bcc).add((short) 1, coseSign1); return bcc; } + + private void updateTrustedConfirmationOperation(KMOperationState op) { + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner().update(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length()); + } + } + + private void finishTrustedConfirmationOperation(KMOperationState op) { + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + + boolean verified = op.getTrustedConfirmationSigner().verify(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()); + if (!verified) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + } + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 99715232..83370b8e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -48,6 +48,8 @@ public class KMOperationState { // First two bytes are reserved to store number of secure ids. So total 42 bytes. public static final byte USER_SECURE_IDS_SIZE = 42; + private static final byte OPERATION = 0; + private static final byte HMAC_SIGNER_OPERATION = 1; // Flag masks private static final short AUTH_PER_OP_REQD = 1; private static final short SECURE_USER_ID_REQD = 2; @@ -61,14 +63,14 @@ public class KMOperationState { private byte[] authTime; private byte[] userSecureIds; private short[] data; - private Object[] operation; + private Object[] operations; public KMOperationState() { opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET); authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET); data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); - operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + operations = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET); reset(); } @@ -81,16 +83,18 @@ public void reset() { } Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0); Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0); - - if (null != operation[0]) { - ((KMOperation) operation[0]).abort(); - } - - operation[0] = null; + + if(null != operations[OPERATION]) + ((KMOperation)operations[OPERATION]).abort(); + operations[OPERATION] = null; + + if(null != operations[HMAC_SIGNER_OPERATION]) + ((KMOperation)operations[HMAC_SIGNER_OPERATION]).abort(); + operations[HMAC_SIGNER_OPERATION] = null; } - public short compare(byte[] handle, short start, short len) { - return Util.arrayCompare(handle, start, opHandle, (short) 0, (short) opHandle.length); + public short compare(byte[] handle, short start, short len){ + return Util.arrayCompare(handle, start, opHandle, (short)0, (short)opHandle.length); } public void setKeySize(short keySize) { @@ -118,11 +122,11 @@ public void setPurpose(short purpose) { } public void setOperation(KMOperation op) { - operation[0] = op; + operations[OPERATION] = op; } public KMOperation getOperation() { - return (KMOperation) operation[0]; + return (KMOperation) operations[OPERATION]; } public boolean isAuthPerOperationReqd() { @@ -312,4 +316,16 @@ public byte getBufferingMode() { } return KMType.BUF_NONE; } + + public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) { + operations[HMAC_SIGNER_OPERATION] = hmacSignerOp; + } + + public KMOperation getTrustedConfirmationSigner() { + return (KMOperation)operations[HMAC_SIGNER_OPERATION]; + } + + public boolean isTrustedConfirmationRequired() { + return operations[HMAC_SIGNER_OPERATION] != null; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index b4158b9c..03791210 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -53,9 +53,9 @@ public class KMRepository implements KMUpgradable { public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 public static final byte AUTH_TAG_1 = 8; + public static final byte BOOT_ENDED_FLAG = 15; public static final byte EARLY_BOOT_ENDED_FLAG = 16; - - + // Data Item sizes public static final short HMAC_SEED_NONCE_SIZE = 32; public static final short COMPUTED_HMAC_KEY_SIZE = 32; @@ -96,13 +96,6 @@ public KMRepository(boolean isUpgrading) { repository = this; } - public void initComputedHmac(byte[] key, short start, short len) { - if (len != COMPUTED_HMAC_KEY_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(COMPUTED_HMAC_KEY, key, start, len); - } - public void initHmacNonce(byte[] nonce, short offset, short len) { if (len != HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); @@ -114,10 +107,6 @@ public void clearHmacNonce() { clearDataEntry(HMAC_NONCE); } - public void clearComputedHmac() { - clearDataEntry(COMPUTED_HMAC_KEY); - } - public void onUninstall() { // Javacard Runtime environment cleans up the data. @@ -253,10 +242,6 @@ public short getHmacNonce() { return readData(HMAC_NONCE); } - public short getComputedHmacKey() { - return readData(COMPUTED_HMAC_KEY); - } - public short readData(short id) { short len = dataLength(id); if (len != 0) { @@ -319,6 +304,10 @@ public boolean getEarlyBootEndedStatus() { return readBoolean(EARLY_BOOT_ENDED_FLAG); } + public boolean getBootEndedStatus() { + return readBoolean(BOOT_ENDED_FLAG); + } + public short getDeviceTimeStamp() { short blob = readData(DEVICE_LOCKED_TIME); if (blob != KMType.INVALID_VALUE) { @@ -371,6 +360,10 @@ public void setDeviceLockTimestamp(byte[] buf, short start, short len) { public void setEarlyBootEndedStatus(boolean flag) { writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); } + + public void setBootEndedStatus(boolean flag) { + writeBoolean(BOOT_ENDED_FLAG, flag); + } public void clearDeviceLockTimeStamp() { clearDataEntry(DEVICE_LOCKED_TIME); From c523e8996c12e809a805dbb47b9c21804e169965 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Thu, 23 Dec 2021 08:00:17 +0000 Subject: [PATCH 12/15] Confirmation token HAL changes --- HAL/JavacardKeyMintOperation.cpp | 6 +++++- HAL/JavacardKeyMintOperation.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp index a1882266..59870668 100644 --- a/HAL/JavacardKeyMintOperation.cpp +++ b/HAL/JavacardKeyMintOperation.cpp @@ -78,6 +78,7 @@ ScopedAStatus JavacardKeyMintOperation::finish( const optional>& /*confirmationToken*/, vector* output) { HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + const vector confToken = confirmationToken.value_or(vector()); const vector inData = input.value_or(vector()); DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; const vector sign = signature.value_or(vector()); @@ -92,7 +93,7 @@ ScopedAStatus JavacardKeyMintOperation::finish( } } vector remaining = popNextChunk(view, view.length); - return km_utils::kmError2ScopedAStatus(sendFinish(remaining, sign, aToken, tToken, *output)); + return km_utils::kmError2ScopedAStatus(sendFinish(remaining, sign, aToken, tToken, confToken, *output)); } ScopedAStatus JavacardKeyMintOperation::abort() { @@ -262,6 +263,7 @@ keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector& da const vector& sign, const HardwareAuthToken& authToken, const TimeStampToken& timestampToken, + const vector& confToken, vector& output) { cppbor::Array request; request.add(Uint(opHandle_)); @@ -269,6 +271,8 @@ keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector& da request.add(Bstr(sign)); cbor_.addHardwareAuthToken(request, authToken); cbor_.addTimeStampToken(request, timestampToken); + request.add(Bstr(confToken)); + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request); if (err != KM_ERROR_OK) { return err; diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h index 642d65ee..2ac6930b 100644 --- a/HAL/JavacardKeyMintOperation.h +++ b/HAL/JavacardKeyMintOperation.h @@ -98,7 +98,7 @@ class JavacardKeyMintOperation : public BnKeyMintOperation { keymaster_error_t sendFinish(const vector& data, const vector& signature, const HardwareAuthToken& authToken, - const TimeStampToken& timestampToken, vector& output); + const TimeStampToken& timestampToken, const vector& confToken, vector& output); keymaster_error_t sendUpdate(const vector& data, const HardwareAuthToken& authToken, const TimeStampToken& timestampToken, vector& output); From a907cb02665cab3b0f099d32a317c7f883537faf Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Thu, 23 Dec 2021 13:27:08 +0000 Subject: [PATCH 13/15] Trusted confirmation changes for Jcard --- .../seprovider/KMComputedHmacKey.java | 5 ++ .../javacard/seprovider/KMHmacKey.java | 4 ++ .../javacard/seprovider/KMJCardSimulator.java | 68 ++++++++++++++++--- .../javacard/seprovider/KMSEProvider.java | 46 +++++++++++-- .../javacard/keymaster/KMKeymasterApplet.java | 1 - 5 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java new file mode 100644 index 00000000..3b0aa405 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java @@ -0,0 +1,5 @@ +package com.android.javacard.seprovider; + + +public interface KMComputedHmacKey { +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index 8473f1bf..735789b6 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -36,4 +36,8 @@ public byte getKey(byte[] keyData, short kOff) { public short getKeySizeBits() { return hmacKey.getSize(); } + + public HMACKey getKey() { + return hmacKey; + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index 1d0c426e..ed41afe1 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -39,6 +39,7 @@ import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; import javacard.security.KeyAgreement; import javacard.security.RandomData; @@ -75,6 +76,7 @@ public class KMJCardSimulator implements KMSEProvider { public static final byte POWER_RESET_FALSE = (byte)0xAA; public static final byte POWER_RESET_TRUE = (byte)0x00; public static final byte AES_BLOCK_SIZE = 16; + private static final short COMPUTED_HMAC_KEY_SIZE = 32; public static byte[] resetFlag; public static boolean jcardSim = false; @@ -95,6 +97,7 @@ public class KMJCardSimulator implements KMSEProvider { private KMECDeviceUniqueKey testKey; private KMECDeviceUniqueKey deviceUniqueKey; private KMHmacKey preSharedKey; + private KMHmacKey computedHmacKey; private boolean deviceReboot; // Data - originally was in repository @@ -571,12 +574,6 @@ public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLengt return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); } - public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, - byte[] mac, short macStart, short macLength) { - hmacSignature.init(key, Signature.MODE_VERIFY); - return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); - } - @Override public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, short dataLength, byte[] signature, short signatureStart) { @@ -651,10 +648,11 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] dat } @Override - public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, - short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacVerify(key, data, dataStart, dataLength, mac, macStart, macLength); + public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); } @Override @@ -697,6 +695,14 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, b return null; } + @Override + public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { + KMOperationImpl opr = null; + KMHmacKey key = (KMHmacKey) computedHmacKey; + Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); + return new KMOperationImpl(signerVerifier); + } + @Override public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, @@ -1120,6 +1126,15 @@ public Signature createHmacSignerVerifier(short purpose, short digest, byte[] se return hmacSignerVerifier; } + private Signature createHmacSignerVerifier(short purpose, short digest, HMACKey key) { + byte alg = Signature.ALG_HMAC_SHA_256; + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Signature hmacSignerVerifier = Signature.getInstance((byte) alg, false); + hmacSignerVerifier.init(key, (byte) purpose); + return hmacSignerVerifier; + } public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, short secretStart, short secretLength, @@ -1466,6 +1481,20 @@ public boolean isUpgrading() { return false; } + @Override + public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (computedHmacKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), + false); + computedHmacKey = new KMHmacKey(key); + } + computedHmacKey.setKey(keyData, offset, length); + return (KMComputedHmacKey) computedHmacKey; + } + @Override public KMMasterKey createMasterKey(short keySizeBits) { if (masterKey == null) { @@ -1760,5 +1789,24 @@ public void setProvisionLocked(boolean locked) { public boolean isProvisionLocked() { return isProvisionLocked; } + + @Override + public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.getInitializedMessageDigestInstance(MessageDigest.ALG_SHA_256, false); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } catch (Exception e) { + + } + return len; + } + + @Override + public KMComputedHmacKey getComputedHmacKey() { + return (KMComputedHmacKey) computedHmacKey; + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index d23d7f15..e493f3aa 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -63,6 +63,14 @@ void createAsymmetricKey( short pubModMaxLength, short[] lengths); + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then * it should throw a CryptoException. @@ -289,9 +297,7 @@ short hmacKDF( /** * This is a oneshot operation that verifies the signature using hmac algorithm. * - * @param keyBuf is the buffer with hmac key. - * @param keyStart is the start of the buffer. - * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param hmacKey is the computed hmac key. * @param data is the buffer containing data. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -301,9 +307,7 @@ short hmacKDF( * @return true if the signature matches. */ boolean hmacVerify( - byte[] keyBuf, - short keyStart, - short keyLength, + KMComputedHmacKey hmacKey, byte[] data, short dataStart, short dataLength, @@ -555,6 +559,16 @@ KMOperation initAsymmetricOperation( */ boolean isUpgrading(); + /** + * This function creates an HMACKey and initializes the key with the provided input key data. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMComputedHmacKey. + */ + KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length); + /** * This function generates an AES Key of keySizeBits, which is used as an master key. This * generated key is maintained by the SEProvider. This function should be called only once at the @@ -685,6 +699,26 @@ KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, * @return boot certificate chain. */ byte[] getBootCertificateChain(); + + /** + * Returns the computed Hmac key. + * + * @return Instance of the computed hmac key. + */ + KMComputedHmacKey getComputedHmacKey(); + + /** + * This is a one-shot operation the does digest of the input mesage. + * + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. + */ + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, + short outOffset); public boolean isProvisionLocked(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 15ebae13..ac2c07e8 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -17,7 +17,6 @@ package com.android.javacard.keymaster; import com.android.javacard.seprovider.KMAttestationCert; -import com.android.javacard.seprovider.KMComputedHmacKey; import com.android.javacard.seprovider.KMDeviceUniqueKey; import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMHmacKey; From 815e57afff747d8c0ff0b04523202ffd87383191 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Thu, 13 Jan 2022 07:21:57 +0000 Subject: [PATCH 14/15] added confirmation token in HAL --- HAL/JavacardKeyMintOperation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp index 59870668..efbcd562 100644 --- a/HAL/JavacardKeyMintOperation.cpp +++ b/HAL/JavacardKeyMintOperation.cpp @@ -75,7 +75,7 @@ ScopedAStatus JavacardKeyMintOperation::update(const vector& input, ScopedAStatus JavacardKeyMintOperation::finish( const optional>& input, const optional>& signature, const optional& authToken, const optional& timestampToken, - const optional>& /*confirmationToken*/, vector* output) { + const optional>& confirmationToken, vector* output) { HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); const vector confToken = confirmationToken.value_or(vector()); From fd1f5415e197efca88a22f515f1f13ab84fa137f Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Fri, 14 Jan 2022 07:35:30 +0000 Subject: [PATCH 15/15] Handled attest key purpose validation in generate key and import key --- .../javacard/keymaster/KMKeymasterApplet.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index ac2c07e8..530642d2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -3082,6 +3082,14 @@ private void processImportKeyCmd(APDU apdu) { } private void validateImportKey(short params, short keyFmt){ + short attKeyPurpose = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, params); + // ATTEST_KEY cannot be combined with any other purpose. + if (attKeyPurpose != KMType.INVALID_VALUE + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } // Rollback protection not supported KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is @@ -3513,6 +3521,15 @@ private void processGenerateKey(APDU apdu) { // As per specification Early boot keys may be created after early boot ended. // Algorithm must be present KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); + + short attKeyPurpose = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); + // ATTEST_KEY cannot be combined with any other purpose. + if (attKeyPurpose != KMType.INVALID_VALUE + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); // Check algorithm and dispatch to appropriate handler. switch (alg) {