Loading security/keymint/aidl/vts/functional/AuthTest.cpp +227 −9 Original line number Diff line number Diff line Loading @@ -93,17 +93,21 @@ class AuthTest : public KeyMintAidlTestBase { void TearDown() { if (gk_ == nullptr) return; gk_->deleteUser(uid_); if (alt_uid_ != 0) { gk_->deleteUser(alt_uid_); } } bool GatekeeperAvailable() { return (gk_ != nullptr) || (hidl_gk_ != nullptr); } std::optional<GatekeeperEnrollResponse> doEnroll(const std::vector<uint8_t>& newPwd, std::optional<GatekeeperEnrollResponse> doEnroll(uint32_t uid, const std::vector<uint8_t>& newPwd, const std::vector<uint8_t>& curHandle = {}, const std::vector<uint8_t>& curPwd = {}) { if (gk_ != nullptr) { while (true) { GatekeeperEnrollResponse rsp; Status status = gk_->enroll(uid_, curHandle, curPwd, newPwd, &rsp); Status status = gk_->enroll(uid, curHandle, curPwd, newPwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); Loading @@ -120,7 +124,7 @@ class AuthTest : public KeyMintAidlTestBase { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->enroll( uid_, curHandle, curPwd, newPwd, uid, curHandle, curPwd, newPwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doEnroll(HIDL) failed"; Loading Loading @@ -155,20 +159,23 @@ class AuthTest : public KeyMintAidlTestBase { } } std::optional<GatekeeperEnrollResponse> doEnroll(const string& newPwd, std::optional<GatekeeperEnrollResponse> doEnroll(uint32_t uid, const string& newPwd, const std::vector<uint8_t>& curHandle = {}, const string& curPwd = {}) { return doEnroll(std::vector<uint8_t>(newPwd.begin(), newPwd.end()), curHandle, return doEnroll(uid, std::vector<uint8_t>(newPwd.begin(), newPwd.end()), curHandle, std::vector<uint8_t>(curPwd.begin(), curPwd.end())); } std::optional<GatekeeperEnrollResponse> doEnroll(const string& newPwd) { return doEnroll(uid_, newPwd); } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, std::optional<HardwareAuthToken> doVerify(uint32_t uid, uint64_t challenge, const std::vector<uint8_t>& handle, const std::vector<uint8_t>& pwd) { if (gk_ != nullptr) { while (true) { GatekeeperVerifyResponse rsp; Status status = gk_->verify(uid_, challenge, handle, pwd, &rsp); Status status = gk_->verify(uid, challenge, handle, pwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); Loading @@ -185,7 +192,7 @@ class AuthTest : public KeyMintAidlTestBase { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->verify( uid_, challenge, handle, pwd, uid, challenge, handle, pwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doVerify(HIDL) failed"; Loading Loading @@ -220,10 +227,15 @@ class AuthTest : public KeyMintAidlTestBase { return std::nullopt; } } std::optional<HardwareAuthToken> doVerify(uint32_t uid, uint64_t challenge, const std::vector<uint8_t>& handle, const string& pwd) { return doVerify(uid, challenge, handle, std::vector<uint8_t>(pwd.begin(), pwd.end())); } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, const std::vector<uint8_t>& handle, const string& pwd) { return doVerify(challenge, handle, std::vector<uint8_t>(pwd.begin(), pwd.end())); return doVerify(uid_, challenge, handle, pwd); } // Variants of the base class methods but with authentication information included. Loading Loading @@ -268,6 +280,13 @@ class AuthTest : public KeyMintAidlTestBase { return plaintext; } string SignMessage(const vector<uint8_t>& key_blob, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params, const HardwareAuthToken& hat) { SCOPED_TRACE("SignMessage"); return ProcessMessage(key_blob, KeyPurpose::SIGN, message, in_params, out_params, hat); } protected: std::shared_ptr<IGatekeeper> gk_; sp<IHidlGatekeeper> hidl_gk_; Loading @@ -275,6 +294,8 @@ class AuthTest : public KeyMintAidlTestBase { string password_; uint32_t uid_; int64_t sid_; uint32_t alt_uid_; int64_t alt_sid_; std::vector<uint8_t> handle_; }; Loading Loading @@ -347,6 +368,116 @@ TEST_P(AuthTest, TimeoutAuthentication) { } } // Test use of a key that requires user-authentication within recent history, but where // the `TimestampToken` provided to the device is unrelated to the in-progress operation. TEST_P(AuthTest, TimeoutAuthenticationIncorrectTimestampToken) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } if (!timestamp_token_required_) { GTEST_SKIP() << "Test only applies to devices with no secure clock"; } if (clock_ == nullptr) { GTEST_SKIP() << "Device requires timestamps and no ISecureClock available"; } // Create an AES key that requires authentication within the last 3 seconds. const uint32_t timeout_secs = 3; auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD) .Authorization(TAG_AUTH_TIMEOUT, timeout_secs); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Verify to get a HAT, arbitrary challenge. const uint64_t challenge = 42; const std::optional<HardwareAuthToken> hat = doVerify(challenge, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); // KeyMint implementation has no clock, so only detects timeout via timestamp token provided // on update()/finish(). However, for this test we ensure that that the timestamp token has a // *different* challenge value. const string message = "Hello World!"; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); AuthorizationSet out_params; ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, hat)); secureclock::TimeStampToken time_token; EXPECT_EQ(ErrorCode::OK, GetReturnErrorCode(clock_->generateTimeStamp(challenge_ + 1, &time_token))); string output; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output, hat, time_token)); } // Test use of a key with multiple USER_SECURE_ID values. For variety, use an EC signing key // generated with attestation. TEST_P(AuthTest, TimeoutAuthenticationMultiSid) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } if (timestamp_token_required_ && clock_ == nullptr) { GTEST_SKIP() << "Device requires timestamps and no ISecureClock available"; } // Enroll a password for a second user. alt_uid_ = 20001; const string alt_password = "correcthorsebatterystaple2"; std::optional<GatekeeperEnrollResponse> rsp = doEnroll(alt_uid_, alt_password); ASSERT_TRUE(rsp.has_value()); alt_sid_ = rsp->secureUserId; const std::vector<uint8_t> alt_handle = rsp->data; // Create an attested EC key that requires authentication within the last 3 seconds from either // secure ID. Also allow any authenticator type. const uint32_t timeout_secs = 3; auto builder = AuthorizationSetBuilder() .EcdsaSigningKey(EcCurve::P_256) .Digest(Digest::NONE) .Digest(Digest::SHA_2_256) .SetDefaultValidity() .AttestationChallenge("challenge") .AttestationApplicationId("app_id") .Authorization(TAG_USER_SECURE_ID, alt_sid_) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::ANY) .Authorization(TAG_AUTH_TIMEOUT, timeout_secs); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Verify first user to get a HAT that should work. const uint64_t challenge = 42; const std::optional<HardwareAuthToken> hat = doVerify(uid_, challenge, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); const string message = "Hello World!"; auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256); AuthorizationSet out_params; const string signature = SignMessage(keyblob, message, params, &out_params, hat.value()); // Verify second user to get a HAT that should work. const uint64_t alt_challenge = 43; const std::optional<HardwareAuthToken> alt_hat = doVerify(alt_uid_, alt_challenge, alt_handle, alt_password); ASSERT_TRUE(alt_hat.has_value()); EXPECT_EQ(alt_hat->userId, alt_sid_); const string alt_signature = SignMessage(keyblob, message, params, &out_params, alt_hat.value()); } // Test use of a key that requires an auth token for each action on the operation, with // a per-operation challenge value included. TEST_P(AuthTest, AuthPerOperation) { Loading Loading @@ -407,6 +538,93 @@ TEST_P(AuthTest, AuthPerOperation) { Finish(message, {} /* signature */, &ciphertext, hat.value())); } // Test use of a key that requires an auth token for each action on the operation, with // a per-operation challenge value included, with multiple secure IDs allowed. TEST_P(AuthTest, AuthPerOperationMultiSid) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } // Enroll a password for a second user. alt_uid_ = 20001; const string alt_password = "correcthorsebatterystaple2"; std::optional<GatekeeperEnrollResponse> rsp = doEnroll(alt_uid_, alt_password); ASSERT_TRUE(rsp.has_value()); alt_sid_ = rsp->secureUserId; const std::vector<uint8_t> alt_handle = rsp->data; // Create an AES key that requires authentication per-action. auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_SECURE_ID, alt_sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::ANY); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Get a HAT for first user with the challenge from an in-progress operation. const string message = "Hello World!"; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); AuthorizationSet out_params; EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> hat = doVerify(uid_, challenge_, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); string ciphertext; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &ciphertext, hat.value())); // Get a HAT for second user with the challenge from an in-progress operation. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> alt_hat = doVerify(alt_uid_, challenge_, alt_handle, alt_password); ASSERT_TRUE(alt_hat.has_value()); EXPECT_EQ(alt_hat->userId, alt_sid_); string alt_ciphertext; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &ciphertext, alt_hat.value())); } // Test use of a key that requires an auth token for each action on the operation, but // which gets passed a HAT of the wrong type TEST_P(AuthTest, AuthPerOperationWrongAuthType) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } // Create an AES key that requires authentication per-action, but with no valid authenticator // types. auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::FINGERPRINT); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Get a HAT with the challenge from an in-progress operation. const string message = "Hello World!"; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); AuthorizationSet out_params; EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> hat = doVerify(challenge_, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); // Should fail because auth type doesn't (can't) match. string ciphertext; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &ciphertext, hat.value())); } INSTANTIATE_KEYMINT_AIDL_TEST(AuthTest); } // namespace aidl::android::hardware::security::keymint::test Loading
security/keymint/aidl/vts/functional/AuthTest.cpp +227 −9 Original line number Diff line number Diff line Loading @@ -93,17 +93,21 @@ class AuthTest : public KeyMintAidlTestBase { void TearDown() { if (gk_ == nullptr) return; gk_->deleteUser(uid_); if (alt_uid_ != 0) { gk_->deleteUser(alt_uid_); } } bool GatekeeperAvailable() { return (gk_ != nullptr) || (hidl_gk_ != nullptr); } std::optional<GatekeeperEnrollResponse> doEnroll(const std::vector<uint8_t>& newPwd, std::optional<GatekeeperEnrollResponse> doEnroll(uint32_t uid, const std::vector<uint8_t>& newPwd, const std::vector<uint8_t>& curHandle = {}, const std::vector<uint8_t>& curPwd = {}) { if (gk_ != nullptr) { while (true) { GatekeeperEnrollResponse rsp; Status status = gk_->enroll(uid_, curHandle, curPwd, newPwd, &rsp); Status status = gk_->enroll(uid, curHandle, curPwd, newPwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); Loading @@ -120,7 +124,7 @@ class AuthTest : public KeyMintAidlTestBase { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->enroll( uid_, curHandle, curPwd, newPwd, uid, curHandle, curPwd, newPwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doEnroll(HIDL) failed"; Loading Loading @@ -155,20 +159,23 @@ class AuthTest : public KeyMintAidlTestBase { } } std::optional<GatekeeperEnrollResponse> doEnroll(const string& newPwd, std::optional<GatekeeperEnrollResponse> doEnroll(uint32_t uid, const string& newPwd, const std::vector<uint8_t>& curHandle = {}, const string& curPwd = {}) { return doEnroll(std::vector<uint8_t>(newPwd.begin(), newPwd.end()), curHandle, return doEnroll(uid, std::vector<uint8_t>(newPwd.begin(), newPwd.end()), curHandle, std::vector<uint8_t>(curPwd.begin(), curPwd.end())); } std::optional<GatekeeperEnrollResponse> doEnroll(const string& newPwd) { return doEnroll(uid_, newPwd); } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, std::optional<HardwareAuthToken> doVerify(uint32_t uid, uint64_t challenge, const std::vector<uint8_t>& handle, const std::vector<uint8_t>& pwd) { if (gk_ != nullptr) { while (true) { GatekeeperVerifyResponse rsp; Status status = gk_->verify(uid_, challenge, handle, pwd, &rsp); Status status = gk_->verify(uid, challenge, handle, pwd, &rsp); if (!status.isOk() && status.getExceptionCode() == EX_SERVICE_SPECIFIC && status.getServiceSpecificError() == IGatekeeper::ERROR_RETRY_TIMEOUT) { sleep(1); Loading @@ -185,7 +192,7 @@ class AuthTest : public KeyMintAidlTestBase { while (true) { HidlGatekeeperResponse rsp; auto status = hidl_gk_->verify( uid_, challenge, handle, pwd, uid, challenge, handle, pwd, [&rsp](const HidlGatekeeperResponse& cbRsp) { rsp = cbRsp; }); if (!status.isOk()) { GTEST_LOG_(ERROR) << "doVerify(HIDL) failed"; Loading Loading @@ -220,10 +227,15 @@ class AuthTest : public KeyMintAidlTestBase { return std::nullopt; } } std::optional<HardwareAuthToken> doVerify(uint32_t uid, uint64_t challenge, const std::vector<uint8_t>& handle, const string& pwd) { return doVerify(uid, challenge, handle, std::vector<uint8_t>(pwd.begin(), pwd.end())); } std::optional<HardwareAuthToken> doVerify(uint64_t challenge, const std::vector<uint8_t>& handle, const string& pwd) { return doVerify(challenge, handle, std::vector<uint8_t>(pwd.begin(), pwd.end())); return doVerify(uid_, challenge, handle, pwd); } // Variants of the base class methods but with authentication information included. Loading Loading @@ -268,6 +280,13 @@ class AuthTest : public KeyMintAidlTestBase { return plaintext; } string SignMessage(const vector<uint8_t>& key_blob, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params, const HardwareAuthToken& hat) { SCOPED_TRACE("SignMessage"); return ProcessMessage(key_blob, KeyPurpose::SIGN, message, in_params, out_params, hat); } protected: std::shared_ptr<IGatekeeper> gk_; sp<IHidlGatekeeper> hidl_gk_; Loading @@ -275,6 +294,8 @@ class AuthTest : public KeyMintAidlTestBase { string password_; uint32_t uid_; int64_t sid_; uint32_t alt_uid_; int64_t alt_sid_; std::vector<uint8_t> handle_; }; Loading Loading @@ -347,6 +368,116 @@ TEST_P(AuthTest, TimeoutAuthentication) { } } // Test use of a key that requires user-authentication within recent history, but where // the `TimestampToken` provided to the device is unrelated to the in-progress operation. TEST_P(AuthTest, TimeoutAuthenticationIncorrectTimestampToken) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } if (!timestamp_token_required_) { GTEST_SKIP() << "Test only applies to devices with no secure clock"; } if (clock_ == nullptr) { GTEST_SKIP() << "Device requires timestamps and no ISecureClock available"; } // Create an AES key that requires authentication within the last 3 seconds. const uint32_t timeout_secs = 3; auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD) .Authorization(TAG_AUTH_TIMEOUT, timeout_secs); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Verify to get a HAT, arbitrary challenge. const uint64_t challenge = 42; const std::optional<HardwareAuthToken> hat = doVerify(challenge, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); // KeyMint implementation has no clock, so only detects timeout via timestamp token provided // on update()/finish(). However, for this test we ensure that that the timestamp token has a // *different* challenge value. const string message = "Hello World!"; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); AuthorizationSet out_params; ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params, hat)); secureclock::TimeStampToken time_token; EXPECT_EQ(ErrorCode::OK, GetReturnErrorCode(clock_->generateTimeStamp(challenge_ + 1, &time_token))); string output; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &output, hat, time_token)); } // Test use of a key with multiple USER_SECURE_ID values. For variety, use an EC signing key // generated with attestation. TEST_P(AuthTest, TimeoutAuthenticationMultiSid) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } if (timestamp_token_required_ && clock_ == nullptr) { GTEST_SKIP() << "Device requires timestamps and no ISecureClock available"; } // Enroll a password for a second user. alt_uid_ = 20001; const string alt_password = "correcthorsebatterystaple2"; std::optional<GatekeeperEnrollResponse> rsp = doEnroll(alt_uid_, alt_password); ASSERT_TRUE(rsp.has_value()); alt_sid_ = rsp->secureUserId; const std::vector<uint8_t> alt_handle = rsp->data; // Create an attested EC key that requires authentication within the last 3 seconds from either // secure ID. Also allow any authenticator type. const uint32_t timeout_secs = 3; auto builder = AuthorizationSetBuilder() .EcdsaSigningKey(EcCurve::P_256) .Digest(Digest::NONE) .Digest(Digest::SHA_2_256) .SetDefaultValidity() .AttestationChallenge("challenge") .AttestationApplicationId("app_id") .Authorization(TAG_USER_SECURE_ID, alt_sid_) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::ANY) .Authorization(TAG_AUTH_TIMEOUT, timeout_secs); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Verify first user to get a HAT that should work. const uint64_t challenge = 42; const std::optional<HardwareAuthToken> hat = doVerify(uid_, challenge, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); const string message = "Hello World!"; auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256); AuthorizationSet out_params; const string signature = SignMessage(keyblob, message, params, &out_params, hat.value()); // Verify second user to get a HAT that should work. const uint64_t alt_challenge = 43; const std::optional<HardwareAuthToken> alt_hat = doVerify(alt_uid_, alt_challenge, alt_handle, alt_password); ASSERT_TRUE(alt_hat.has_value()); EXPECT_EQ(alt_hat->userId, alt_sid_); const string alt_signature = SignMessage(keyblob, message, params, &out_params, alt_hat.value()); } // Test use of a key that requires an auth token for each action on the operation, with // a per-operation challenge value included. TEST_P(AuthTest, AuthPerOperation) { Loading Loading @@ -407,6 +538,93 @@ TEST_P(AuthTest, AuthPerOperation) { Finish(message, {} /* signature */, &ciphertext, hat.value())); } // Test use of a key that requires an auth token for each action on the operation, with // a per-operation challenge value included, with multiple secure IDs allowed. TEST_P(AuthTest, AuthPerOperationMultiSid) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } // Enroll a password for a second user. alt_uid_ = 20001; const string alt_password = "correcthorsebatterystaple2"; std::optional<GatekeeperEnrollResponse> rsp = doEnroll(alt_uid_, alt_password); ASSERT_TRUE(rsp.has_value()); alt_sid_ = rsp->secureUserId; const std::vector<uint8_t> alt_handle = rsp->data; // Create an AES key that requires authentication per-action. auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_SECURE_ID, alt_sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::ANY); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Get a HAT for first user with the challenge from an in-progress operation. const string message = "Hello World!"; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); AuthorizationSet out_params; EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> hat = doVerify(uid_, challenge_, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); string ciphertext; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &ciphertext, hat.value())); // Get a HAT for second user with the challenge from an in-progress operation. EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> alt_hat = doVerify(alt_uid_, challenge_, alt_handle, alt_password); ASSERT_TRUE(alt_hat.has_value()); EXPECT_EQ(alt_hat->userId, alt_sid_); string alt_ciphertext; EXPECT_EQ(ErrorCode::OK, Finish(message, {} /* signature */, &ciphertext, alt_hat.value())); } // Test use of a key that requires an auth token for each action on the operation, but // which gets passed a HAT of the wrong type TEST_P(AuthTest, AuthPerOperationWrongAuthType) { if (!GatekeeperAvailable()) { GTEST_SKIP() << "No Gatekeeper available"; } // Create an AES key that requires authentication per-action, but with no valid authenticator // types. auto builder = AuthorizationSetBuilder() .AesEncryptionKey(256) .BlockMode(BlockMode::ECB) .Padding(PaddingMode::PKCS7) .Authorization(TAG_USER_SECURE_ID, sid_) .Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::FINGERPRINT); vector<uint8_t> keyblob; vector<KeyCharacteristics> key_characteristics; vector<Certificate> cert_chain; ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, std::nullopt, &keyblob, &key_characteristics, &cert_chain)); // Get a HAT with the challenge from an in-progress operation. const string message = "Hello World!"; auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); AuthorizationSet out_params; EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, keyblob, params, &out_params)); const std::optional<HardwareAuthToken> hat = doVerify(challenge_, handle_, password_); ASSERT_TRUE(hat.has_value()); EXPECT_EQ(hat->userId, sid_); // Should fail because auth type doesn't (can't) match. string ciphertext; EXPECT_EQ(ErrorCode::KEY_USER_NOT_AUTHENTICATED, Finish(message, {} /* signature */, &ciphertext, hat.value())); } INSTANTIATE_KEYMINT_AIDL_TEST(AuthTest); } // namespace aidl::android::hardware::security::keymint::test