| // 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 "cose/timestamp_updater.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/string_view.h" | |
| #include "absl/synchronization/notification.h" | |
| #include "absl/time/clock_interface.h" | |
| #include "absl/time/simulated_clock.h" | |
| #include "absl/time/time.h" | |
| #include "async/async_context.h" | |
| #include "cose/sig_structure.h" | |
| #include "cose/simple_cms_parser.h" | |
| #include "cose/timestamp_creator.h" | |
| #include "crypto/default/hasher.h" | |
| #include "crypto/timestamp_write_handler.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "testing/create_test_crypto_write_handler.h" | |
| #include "tsp/parsed_timestamp_token.h" | |
| #include "tsp/timestamp_verifier.h" | |
| #include "tsp/verified_timestamp.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::StatusIs; | |
| using ::testing::HasSubstr; | |
| using ::testing::IsEmpty; | |
| using ::testing::Not; | |
| using ::testing::SizeIs; | |
| constexpr absl::Time kTestTime = absl::FromUnixSeconds(1741403167); | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> CreateTimestampHandler( | |
| absl::Clock* clock) { | |
| return CreateTestCryptoWriteHandler({.clock = clock}); | |
| } | |
| absl::StatusOr<std::string> UpdateTimestamp( | |
| TimestampUpdater& updater, absl::string_view encoded_cose_sign1) { | |
| std::string updated_encoded_cose_sign1(encoded_cose_sign1); | |
| absl::Notification done; | |
| absl::Status status; | |
| updater.UpdateTimestamp(&updated_encoded_cose_sign1, AsyncContext(), | |
| [&](absl::Status s) { | |
| status = std::move(s); | |
| done.Notify(); | |
| }); | |
| done.WaitForNotification(); | |
| if (!status.ok()) return status; | |
| return updated_encoded_cose_sign1; | |
| } | |
| TEST(TimestampUpdater, UpdateTimestamp) { | |
| // Create a timestamp creator. | |
| absl::SimulatedClock clock; | |
| clock.SetTime(kTestTime); | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> timestamp_handler = | |
| CreateTimestampHandler(&clock); | |
| ABSL_ASSERT_OK(timestamp_handler); | |
| TimestampCreator timestamp_creator(timestamp_handler->get(), | |
| &DefaultHasherFactory()); | |
| TimestampUpdater timestamp_updater(×tamp_creator); | |
| // Create a timestamp placeholder. | |
| CoseSign1TaggedStructure to_be_updated{ | |
| .protected_header = "def", | |
| .unprotected_header = | |
| { | |
| .sig_tst2 = TstContainer{.tst_tokens = {{.val = std::string( | |
| 1200, '\0')}}}, | |
| .pad = std::string(25, '\0'), | |
| }, | |
| .signature = "abc", | |
| }; | |
| std::string encoded_cose_sign1 = | |
| EncodeCoseSign1TaggedStructure(to_be_updated); | |
| // Update the timestamp. | |
| absl::StatusOr<std::string> updated_cose_sign1 = | |
| UpdateTimestamp(timestamp_updater, encoded_cose_sign1); | |
| ABSL_ASSERT_OK(updated_cose_sign1); | |
| EXPECT_EQ(updated_cose_sign1->size(), encoded_cose_sign1.size()); | |
| // Verify the updated timestamp. | |
| TimestampVerifier verifier(/*tsa_roots=*/nullptr); | |
| SimpleCmsParser parser; | |
| absl::StatusOr<CoseSign1TaggedStructure> decoded = | |
| DecodeCoseSign1TaggedStructure(*updated_cose_sign1); | |
| ABSL_ASSERT_OK(decoded); | |
| ASSERT_THAT(decoded->unprotected_header.sig_tst2->tst_tokens, SizeIs(1)); | |
| ASSERT_THAT(decoded->unprotected_header.sig_tst2->tst_tokens[0].val, | |
| Not(IsEmpty())); | |
| absl::StatusOr<std::unique_ptr<ParsedTimestampToken>> parsed_token = | |
| parser.ParseTimestampToken( | |
| decoded->unprotected_header.sig_tst2->tst_tokens[0].val); | |
| ABSL_ASSERT_OK(parsed_token); | |
| absl::StatusOr<VerifiedTimestamp> updated_verified_timestamp = | |
| verifier.VerifyTimestampToken(**parsed_token); | |
| ABSL_ASSERT_OK(updated_verified_timestamp); | |
| EXPECT_EQ(updated_verified_timestamp->asserted_time(), kTestTime); | |
| } | |
| TEST(TimestampUpdater, UpdateTimestampNotEnoughSpace) { | |
| // Create a timestamp creator. | |
| absl::SimulatedClock clock; | |
| clock.SetTime(kTestTime); | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> timestamp_handler = | |
| CreateTimestampHandler(&clock); | |
| ABSL_ASSERT_OK(timestamp_handler); | |
| TimestampCreator timestamp_creator(timestamp_handler->get(), | |
| &DefaultHasherFactory()); | |
| TimestampUpdater timestamp_updater(×tamp_creator); | |
| // Create a timestamp. | |
| CoseSign1TaggedStructure cose_sign1; | |
| EXPECT_THAT(UpdateTimestamp(timestamp_updater, | |
| EncodeCoseSign1TaggedStructure(cose_sign1)), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Not enough space"))); | |
| } | |
| } // namespace | |
| } // namespace credentio |