| // 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 "claim/timestamp_updater.h" | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_macros.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 "constants/labels.h" | |
| #include "cose/sig_structure.h" | |
| #include "cose/timestamp_creator.h" | |
| #include "crypto/crypto_read_handler.h" | |
| #include "crypto/default/default_crypto_read_handler.h" | |
| #include "crypto/default/hasher.h" | |
| #include "crypto/hash.h" | |
| #include "crypto/timestamp_write_handler.h" | |
| #include "gmock/gmock.h" | |
| #include "google/protobuf/timestamp.pb.h" | |
| #include "gtest/gtest.h" | |
| #include "jumbf/test_utils.h" | |
| #include "proto/validation_result.pb.h" | |
| #include "testing/create_test_crypto_write_handler.h" | |
| #include "testing/manifest_store_test_utils.h" | |
| #include "validator/manifest_store_validator_impl.h" | |
| #include "validator/validator_options.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::IsOkAndHolds; | |
| constexpr absl::Time kTestTime = absl::FromUnixSeconds(1741403167); | |
| absl::StatusOr<absl::Time> ValidateAndGetTime( | |
| absl::string_view manifest_store) { | |
| ABSL_ASSIGN_OR_RETURN( | |
| std::unique_ptr<CryptoReadHandler> crypto_read_handler, | |
| CreateDefaultCryptoReadHandler({ | |
| .tsa_trust_anchors_pem = DefaultTestPki().root_ca.cert_pem, | |
| .skip_claim_signer_trust_checks_for_test = true, | |
| })); | |
| ManifestStoreValidatorImpl validator(ValidatorOptions{ | |
| .crypto_read_handler = std::move(crypto_read_handler), | |
| }); | |
| ABSL_ASSIGN_OR_RETURN(std::unique_ptr<PartialValidationResultProto> result, | |
| validator.Validate(manifest_store)); | |
| const google::protobuf::Timestamp& ts = | |
| result->active_manifest().claim().signature_info().timestamping_time(); | |
| return absl::FromUnixSeconds(ts.seconds()) + absl::Nanoseconds(ts.nanos()); | |
| } | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> CreateTimestampHandler( | |
| absl::Clock* clock) { | |
| return CreateTestCryptoWriteHandler({.clock = clock}); | |
| } | |
| absl::StatusOr<std::string> InsertTimestamp(ClaimTimestampUpdater& updater, | |
| absl::string_view manifest_store) { | |
| std::string updated_manifest_store(manifest_store); | |
| absl::Notification done; | |
| absl::Status status; | |
| updater.InsertTimestamp(&updated_manifest_store, AsyncContext(), | |
| [&](absl::Status s) { | |
| status = std::move(s); | |
| done.Notify(); | |
| }); | |
| done.WaitForNotification(); | |
| if (!status.ok()) return status; | |
| return updated_manifest_store; | |
| } | |
| TEST(ClaimTimestampUpdater, InsertTimestampStandardManifest) { | |
| absl::StatusOr<std::string> manifest_store = | |
| CreateSimpleManifestStoreBytes({.disable_timestamping = true}); | |
| ABSL_ASSERT_OK(manifest_store); | |
| absl::SimulatedClock clock; | |
| clock.SetTime(kTestTime); | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> timestamp_handler = | |
| CreateTimestampHandler(&clock); | |
| ABSL_ASSERT_OK(timestamp_handler); | |
| auto timestamp_creator = std::make_unique<TimestampCreator>( | |
| timestamp_handler->get(), &DefaultHasherFactory()); | |
| ClaimTimestampUpdater updater(std::move(timestamp_creator)); | |
| absl::StatusOr<std::string> updated_manifest_store = | |
| InsertTimestamp(updater, *manifest_store); | |
| ABSL_ASSERT_OK(updated_manifest_store); | |
| EXPECT_THAT(ValidateAndGetTime(*updated_manifest_store), | |
| IsOkAndHolds(kTestTime)); | |
| EXPECT_EQ(updated_manifest_store->size(), manifest_store->size()); | |
| } | |
| TEST(ClaimTimestampUpdater, InsertTimestampInUpdateManifest) { | |
| absl::SimulatedClock clock; | |
| clock.SetTime(kTestTime); | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> timestamp_handler = | |
| CreateTimestampHandler(&clock); | |
| ABSL_ASSERT_OK(timestamp_handler); | |
| std::string timestamp_placeholder( | |
| (*timestamp_handler) | |
| ->max_timestamp_byte_size(DefaultHasherFactory().algorithm()), | |
| '\0'); | |
| CoseSign1TaggedStructure cose_sign1{ | |
| .unprotected_header = | |
| {.sig_tst2 = | |
| TstContainer{.tst_tokens = {{.val = timestamp_placeholder}}}}, | |
| .signature = "abc", | |
| }; | |
| std::string claim_signature = jumbf::EncodeSuperBox( | |
| kClaimSignatureUuid, kClaimSignatureLabel, | |
| {jumbf::EncodeCborBox(EncodeCoseSign1TaggedStructure(cose_sign1))}); | |
| std::string manifest = jumbf::EncodeSuperBox( | |
| kUpdateManifestUuid, "update_manifest", {claim_signature}); | |
| std::string manifest_store = jumbf::EncodeSuperBox( | |
| kManifestStoreUuid, kManifestStoreLabel, {manifest}); | |
| auto timestamp_creator = std::make_unique<TimestampCreator>( | |
| timestamp_handler->get(), &DefaultHasherFactory()); | |
| ClaimTimestampUpdater updater(std::move(timestamp_creator)); | |
| absl::StatusOr<std::string> updated_manifest_store = | |
| InsertTimestamp(updater, manifest_store); | |
| ABSL_ASSERT_OK(updated_manifest_store); | |
| EXPECT_EQ(updated_manifest_store->size(), manifest_store.size()); | |
| } | |
| TEST(ClaimTimestampUpdater, TimestampExistsNoOp) { | |
| absl::SimulatedClock clock; | |
| clock.SetTime(kTestTime); | |
| absl::StatusOr<std::string> manifest_store = CreateSimpleManifestStoreBytes( | |
| {.clock = &clock, .disable_timestamping = false}); | |
| ABSL_ASSERT_OK(manifest_store); | |
| clock.AdvanceTime(absl::Seconds(100)); | |
| absl::StatusOr<std::unique_ptr<TimestampWriteHandler>> timestamp_handler = | |
| CreateTimestampHandler(&clock); | |
| ABSL_ASSERT_OK(timestamp_handler); | |
| auto timestamp_creator = std::make_unique<TimestampCreator>( | |
| timestamp_handler->get(), &DefaultHasherFactory()); | |
| ClaimTimestampUpdater updater(std::move(timestamp_creator)); | |
| absl::StatusOr<std::string> updated_manifest_store = | |
| InsertTimestamp(updater, *manifest_store); | |
| ABSL_ASSERT_OK(updated_manifest_store); | |
| EXPECT_EQ(*updated_manifest_store, *manifest_store); | |
| } | |
| } // namespace | |
| } // namespace credentio |