| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <regex> // NOLINT(build/c++11) |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/check.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/test/task_environment.h> |
| #include <base/time/time.h> |
| #include <brillo/dbus/mock_dbus_method_response.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "u2fd/mock_tpm_vendor_cmd.h" |
| #include "u2fd/u2f_command_processor_gsc.h" |
| #include "u2fd/util.h" |
| |
| namespace u2f { |
| namespace { |
| |
| using ::brillo::dbus_utils::MockDBusMethodResponse; |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Matcher; |
| using ::testing::MatchesRegex; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| using ::testing::StrictMock; |
| |
| constexpr base::TimeDelta kVerificationTimeout = |
| base::TimeDelta::FromSeconds(10); |
| constexpr base::TimeDelta kRequestPresenceDelay = |
| base::TimeDelta::FromMilliseconds(500); |
| constexpr int kMaxRetries = kVerificationTimeout / kRequestPresenceDelay; |
| constexpr uint32_t kCr50StatusSuccess = 0; |
| constexpr uint32_t kCr50StatusNotAllowed = 0x507; |
| constexpr uint32_t kCr50StatusPasswordRequired = 0x50a; |
| |
| constexpr char kCredentialSecret[65] = {[0 ... 63] = 'E', '\0'}; |
| // Dummy RP id. |
| constexpr char kRpId[] = "example.com"; |
| // Wrong RP id is used to test app id extension path. |
| constexpr char kWrongRpId[] = "wrong.com"; |
| |
| std::vector<uint8_t> GetRpIdHash() { |
| return util::Sha256(std::string(kRpId)); |
| } |
| |
| std::vector<uint8_t> GetWrongRpIdHash() { |
| return util::Sha256(std::string(kWrongRpId)); |
| } |
| |
| std::vector<uint8_t> GetHashToSign() { |
| return std::vector<uint8_t>(U2F_P256_SIZE, 0xcd); |
| } |
| |
| std::vector<uint8_t> GetDataToSign() { |
| return std::vector<uint8_t>(256, 0xcd); |
| } |
| |
| std::vector<uint8_t> GetUserSecret() { |
| return std::vector<uint8_t>(32, 'E'); |
| } |
| |
| std::vector<uint8_t> GetCredId() { |
| return std::vector<uint8_t>(U2F_V0_KH_SIZE, 0xFD); |
| } |
| |
| std::vector<uint8_t> GetVersionedCredId() { |
| return std::vector<uint8_t>(U2F_V1_KH_SIZE + SHA256_DIGEST_LENGTH, 0xFD); |
| } |
| |
| std::vector<uint8_t> GetAuthTimeSecretHash() { |
| return std::vector<uint8_t>(32, 0xFD); |
| } |
| |
| std::string ExpectedUserPresenceU2fGenerateRequestRegex(bool uv_compatible) { |
| // See U2F_GENERATE_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex_uv_compatible = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("[A-F0-9]{64}") + // Credential Secret |
| std::string("0B") + // U2F_UV_ENABLED_KH | U2F_AUTH_ENFORCE |
| std::string("(FD){32}"); // Auth time secret hash |
| |
| static const std::string request_regex = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("(EE){32}") + // Legacy user secret |
| std::string("03") + // U2F_UV_ENABLED_KH | U2F_AUTH_ENFORCE |
| std::string("(00){32}"); // Auth time secret hash, unset |
| return uv_compatible ? request_regex_uv_compatible : request_regex; |
| } |
| |
| std::string ExpectedUserVerificationU2fGenerateRequestRegex() { |
| // See U2F_GENERATE_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("[A-F0-9]{64}") + // Credential Secret |
| std::string("08") + // U2F_UV_ENABLED_KH |
| std::string("(FD){32}"); // Auth time secret hash |
| return request_regex; |
| } |
| |
| // Only used to test U2fSign, where the hash to sign can be determined. |
| std::string ExpectedDeterministicU2fSignRequestRegex() { |
| // See U2F_SIGN_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("(EE){32}") + // Credential Secret |
| std::string("(FD){64}") + // Key handle |
| std::string("(CD){32}") + // Hash to sign |
| std::string("03"); // U2F_AUTH_ENFORCE |
| return request_regex; |
| } |
| |
| // User-verification flow version. |
| std::string ExpectedDeterministicU2fSignVersionedRequestRegex() { |
| // See U2F_SIGN_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("(EE){32}") + // User Secret |
| std::string("(00){32}") + // Auth time secret |
| std::string("(CD){32}") + // Hash to sign |
| std::string("00") + // Flag |
| std::string("(FD){113}"); // Versioned Key handle |
| return request_regex; |
| } |
| |
| std::string ExpectedU2fSignCheckOnlyRequestRegex() { |
| // See U2F_SIGN_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("(EE){32}") + // User Secret |
| std::string("(FD){64}") + // Key handle |
| std::string("(00){32}") + // Hash to sign (empty) |
| std::string("07"); // U2F_AUTH_CHECK_ONLY |
| return request_regex; |
| } |
| |
| std::string ExpectedU2fSignCheckOnlyRequestRegexWrongRpId() { |
| // See U2F_SIGN_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex = |
| base::HexEncode(GetWrongRpIdHash().data(), |
| GetWrongRpIdHash().size()) + // AppId |
| std::string("(EE){32}") + // User Secret |
| std::string("(FD){64}") + // Key handle |
| std::string("(00){32}") + // Hash to sign (empty) |
| std::string("07"); // U2F_AUTH_CHECK_ONLY |
| return request_regex; |
| } |
| |
| // User-verification flow version |
| std::string ExpectedU2fSignCheckOnlyVersionedRequestRegex() { |
| // See U2F_SIGN_REQ in //platform/ec/include/u2f.h |
| static const std::string request_regex = |
| base::HexEncode(GetRpIdHash().data(), GetRpIdHash().size()) + // AppId |
| std::string("(EE){32}") + // User Secret |
| std::string("(00){32}") + // Auth time secret |
| std::string("(00){32}") + // Hash to sign (empty) |
| std::string("07") + // U2F_AUTH_CHECK_ONLY |
| std::string("(FD){113}"); // Versioned Key handle |
| return request_regex; |
| } |
| |
| // Dummy cr50 U2F_GENERATE_RESP. |
| const struct u2f_generate_resp kU2fGenerateResponse = { |
| .pubKey = {.pointFormat = 0xAB, |
| .x = {[0 ... 31] = 0xAB}, |
| .y = {[0 ... 31] = 0xAB}}, |
| .keyHandle = {.origin_seed = {[0 ... 31] = 0xFD}, |
| .hmac = {[0 ... 31] = 0xFD}}}; |
| const struct u2f_generate_versioned_resp kU2fGenerateVersionedResponse = { |
| .pubKey = {.pointFormat = 0xAB, |
| .x = {[0 ... 31] = 0xAB}, |
| .y = {[0 ... 31] = 0xAB}}, |
| .keyHandle = {.header = {.version = 0xFD, |
| .origin_seed = {[0 ... 31] = 0xFD}, |
| .kh_hmac = {[0 ... 31] = 0xFD}}, |
| .authorization_salt = {[0 ... 15] = 0xFD}, |
| .authorization_hmac = {[0 ... 31] = 0xFD}}}; |
| |
| // Dummy cr50 U2F_SIGN_RESP. |
| const struct u2f_sign_resp kU2fSignResponse = {.sig_r = {[0 ... 31] = 0x12}, |
| .sig_s = {[0 ... 31] = 0x34}}; |
| |
| brillo::Blob HexArrayToBlob(const char* array) { |
| brillo::Blob blob; |
| CHECK(base::HexStringToBytes(array, &blob)); |
| return blob; |
| } |
| |
| MATCHER_P(StructMatchesRegex, pattern, "") { |
| std::string arg_hex = base::HexEncode(&arg, sizeof(arg)); |
| if (std::regex_match(arg_hex, std::regex(pattern))) { |
| return true; |
| } |
| *result_listener << arg_hex << " did not match regex: " << pattern; |
| return false; |
| } |
| |
| } // namespace |
| |
| class U2fCommandProcessorGscTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| processor_ = |
| std::make_unique<U2fCommandProcessorGsc>(&mock_tpm_proxy_, [this]() { |
| presence_requested_count_++; |
| task_environment_.FastForwardBy(kRequestPresenceDelay); |
| }); |
| } |
| |
| void TearDown() override { |
| EXPECT_EQ(presence_requested_expected_, presence_requested_count_); |
| } |
| |
| protected: |
| static std::vector<uint8_t> GetCredPubKey() { |
| return U2fCommandProcessorGsc::EncodeCredentialPublicKeyInCBOR( |
| std::vector<uint8_t>(65, 0xAB)); |
| } |
| |
| void CallAndWaitForPresence(std::function<uint32_t()> fn, uint32_t* status) { |
| processor_->CallAndWaitForPresence(fn, status); |
| } |
| |
| bool PresenceRequested() { return presence_requested_count_ > 0; } |
| |
| MakeCredentialResponse::MakeCredentialStatus U2fGenerate( |
| PresenceRequirement presence_requirement, |
| bool uv_compatible, |
| const brillo::Blob* auth_time_secret_hash, |
| std::vector<uint8_t>* credential_id, |
| std::vector<uint8_t>* credential_pubkey) { |
| return processor_->U2fGenerate( |
| GetRpIdHash(), HexArrayToBlob(kCredentialSecret), presence_requirement, |
| uv_compatible, auth_time_secret_hash, credential_id, credential_pubkey); |
| } |
| |
| GetAssertionResponse::GetAssertionStatus U2fSign( |
| const std::vector<uint8_t>& hash_to_sign, |
| const std::vector<uint8_t>& credential_id, |
| PresenceRequirement presence_requirement, |
| std::vector<uint8_t>* signature) { |
| return processor_->U2fSign(GetRpIdHash(), hash_to_sign, credential_id, |
| HexArrayToBlob(kCredentialSecret), |
| presence_requirement, signature); |
| } |
| |
| HasCredentialsResponse::HasCredentialsStatus U2fSignCheckOnly( |
| const std::vector<uint8_t>& rp_id_hash, |
| const std::vector<uint8_t>& credential_id) { |
| return processor_->U2fSignCheckOnly(rp_id_hash, credential_id, |
| HexArrayToBlob(kCredentialSecret)); |
| } |
| |
| MakeCredentialResponse::MakeCredentialStatus G2fAttest( |
| const std::vector<uint8_t>& data, |
| const brillo::SecureBlob& secret, |
| std::vector<uint8_t>* signature_out) { |
| return processor_->G2fAttest(data, secret, U2F_ATTEST_FORMAT_REG_RESP, |
| signature_out); |
| } |
| |
| void InsertAuthTimeSecretHashToCredentialId(std::vector<uint8_t>* input) { |
| auto hash = GetAuthTimeSecretHash(); |
| processor_->InsertAuthTimeSecretHashToCredentialId(&hash, input); |
| } |
| |
| int presence_requested_expected_ = 0; |
| StrictMock<MockTpmVendorCommandProxy> mock_tpm_proxy_; |
| |
| private: |
| int presence_requested_count_ = 0; |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| |
| std::unique_ptr<U2fCommandProcessorGsc> processor_; |
| }; |
| |
| namespace { |
| |
| TEST_F(U2fCommandProcessorGscTest, CallAndWaitForPresenceDirectSuccess) { |
| uint32_t status = kCr50StatusNotAllowed; |
| // If presence is already available, we won't request it. |
| CallAndWaitForPresence([]() { return kCr50StatusSuccess; }, &status); |
| EXPECT_EQ(status, kCr50StatusSuccess); |
| presence_requested_expected_ = 0; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, CallAndWaitForPresenceRequestSuccess) { |
| uint32_t status = kCr50StatusNotAllowed; |
| CallAndWaitForPresence( |
| [this]() { |
| if (PresenceRequested()) |
| return kCr50StatusSuccess; |
| return kCr50StatusNotAllowed; |
| }, |
| &status); |
| EXPECT_EQ(status, kCr50StatusSuccess); |
| presence_requested_expected_ = 1; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, CallAndWaitForPresenceTimeout) { |
| uint32_t status = kCr50StatusSuccess; |
| base::TimeTicks verification_start = base::TimeTicks::Now(); |
| CallAndWaitForPresence([]() { return kCr50StatusNotAllowed; }, &status); |
| EXPECT_GE(base::TimeTicks::Now() - verification_start, kVerificationTimeout); |
| EXPECT_EQ(status, kCr50StatusNotAllowed); |
| presence_requested_expected_ = kMaxRetries; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fGenerateVersionedNoAuthTimeSecretHash) { |
| std::vector<uint8_t> cred_id, cred_pubkey; |
| EXPECT_EQ( |
| U2fGenerate(PresenceRequirement::kPowerButton, |
| /* uv_compatible = */ true, nullptr, &cred_id, &cred_pubkey), |
| MakeCredentialResponse::INTERNAL_ERROR); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fGenerateVersionedSuccessUserPresence) { |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fGenerate( |
| StructMatchesRegex(ExpectedUserPresenceU2fGenerateRequestRegex(true)), |
| Matcher<u2f_generate_versioned_resp*>(_))) |
| .WillOnce(Return(kCr50StatusNotAllowed)) |
| .WillOnce(DoAll(SetArgPointee<1>(kU2fGenerateVersionedResponse), |
| Return(kCr50StatusSuccess))); |
| std::vector<uint8_t> cred_id, cred_pubkey; |
| auto auth_time_secret_hash = GetAuthTimeSecretHash(); |
| EXPECT_EQ(U2fGenerate(PresenceRequirement::kPowerButton, |
| /* uv_compatible = */ true, &auth_time_secret_hash, |
| &cred_id, &cred_pubkey), |
| MakeCredentialResponse::SUCCESS); |
| EXPECT_EQ(cred_id, GetVersionedCredId()); |
| EXPECT_EQ(cred_pubkey, U2fCommandProcessorGscTest::GetCredPubKey()); |
| presence_requested_expected_ = 1; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fGenerateVersionedNoUserPresence) { |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fGenerate( |
| StructMatchesRegex(ExpectedUserPresenceU2fGenerateRequestRegex(true)), |
| Matcher<u2f_generate_versioned_resp*>(_))) |
| .WillRepeatedly(Return(kCr50StatusNotAllowed)); |
| std::vector<uint8_t> cred_id, cred_pubkey; |
| auto auth_time_secret_hash = GetAuthTimeSecretHash(); |
| EXPECT_EQ(U2fGenerate(PresenceRequirement::kPowerButton, |
| /* uv_compatible = */ true, &auth_time_secret_hash, |
| &cred_id, &cred_pubkey), |
| MakeCredentialResponse::VERIFICATION_FAILED); |
| presence_requested_expected_ = kMaxRetries; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fGenerateSuccessUserPresence) { |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fGenerate(StructMatchesRegex( |
| ExpectedUserPresenceU2fGenerateRequestRegex(false)), |
| Matcher<u2f_generate_resp*>(_))) |
| .WillOnce(Return(kCr50StatusNotAllowed)) |
| .WillOnce(DoAll(SetArgPointee<1>(kU2fGenerateResponse), |
| Return(kCr50StatusSuccess))); |
| std::vector<uint8_t> cred_id, cred_pubkey; |
| EXPECT_EQ( |
| U2fGenerate(PresenceRequirement::kPowerButton, |
| /* uv_compatible = */ false, nullptr, &cred_id, &cred_pubkey), |
| MakeCredentialResponse::SUCCESS); |
| EXPECT_EQ(cred_id, GetCredId()); |
| EXPECT_EQ(cred_pubkey, U2fCommandProcessorGscTest::GetCredPubKey()); |
| presence_requested_expected_ = 1; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fGenerateNoUserPresence) { |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fGenerate(StructMatchesRegex( |
| ExpectedUserPresenceU2fGenerateRequestRegex(false)), |
| Matcher<u2f_generate_resp*>(_))) |
| .WillRepeatedly(Return(kCr50StatusNotAllowed)); |
| std::vector<uint8_t> cred_id, cred_pubkey; |
| EXPECT_EQ( |
| U2fGenerate(PresenceRequirement::kPowerButton, |
| /* uv_compatible = */ false, nullptr, &cred_id, &cred_pubkey), |
| MakeCredentialResponse::VERIFICATION_FAILED); |
| presence_requested_expected_ = kMaxRetries; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, |
| U2fGenerateVersionedSuccessUserVerification) { |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fGenerate( |
| StructMatchesRegex(ExpectedUserVerificationU2fGenerateRequestRegex()), |
| Matcher<u2f_generate_versioned_resp*>(_))) |
| // Should succeed at the first time since no presence is required. |
| .WillOnce(DoAll(SetArgPointee<1>(kU2fGenerateVersionedResponse), |
| Return(kCr50StatusSuccess))); |
| std::vector<uint8_t> cred_id, cred_pubkey; |
| auto auth_time_secret_hash = GetAuthTimeSecretHash(); |
| // UI has verified the user so do not require presence. |
| EXPECT_EQ(U2fGenerate(PresenceRequirement::kNone, /* uv_compatible = */ true, |
| &auth_time_secret_hash, &cred_id, &cred_pubkey), |
| MakeCredentialResponse::SUCCESS); |
| EXPECT_EQ(cred_id, GetVersionedCredId()); |
| EXPECT_EQ(cred_pubkey, U2fCommandProcessorGscTest::GetCredPubKey()); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignPresenceNoPresence) { |
| EXPECT_CALL(mock_tpm_proxy_, |
| SendU2fSign(Matcher<const u2f_sign_req&>(StructMatchesRegex( |
| ExpectedDeterministicU2fSignRequestRegex())), |
| _)) |
| .WillRepeatedly(Return(kCr50StatusNotAllowed)); |
| std::vector<uint8_t> signature; |
| EXPECT_EQ(U2fSign(GetHashToSign(), GetCredId(), |
| PresenceRequirement::kPowerButton, &signature), |
| MakeCredentialResponse::VERIFICATION_FAILED); |
| presence_requested_expected_ = kMaxRetries; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignPresenceSuccess) { |
| EXPECT_CALL(mock_tpm_proxy_, |
| SendU2fSign(Matcher<const u2f_sign_req&>(StructMatchesRegex( |
| ExpectedDeterministicU2fSignRequestRegex())), |
| _)) |
| .WillOnce(Return(kCr50StatusNotAllowed)) |
| .WillOnce(DoAll(SetArgPointee<1>(kU2fSignResponse), |
| Return(kCr50StatusSuccess))); |
| std::vector<uint8_t> signature; |
| EXPECT_EQ(U2fSign(GetHashToSign(), GetCredId(), |
| PresenceRequirement::kPowerButton, &signature), |
| MakeCredentialResponse::SUCCESS); |
| EXPECT_EQ(signature, util::SignatureToDerBytes(kU2fSignResponse.sig_r, |
| kU2fSignResponse.sig_s)); |
| presence_requested_expected_ = 1; |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignVersionedSuccess) { |
| brillo::Blob credential_id(GetVersionedCredId()); |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fSign(Matcher<const u2f_sign_versioned_req&>(StructMatchesRegex( |
| ExpectedDeterministicU2fSignVersionedRequestRegex())), |
| _)) |
| .WillOnce(DoAll(SetArgPointee<1>(kU2fSignResponse), |
| Return(kCr50StatusSuccess))); |
| std::vector<uint8_t> signature; |
| EXPECT_EQ(U2fSign(GetHashToSign(), credential_id, PresenceRequirement::kNone, |
| &signature), |
| MakeCredentialResponse::SUCCESS); |
| EXPECT_EQ(signature, util::SignatureToDerBytes(kU2fSignResponse.sig_r, |
| kU2fSignResponse.sig_s)); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignCheckOnlyWrongRpIdHash) { |
| EXPECT_CALL(mock_tpm_proxy_, |
| SendU2fSign(Matcher<const u2f_sign_req&>(StructMatchesRegex( |
| ExpectedU2fSignCheckOnlyRequestRegexWrongRpId())), |
| _)) |
| .WillOnce(Return(kCr50StatusPasswordRequired)); |
| EXPECT_EQ(U2fSignCheckOnly(GetWrongRpIdHash(), GetCredId()), |
| HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignCheckOnlySuccess) { |
| EXPECT_CALL(mock_tpm_proxy_, |
| SendU2fSign(Matcher<const u2f_sign_req&>(StructMatchesRegex( |
| ExpectedU2fSignCheckOnlyRequestRegex())), |
| _)) |
| .WillOnce(Return(kCr50StatusSuccess)); |
| EXPECT_EQ(U2fSignCheckOnly(GetRpIdHash(), GetCredId()), |
| HasCredentialsResponse::SUCCESS); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignCheckOnlyVersionedSuccess) { |
| brillo::Blob credential_id(GetVersionedCredId()); |
| EXPECT_CALL( |
| mock_tpm_proxy_, |
| SendU2fSign(Matcher<const u2f_sign_versioned_req&>(StructMatchesRegex( |
| ExpectedU2fSignCheckOnlyVersionedRequestRegex())), |
| _)) |
| .WillOnce(Return(kCr50StatusSuccess)); |
| EXPECT_EQ(U2fSignCheckOnly(GetRpIdHash(), credential_id), |
| HasCredentialsResponse::SUCCESS); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, U2fSignCheckOnlyWrongLength) { |
| std::vector<uint8_t> wrong_length_key_handle(U2F_V0_KH_SIZE + 1, 0xab); |
| EXPECT_EQ(U2fSignCheckOnly(GetRpIdHash(), wrong_length_key_handle), |
| HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, G2fAttestSuccess) { |
| EXPECT_CALL(mock_tpm_proxy_, SendU2fAttest(_, _)).WillOnce(Return(0)); |
| auto secret = brillo::SecureBlob(GetUserSecret()); |
| brillo::Blob signature_out; |
| EXPECT_EQ(G2fAttest(GetDataToSign(), secret, &signature_out), |
| MakeCredentialResponse::SUCCESS); |
| } |
| |
| TEST_F(U2fCommandProcessorGscTest, InsertAuthTimeSecretHashToCredentialId) { |
| std::vector<uint8_t> input; |
| input.reserve(sizeof(u2f_versioned_key_handle)); |
| input.insert(input.cend(), 65, 0x01); // header |
| input.insert(input.cend(), 16, 0x02); // authorization_salt |
| input.insert(input.cend(), 32, 0x03); // authorization_hmac |
| InsertAuthTimeSecretHashToCredentialId(&input); |
| |
| const std::string expected_output( |
| "(01){65}" // header |
| "(02){16}" // authorization_salt |
| "(FD){32}" // auth_time_secret_hash |
| "(03){32}"); // authorization_hmac |
| EXPECT_THAT(base::HexEncode(input.data(), input.size()), |
| MatchesRegex(expected_output)); |
| } |
| |
| } // namespace |
| |
| } // namespace u2f |