| // 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 "testing/create_test_crypto_write_handler.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/simulated_clock.h" | |
| #include "absl/time/time.h" | |
| #include "cose/simple_cms_parser.h" | |
| #include "crypto/algorithms.h" | |
| #include "crypto/crypto_write_handler.h" | |
| #include "crypto/default/trust_store.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "openssl/pki/verify.h" | |
| #include "testing/local_key_crypto_write_handler.h" | |
| #include "testing/test_validation_tracker.h" | |
| #include "tsp/parsed_timestamp_token.h" | |
| #include "tsp/timestamp_verifier.h" | |
| #include "tsp/verified_timestamp.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::IsOk; | |
| using ::absl_testing::StatusIs; | |
| using ::testing::ElementsAreArray; | |
| using ::testing::HasSubstr; | |
| using ::testing::IsEmpty; | |
| using ::testing::Not; | |
| absl::StatusOr<std::string> GetTimestamp(CryptoWriteHandler& handler, | |
| HashAlgorithm hash_algorithm, | |
| absl::string_view hashed_message) { | |
| absl::Notification done; | |
| absl::StatusOr<std::string> result; | |
| handler.CreateTimestamp(hash_algorithm, std::string(hashed_message), | |
| /*options=*/{}, | |
| [&result, &done](absl::StatusOr<std::string> res) { | |
| result = std::move(res); | |
| done.Notify(); | |
| }); | |
| done.WaitForNotification(); | |
| return result; | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, CreateTimestamp) { | |
| absl::SimulatedClock clock; | |
| absl::Time now = absl::FromUnixSeconds(1739221301); // within validity period | |
| clock.SetTime(now); | |
| std::string message_imprint = "message_imprint"; | |
| absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> writer = | |
| CreateTestCryptoWriteHandler({.clock = &clock}); | |
| ABSL_ASSERT_OK(writer); | |
| absl::StatusOr<std::string> created_timestamp = | |
| GetTimestamp(**writer, HashAlgorithm::kSha384, message_imprint); | |
| ABSL_ASSERT_OK(created_timestamp); | |
| absl::StatusOr<std::unique_ptr<bssl::VerifyTrustStore>> trust_store = | |
| LoadTrustStore(DefaultTestPki().root_ca.cert_pem); | |
| ABSL_ASSERT_OK(trust_store); | |
| TimestampVerifier verifier(trust_store->get()); | |
| SimpleCmsParser parser; | |
| TestValidationTracker t; | |
| absl::StatusOr<std::unique_ptr<ParsedTimestampToken>> parsed_token = | |
| parser.ParseTimestampToken(*created_timestamp); | |
| ABSL_ASSERT_OK(parsed_token); | |
| absl::StatusOr<VerifiedTimestamp> verified_timestamp = | |
| verifier.VerifyTimestampToken(**parsed_token); | |
| ABSL_ASSERT_OK(verified_timestamp); | |
| ASSERT_THAT(t.GetFailureStatuses(), IsEmpty()); | |
| EXPECT_EQ(verified_timestamp->message_imprint_hash(), message_imprint); | |
| EXPECT_EQ(verified_timestamp->asserted_time(), now); | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, HasCertChainAndOcspResponse) { | |
| absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> writer = | |
| CreateTestCryptoWriteHandler({}); | |
| ABSL_ASSERT_OK(writer); | |
| EXPECT_THAT((*writer)->GetSigner().certificate_chain(), Not(IsEmpty())); | |
| EXPECT_THAT((*writer)->GetSigner().ocsp_responses(), Not(IsEmpty())); | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, | |
| UsesConfiguredCertChainAndOcspResponses) { | |
| LocalKeyClaimSignerCredentials credentials = TestClaimSignerCredentials(); | |
| credentials.ocsp_responses.clear(); | |
| absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> writer = | |
| CreateTestCryptoWriteHandler({.claim_signer = std::move(credentials)}); | |
| ABSL_ASSERT_OK(writer); | |
| EXPECT_THAT((*writer)->GetSigner().certificate_chain(), | |
| ElementsAreArray(TestClaimSignerCredentials().certificate_chain)); | |
| EXPECT_THAT((*writer)->GetSigner().ocsp_responses(), IsEmpty()); | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, UsesConfiguredSigningKey) { | |
| // Any other valid EC P-256 key will do; reuse the TSA's. | |
| LocalKeyClaimSignerCredentials credentials = TestClaimSignerCredentials(); | |
| credentials.key_pem = TestTsaCredentials().key_pem; | |
| absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> writer = | |
| CreateTestCryptoWriteHandler({.claim_signer = std::move(credentials)}); | |
| ABSL_ASSERT_OK(writer); | |
| absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> default_writer = | |
| CreateTestCryptoWriteHandler({}); | |
| ABSL_ASSERT_OK(default_writer); | |
| absl::StatusOr<SignResponse> response = | |
| (*writer)->GetSigner().Sign("to_be_signed"); | |
| ABSL_ASSERT_OK(response); | |
| absl::StatusOr<SignResponse> default_response = | |
| (*default_writer)->GetSigner().Sign("to_be_signed"); | |
| ABSL_ASSERT_OK(default_response); | |
| EXPECT_NE(response->signature, default_response->signature); | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, UsesConfiguredTsa) { | |
| absl::SimulatedClock clock; | |
| clock.SetTime(absl::FromUnixSeconds(1739221301)); // within validity period | |
| absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> writer = | |
| CreateTestCryptoWriteHandler( | |
| {.clock = &clock, .tsa = TestTsaCredentials()}); | |
| ABSL_ASSERT_OK(writer); | |
| EXPECT_THAT(GetTimestamp(**writer, HashAlgorithm::kSha384, "message_imprint"), | |
| IsOk()); | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, | |
| RejectsIncompleteClaimSignerCredentials) { | |
| EXPECT_THAT(CreateTestCryptoWriteHandler({.claim_signer = {{}}}), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Claim signer key"))); | |
| LocalKeyClaimSignerCredentials no_chain = TestClaimSignerCredentials(); | |
| no_chain.certificate_chain.clear(); | |
| EXPECT_THAT( | |
| CreateTestCryptoWriteHandler({.claim_signer = std::move(no_chain)}), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Claim signer certificate chain"))); | |
| } | |
| TEST(CreateTestCryptoWriteHandlerTest, RejectsIncompleteTsaCredentials) { | |
| EXPECT_THAT( | |
| CreateTestCryptoWriteHandler({.tsa = {{}}}), | |
| StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("TSA key"))); | |
| LocalKeyTsaCredentials no_cert = TestTsaCredentials(); | |
| no_cert.cert_pem.clear(); | |
| EXPECT_THAT(CreateTestCryptoWriteHandler({.tsa = std::move(no_cert)}), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("TSA certificate"))); | |
| } | |
| } // namespace | |
| } // namespace credentio |