| // Copyright 2026 Google LLC | |
| // | |
| // 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 | |
| // | |
| // https://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. | |
| // | |
| #include "bindings/input_hasher.h" | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_matchers.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/escaping.h" | |
| #include "absl/strings/string_view.h" | |
| #include "crypto/algorithms.h" | |
| #include "crypto/default/hasher.h" | |
| #include "crypto/hash.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "riegeli/bytes/string_reader.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::IsOk; | |
| using ::absl_testing::StatusIs; | |
| using ::testing::Eq; | |
| using ::testing::HasSubstr; | |
| using ::testing::Return; | |
| std::string Sha256Digest(absl::string_view data) { | |
| absl::StatusOr<std::unique_ptr<Hasher>> hasher = CreateHasher("sha256"); | |
| if (!hasher.ok()) return ""; | |
| (*hasher)->Update(data); | |
| return (*hasher)->Digest(); | |
| } | |
| TEST(InputHasherTest, CreateWithValidAlgString) { | |
| EXPECT_THAT(InputHasher::Create("sha256"), IsOk()); | |
| EXPECT_THAT(InputHasher::Create("sha384"), IsOk()); | |
| EXPECT_THAT(InputHasher::Create("sha512"), IsOk()); | |
| } | |
| TEST(InputHasherTest, CreateWithInvalidAlgString) { | |
| EXPECT_THAT(InputHasher::Create("invalid"), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(InputHasherTest, CreateWithValidAlgEnum) { | |
| EXPECT_THAT(InputHasher::Create(HashAlgorithm::kSha256), IsOk()); | |
| EXPECT_THAT(InputHasher::Create(HashAlgorithm::kSha384), IsOk()); | |
| EXPECT_THAT(InputHasher::Create(HashAlgorithm::kSha512), IsOk()); | |
| } | |
| class MockHasher : public Hasher { | |
| public: | |
| MOCK_METHOD(void, Update, (absl::string_view data), (override)); | |
| MOCK_METHOD(std::string, Digest, (), (override)); | |
| }; | |
| TEST(InputHasherTest, CreateWithCustomHasher) { | |
| auto mock_hasher = std::make_unique<MockHasher>(); | |
| EXPECT_CALL(*mock_hasher, Update("test")); | |
| EXPECT_CALL(*mock_hasher, Digest()).WillOnce(Return("mock_digest")); | |
| auto input_hasher = InputHasher::Create(std::move(mock_hasher)); | |
| ABSL_ASSERT_OK(input_hasher); | |
| (*input_hasher)->Update("test"); | |
| EXPECT_THAT((*input_hasher)->Digest(), Eq("mock_digest")); | |
| } | |
| TEST(InputHasherTest, UpdateStringView) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| (*hasher)->Update("hello"); | |
| (*hasher)->Update(" "); | |
| (*hasher)->Update("world"); | |
| EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()), | |
| Eq(absl::BytesToHexString(Sha256Digest("hello world")))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliEmpty) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| std::string data = ""; | |
| riegeli::StringReader reader(data); | |
| EXPECT_THAT((*hasher)->Update(reader, 0, 0), IsOk()); | |
| EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()), | |
| Eq(absl::BytesToHexString(Sha256Digest(data)))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliSmall) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| std::string data = "small data"; | |
| riegeli::StringReader reader(data); | |
| EXPECT_THAT((*hasher)->Update(reader, 0, data.length()), IsOk()); | |
| EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()), | |
| Eq(absl::BytesToHexString(Sha256Digest(data)))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliLarge) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| std::string data(20000, 'a'); // Larger than default chunk size | |
| riegeli::StringReader reader(data); | |
| EXPECT_THAT((*hasher)->Update(reader, 0, data.length()), IsOk()); | |
| EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()), | |
| Eq(absl::BytesToHexString(Sha256Digest(data)))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliWithOffset) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| std::string data = "prefix_and_data"; | |
| riegeli::StringReader reader(data); | |
| EXPECT_THAT((*hasher)->Update(reader, 7, 3), IsOk()); // "and" | |
| EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()), | |
| Eq(absl::BytesToHexString(Sha256Digest("and")))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliMultiple) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| std::string data1 = "first part"; | |
| std::string data2 = " second part"; | |
| riegeli::StringReader reader1(data1); | |
| riegeli::StringReader reader2(data2); | |
| EXPECT_THAT((*hasher)->Update(reader1, 0, data1.length()), IsOk()); | |
| (*hasher)->Update(" middle "); | |
| EXPECT_THAT((*hasher)->Update(reader2, 0, data2.length()), IsOk()); | |
| EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()), | |
| Eq(absl::BytesToHexString( | |
| Sha256Digest("first part middle second part")))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliClosedReaderFails) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| riegeli::StringReader reader("test"); | |
| reader.Close(); | |
| EXPECT_THAT((*hasher)->Update(reader, 0, 4), | |
| StatusIs(absl::StatusCode::kFailedPrecondition, | |
| HasSubstr("Object closed"))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliOffsetOutOfRange) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| riegeli::StringReader reader("test"); | |
| EXPECT_THAT( | |
| (*hasher)->Update(reader, 10, 4), | |
| StatusIs(absl::StatusCode::kInternal, HasSubstr("Failed to seek to 10"))); | |
| } | |
| TEST(InputHasherTest, UpdateRiegeliLengthExceedsSourceFails) { | |
| auto hasher = InputHasher::Create("sha256"); | |
| ABSL_ASSERT_OK(hasher); | |
| riegeli::StringReader reader("test"); | |
| EXPECT_THAT((*hasher)->Update(reader, 0, 100), | |
| StatusIs(absl::StatusCode::kDataLoss, | |
| HasSubstr("Failed to read from source"))); | |
| } | |
| } // namespace | |
| } // namespace credentio |