Loading security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -49,5 +49,8 @@ interface IKeyMintDevice { void earlyBootEnded(); byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob); android.hardware.security.keymint.KeyCharacteristics[] getKeyCharacteristics(in byte[] keyBlob, in byte[] appId, in byte[] appData); byte[16] getRootOfTrustChallenge(); byte[] getRootOfTrust(in byte[16] challenge); void sendRootOfTrust(in byte[] rootOfTrust); const int AUTH_TOKEN_MAC_LENGTH = 32; } security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl +78 −0 Original line number Diff line number Diff line Loading @@ -851,4 +851,82 @@ interface IKeyMintDevice { */ KeyCharacteristics[] getKeyCharacteristics( in byte[] keyBlob, in byte[] appId, in byte[] appData); /** * Returns a 16-byte random challenge nonce, used to prove freshness when exchanging root of * trust data. * * This method may only be implemented by StrongBox KeyMint. TEE KeyMint implementations must * return ErrorCode::UNIMPLEMENTED. StrongBox KeyMint implementations MAY return UNIMPLEMENTED, * to indicate that they have an alternative mechanism for getting the data. If the StrongBox * implementation returns UNIMPLEMENTED, the client should not call `getRootofTrust()` or * `sendRootOfTrust()`. */ byte[16] getRootOfTrustChallenge(); /** * Returns the TEE KeyMint Root of Trust data. * * This method is required for TEE KeyMint. StrongBox KeyMint implementations MUST return * ErrorCode::UNIMPLEMENTED. * * The returned data is an encoded COSE_Mac0 structure, denoted MacedRootOfTrust in the * following CDDL schema. Note that K_mac is the shared HMAC key used for auth tokens, etc.: * * MacedRootOfTrust = [ ; COSE_Mac0 (untagged) * protected: bstr .cbor { * 1 : 5, ; Algorithm : HMAC-256 * }, * unprotected : {}, * payload : bstr .cbor RootOfTrust, * tag : bstr HMAC-256(K_mac, MAC_structure) * ] * * MAC_structure = [ * context : "MAC0", * protected : bstr .cbor { * 1 : 5, ; Algorithm : HMAC-256 * }, * external_aad : bstr .size 16 ; Value of challenge argument * payload : bstr .cbor RootOfTrust, * ] * * RootOfTrust = [ * verifiedBootKey : bstr .size 32, * deviceLocked : bool, * verifiedBootState : &VerifiedBootState, * verifiedBootHash : bstr .size 32, * bootPatchLevel : int, ; See Tag::BOOT_PATCHLEVEL * ] * * VerifiedBootState = ( * Verified : 0, * SelfSigned : 1, * Unverified : 2, * Failed : 3 * ) */ byte[] getRootOfTrust(in byte[16] challenge); /** * Delivers the TEE KeyMint Root of Trust data to StrongBox KeyMint. See `getRootOfTrust()` * above for specification of the data format and cryptographic security structure. * * The implementation must verify the MAC on the RootOfTrust data. If it is valid, and if this * is the first time since reboot that StrongBox KeyMint has received this data, it must store * the RoT data for use in key attestation requests, then return ErrorCode::ERROR_OK. * * If the MAC on the Root of Trust data and challenge is incorrect, the implementation must * return ErrorCode::VERIFICATION_FAILED. * * If the RootOfTrust data has already been received since the last boot, the implementation * must validate the data and return ErrorCode::VERIFICATION_FAILED or ErrorCode::ERROR_OK * according to the result, but must not store the data for use in key attestation requests, * even if verification succeeds. On success, the challenge is invalidated and a new challenge * must be requested before the RootOfTrust data may be sent again. * * This method is optional for StrongBox KeyMint, which MUST return ErrorCode::UNIMPLEMENTED if * not implemented. TEE KeyMint implementations must return ErrorCode::UNIMPLEMENTED. */ void sendRootOfTrust(in byte[] rootOfTrust); } security/keymint/aidl/vts/functional/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ cc_test { "AttestKeyTest.cpp", "DeviceUniqueAttestationTest.cpp", "KeyMintTest.cpp", "SecureElementProvisioningTest.cpp", ], static_libs: [ "libkeymint_vts_test_utils", Loading security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp 0 → 100644 +255 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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. */ #define LOG_TAG "keymint_2_se_provisioning_test" #include <map> #include <memory> #include <vector> #include <android-base/logging.h> #include <android/binder_manager.h> #include <cppbor_parse.h> #include <keymaster/cppcose/cppcose.h> #include <keymint_support/key_param_output.h> #include "KeyMintAidlTestBase.h" namespace aidl::android::hardware::security::keymint::test { using std::array; using std::map; using std::shared_ptr; using std::vector; class SecureElementProvisioningTest : public testing::Test { protected: static void SetupTestSuite() { auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); for (auto& param : params) { ASSERT_TRUE(AServiceManager_isDeclared(param.c_str())) << "IKeyMintDevice instance " << param << " found but not declared."; ::ndk::SpAIBinder binder(AServiceManager_waitForService(param.c_str())); auto keymint = IKeyMintDevice::fromBinder(binder); ASSERT_NE(keymint, nullptr) << "Failed to get IKeyMintDevice instance " << param; KeyMintHardwareInfo info; ASSERT_TRUE(keymint->getHardwareInfo(&info).isOk()); ASSERT_EQ(keymints_.count(info.securityLevel), 0) << "There must be exactly one IKeyMintDevice with security level " << info.securityLevel; keymints_[info.securityLevel] = std::move(keymint); } } static map<SecurityLevel, shared_ptr<IKeyMintDevice>> keymints_; }; map<SecurityLevel, shared_ptr<IKeyMintDevice>> SecureElementProvisioningTest::keymints_; TEST_F(SecureElementProvisioningTest, ValidConfigurations) { // TEE is required ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); // StrongBox is optional ASSERT_LE(keymints_.count(SecurityLevel::STRONGBOX), 1); } TEST_F(SecureElementProvisioningTest, TeeOnly) { ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); array<uint8_t, 16> challenge1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; array<uint8_t, 16> challenge2 = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; vector<uint8_t> rootOfTrust1; Status result = tee->getRootOfTrust(challenge1, &rootOfTrust1); // TODO: Remove the next line to require TEEs to succeed. if (!result.isOk()) return; ASSERT_TRUE(result.isOk()); // TODO: Parse and validate rootOfTrust1 here vector<uint8_t> rootOfTrust2; result = tee->getRootOfTrust(challenge2, &rootOfTrust2); ASSERT_TRUE(result.isOk()); // TODO: Parse and validate rootOfTrust2 here ASSERT_NE(rootOfTrust1, rootOfTrust2); vector<uint8_t> rootOfTrust3; result = tee->getRootOfTrust(challenge1, &rootOfTrust3); ASSERT_TRUE(result.isOk()); ASSERT_EQ(rootOfTrust1, rootOfTrust3); // TODO: Parse and validate rootOfTrust3 here } TEST_F(SecureElementProvisioningTest, TeeDoesNotImplementStrongBoxMethods) { ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); array<uint8_t, 16> challenge; Status result = tee->getRootOfTrustChallenge(&challenge); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); result = tee->sendRootOfTrust({}); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); } TEST_F(SecureElementProvisioningTest, StrongBoxDoesNotImplementTeeMethods) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); vector<uint8_t> rootOfTrust; Status result = sb->getRootOfTrust({}, &rootOfTrust); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); } TEST_F(SecureElementProvisioningTest, UnimplementedTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge; Status result = sb->getRootOfTrustChallenge(&challenge); if (!result.isOk()) { // Strongbox does not have to implement this feature if it has uses an alternative mechanism // to provision the root of trust. In that case it MUST return UNIMPLEMENTED, both from // getRootOfTrustChallenge() and from sendRootOfTrust(). ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); result = sb->sendRootOfTrust({}); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); SUCCEED() << "This Strongbox implementation does not use late root of trust delivery."; return; } } TEST_F(SecureElementProvisioningTest, ChallengeQualityTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge1; Status result = sb->getRootOfTrustChallenge(&challenge1); if (!result.isOk()) return; array<uint8_t, 16> challenge2; result = sb->getRootOfTrustChallenge(&challenge2); ASSERT_TRUE(result.isOk()); ASSERT_NE(challenge1, challenge2); // TODO: When we add entropy testing in other relevant places in these tests, add it here, too, // to verify that challenges appear to have adequate entropy. } TEST_F(SecureElementProvisioningTest, ProvisioningTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge; Status result = sb->getRootOfTrustChallenge(&challenge); if (!result.isOk()) return; vector<uint8_t> rootOfTrust; result = tee->getRootOfTrust(challenge, &rootOfTrust); ASSERT_TRUE(result.isOk()); // TODO: Verify COSE_Mac0 structure and content here. result = sb->sendRootOfTrust(rootOfTrust); ASSERT_TRUE(result.isOk()); // Sending again must fail, because a new challenge is required. result = sb->sendRootOfTrust(rootOfTrust); ASSERT_FALSE(result.isOk()); } TEST_F(SecureElementProvisioningTest, InvalidProvisioningTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge; Status result = sb->getRootOfTrustChallenge(&challenge); if (!result.isOk()) return; result = sb->sendRootOfTrust({}); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::VERIFICATION_FAILED); vector<uint8_t> rootOfTrust; result = tee->getRootOfTrust(challenge, &rootOfTrust); ASSERT_TRUE(result.isOk()); vector<uint8_t> corruptedRootOfTrust = rootOfTrust; corruptedRootOfTrust[corruptedRootOfTrust.size() / 2]++; result = sb->sendRootOfTrust(corruptedRootOfTrust); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::VERIFICATION_FAILED); // Now try the correct RoT result = sb->sendRootOfTrust(rootOfTrust); ASSERT_TRUE(result.isOk()); } } // namespace aidl::android::hardware::security::keymint::test Loading
security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -49,5 +49,8 @@ interface IKeyMintDevice { void earlyBootEnded(); byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob); android.hardware.security.keymint.KeyCharacteristics[] getKeyCharacteristics(in byte[] keyBlob, in byte[] appId, in byte[] appData); byte[16] getRootOfTrustChallenge(); byte[] getRootOfTrust(in byte[16] challenge); void sendRootOfTrust(in byte[] rootOfTrust); const int AUTH_TOKEN_MAC_LENGTH = 32; }
security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl +78 −0 Original line number Diff line number Diff line Loading @@ -851,4 +851,82 @@ interface IKeyMintDevice { */ KeyCharacteristics[] getKeyCharacteristics( in byte[] keyBlob, in byte[] appId, in byte[] appData); /** * Returns a 16-byte random challenge nonce, used to prove freshness when exchanging root of * trust data. * * This method may only be implemented by StrongBox KeyMint. TEE KeyMint implementations must * return ErrorCode::UNIMPLEMENTED. StrongBox KeyMint implementations MAY return UNIMPLEMENTED, * to indicate that they have an alternative mechanism for getting the data. If the StrongBox * implementation returns UNIMPLEMENTED, the client should not call `getRootofTrust()` or * `sendRootOfTrust()`. */ byte[16] getRootOfTrustChallenge(); /** * Returns the TEE KeyMint Root of Trust data. * * This method is required for TEE KeyMint. StrongBox KeyMint implementations MUST return * ErrorCode::UNIMPLEMENTED. * * The returned data is an encoded COSE_Mac0 structure, denoted MacedRootOfTrust in the * following CDDL schema. Note that K_mac is the shared HMAC key used for auth tokens, etc.: * * MacedRootOfTrust = [ ; COSE_Mac0 (untagged) * protected: bstr .cbor { * 1 : 5, ; Algorithm : HMAC-256 * }, * unprotected : {}, * payload : bstr .cbor RootOfTrust, * tag : bstr HMAC-256(K_mac, MAC_structure) * ] * * MAC_structure = [ * context : "MAC0", * protected : bstr .cbor { * 1 : 5, ; Algorithm : HMAC-256 * }, * external_aad : bstr .size 16 ; Value of challenge argument * payload : bstr .cbor RootOfTrust, * ] * * RootOfTrust = [ * verifiedBootKey : bstr .size 32, * deviceLocked : bool, * verifiedBootState : &VerifiedBootState, * verifiedBootHash : bstr .size 32, * bootPatchLevel : int, ; See Tag::BOOT_PATCHLEVEL * ] * * VerifiedBootState = ( * Verified : 0, * SelfSigned : 1, * Unverified : 2, * Failed : 3 * ) */ byte[] getRootOfTrust(in byte[16] challenge); /** * Delivers the TEE KeyMint Root of Trust data to StrongBox KeyMint. See `getRootOfTrust()` * above for specification of the data format and cryptographic security structure. * * The implementation must verify the MAC on the RootOfTrust data. If it is valid, and if this * is the first time since reboot that StrongBox KeyMint has received this data, it must store * the RoT data for use in key attestation requests, then return ErrorCode::ERROR_OK. * * If the MAC on the Root of Trust data and challenge is incorrect, the implementation must * return ErrorCode::VERIFICATION_FAILED. * * If the RootOfTrust data has already been received since the last boot, the implementation * must validate the data and return ErrorCode::VERIFICATION_FAILED or ErrorCode::ERROR_OK * according to the result, but must not store the data for use in key attestation requests, * even if verification succeeds. On success, the challenge is invalidated and a new challenge * must be requested before the RootOfTrust data may be sent again. * * This method is optional for StrongBox KeyMint, which MUST return ErrorCode::UNIMPLEMENTED if * not implemented. TEE KeyMint implementations must return ErrorCode::UNIMPLEMENTED. */ void sendRootOfTrust(in byte[] rootOfTrust); }
security/keymint/aidl/vts/functional/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ cc_test { "AttestKeyTest.cpp", "DeviceUniqueAttestationTest.cpp", "KeyMintTest.cpp", "SecureElementProvisioningTest.cpp", ], static_libs: [ "libkeymint_vts_test_utils", Loading
security/keymint/aidl/vts/functional/SecureElementProvisioningTest.cpp 0 → 100644 +255 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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. */ #define LOG_TAG "keymint_2_se_provisioning_test" #include <map> #include <memory> #include <vector> #include <android-base/logging.h> #include <android/binder_manager.h> #include <cppbor_parse.h> #include <keymaster/cppcose/cppcose.h> #include <keymint_support/key_param_output.h> #include "KeyMintAidlTestBase.h" namespace aidl::android::hardware::security::keymint::test { using std::array; using std::map; using std::shared_ptr; using std::vector; class SecureElementProvisioningTest : public testing::Test { protected: static void SetupTestSuite() { auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); for (auto& param : params) { ASSERT_TRUE(AServiceManager_isDeclared(param.c_str())) << "IKeyMintDevice instance " << param << " found but not declared."; ::ndk::SpAIBinder binder(AServiceManager_waitForService(param.c_str())); auto keymint = IKeyMintDevice::fromBinder(binder); ASSERT_NE(keymint, nullptr) << "Failed to get IKeyMintDevice instance " << param; KeyMintHardwareInfo info; ASSERT_TRUE(keymint->getHardwareInfo(&info).isOk()); ASSERT_EQ(keymints_.count(info.securityLevel), 0) << "There must be exactly one IKeyMintDevice with security level " << info.securityLevel; keymints_[info.securityLevel] = std::move(keymint); } } static map<SecurityLevel, shared_ptr<IKeyMintDevice>> keymints_; }; map<SecurityLevel, shared_ptr<IKeyMintDevice>> SecureElementProvisioningTest::keymints_; TEST_F(SecureElementProvisioningTest, ValidConfigurations) { // TEE is required ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); // StrongBox is optional ASSERT_LE(keymints_.count(SecurityLevel::STRONGBOX), 1); } TEST_F(SecureElementProvisioningTest, TeeOnly) { ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); array<uint8_t, 16> challenge1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; array<uint8_t, 16> challenge2 = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; vector<uint8_t> rootOfTrust1; Status result = tee->getRootOfTrust(challenge1, &rootOfTrust1); // TODO: Remove the next line to require TEEs to succeed. if (!result.isOk()) return; ASSERT_TRUE(result.isOk()); // TODO: Parse and validate rootOfTrust1 here vector<uint8_t> rootOfTrust2; result = tee->getRootOfTrust(challenge2, &rootOfTrust2); ASSERT_TRUE(result.isOk()); // TODO: Parse and validate rootOfTrust2 here ASSERT_NE(rootOfTrust1, rootOfTrust2); vector<uint8_t> rootOfTrust3; result = tee->getRootOfTrust(challenge1, &rootOfTrust3); ASSERT_TRUE(result.isOk()); ASSERT_EQ(rootOfTrust1, rootOfTrust3); // TODO: Parse and validate rootOfTrust3 here } TEST_F(SecureElementProvisioningTest, TeeDoesNotImplementStrongBoxMethods) { ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); array<uint8_t, 16> challenge; Status result = tee->getRootOfTrustChallenge(&challenge); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); result = tee->sendRootOfTrust({}); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); } TEST_F(SecureElementProvisioningTest, StrongBoxDoesNotImplementTeeMethods) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); vector<uint8_t> rootOfTrust; Status result = sb->getRootOfTrust({}, &rootOfTrust); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); } TEST_F(SecureElementProvisioningTest, UnimplementedTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge; Status result = sb->getRootOfTrustChallenge(&challenge); if (!result.isOk()) { // Strongbox does not have to implement this feature if it has uses an alternative mechanism // to provision the root of trust. In that case it MUST return UNIMPLEMENTED, both from // getRootOfTrustChallenge() and from sendRootOfTrust(). ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); result = sb->sendRootOfTrust({}); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::UNIMPLEMENTED); SUCCEED() << "This Strongbox implementation does not use late root of trust delivery."; return; } } TEST_F(SecureElementProvisioningTest, ChallengeQualityTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge1; Status result = sb->getRootOfTrustChallenge(&challenge1); if (!result.isOk()) return; array<uint8_t, 16> challenge2; result = sb->getRootOfTrustChallenge(&challenge2); ASSERT_TRUE(result.isOk()); ASSERT_NE(challenge1, challenge2); // TODO: When we add entropy testing in other relevant places in these tests, add it here, too, // to verify that challenges appear to have adequate entropy. } TEST_F(SecureElementProvisioningTest, ProvisioningTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge; Status result = sb->getRootOfTrustChallenge(&challenge); if (!result.isOk()) return; vector<uint8_t> rootOfTrust; result = tee->getRootOfTrust(challenge, &rootOfTrust); ASSERT_TRUE(result.isOk()); // TODO: Verify COSE_Mac0 structure and content here. result = sb->sendRootOfTrust(rootOfTrust); ASSERT_TRUE(result.isOk()); // Sending again must fail, because a new challenge is required. result = sb->sendRootOfTrust(rootOfTrust); ASSERT_FALSE(result.isOk()); } TEST_F(SecureElementProvisioningTest, InvalidProvisioningTest) { if (keymints_.count(SecurityLevel::STRONGBOX) == 0) return; // Need a StrongBox to provision. ASSERT_EQ(keymints_.count(SecurityLevel::TRUSTED_ENVIRONMENT), 1); auto tee = keymints_.find(SecurityLevel::TRUSTED_ENVIRONMENT)->second; ASSERT_NE(tee, nullptr); ASSERT_EQ(keymints_.count(SecurityLevel::STRONGBOX), 1); auto sb = keymints_.find(SecurityLevel::STRONGBOX)->second; ASSERT_NE(sb, nullptr); array<uint8_t, 16> challenge; Status result = sb->getRootOfTrustChallenge(&challenge); if (!result.isOk()) return; result = sb->sendRootOfTrust({}); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::VERIFICATION_FAILED); vector<uint8_t> rootOfTrust; result = tee->getRootOfTrust(challenge, &rootOfTrust); ASSERT_TRUE(result.isOk()); vector<uint8_t> corruptedRootOfTrust = rootOfTrust; corruptedRootOfTrust[corruptedRootOfTrust.size() / 2]++; result = sb->sendRootOfTrust(corruptedRootOfTrust); ASSERT_FALSE(result.isOk()); ASSERT_EQ(result.getExceptionCode(), EX_SERVICE_SPECIFIC); ASSERT_EQ(static_cast<ErrorCode>(result.getServiceSpecificError()), ErrorCode::VERIFICATION_FAILED); // Now try the correct RoT result = sb->sendRootOfTrust(rootOfTrust); ASSERT_TRUE(result.isOk()); } } // namespace aidl::android::hardware::security::keymint::test