blob: 2038bb4b76cd7430df04e3ecaa6c7d23ce017e6e [file]
// 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/crypto_testing.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/log/absl_check.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "openssl/asn1.h"
#include "openssl/base.h"
#include "openssl/bio.h"
#include "openssl/bn.h"
#include "openssl/digest.h"
#include "openssl/ec_key.h"
#include "openssl/evp.h"
#include "openssl/mem.h"
#include "openssl/nid.h"
#include "openssl/obj.h"
#include "openssl/pem.h"
#include "openssl/rsa.h"
#include "openssl/stack.h"
#include "openssl/x509.h"
namespace credentio {
bssl::UniquePtr<EVP_PKEY> CreateTestEcKey(int nid) {
bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(nid));
ABSL_CHECK_NE(key, nullptr);
ABSL_CHECK(EC_KEY_generate_key(key.get()));
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
ABSL_CHECK_NE(pkey, nullptr);
ABSL_CHECK(EVP_PKEY_set1_EC_KEY(pkey.get(), key.get()));
return pkey;
}
bssl::UniquePtr<EVP_PKEY> CreateTestRsaKey(size_t num_bits) {
bssl::UniquePtr<BIGNUM> rsa_f4(BN_new());
ABSL_CHECK_NE(rsa_f4, nullptr);
ABSL_CHECK(BN_set_word(rsa_f4.get(), RSA_F4));
bssl::UniquePtr<RSA> rsa(RSA_new());
ABSL_CHECK_NE(rsa, nullptr);
ABSL_CHECK(RSA_generate_key_ex(rsa.get(), num_bits, rsa_f4.get(), nullptr));
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
ABSL_CHECK_NE(pkey, nullptr);
ABSL_CHECK(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
return pkey;
}
bssl::UniquePtr<EVP_PKEY> CreateTestEdKey() {
bssl::UniquePtr<EVP_PKEY_CTX> ctx(
EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr));
ABSL_CHECK_NE(ctx, nullptr);
ABSL_CHECK(EVP_PKEY_keygen_init(ctx.get()));
EVP_PKEY* pkey = nullptr;
ABSL_CHECK(EVP_PKEY_keygen(ctx.get(), &pkey));
return bssl::UniquePtr<EVP_PKEY>(pkey);
}
bssl::UniquePtr<X509> CreateTestCertificate(
const TestCertificateOptions& options) {
// Options validation.
ABSL_CHECK(!options.subject_common_name.empty());
ABSL_CHECK(!options.issuer_common_name.empty());
ABSL_CHECK(options.signing_key != nullptr);
ABSL_CHECK(options.private_key != nullptr);
// Create the X509 object.
bssl::UniquePtr<X509> x509(X509_new());
ABSL_CHECK(X509_set_version(x509.get(), 2));
// Set the not_before/not_after fields.
bssl::UniquePtr<ASN1_TIME> not_before(ASN1_TIME_new());
ABSL_CHECK(ASN1_TIME_set_posix(not_before.get(),
absl::ToUnixSeconds(options.not_before)));
ABSL_CHECK(X509_set1_notBefore(x509.get(), not_before.get()));
bssl::UniquePtr<ASN1_TIME> not_after(ASN1_TIME_new());
ABSL_CHECK(ASN1_TIME_set_posix(not_after.get(),
absl::ToUnixSeconds(options.not_after)));
ABSL_CHECK(X509_set1_notAfter(x509.get(), not_after.get()));
// Set the subject DN.
bssl::UniquePtr<X509_NAME> subject_name(X509_NAME_new());
ABSL_CHECK(X509_NAME_add_entry_by_txt(
subject_name.get(), /*field=*/"CN", MBSTRING_ASC,
reinterpret_cast<const unsigned char*>(
options.subject_common_name.c_str()),
/*len=*/-1, /*loc=*/-1,
/*set=*/0));
ABSL_CHECK(X509_NAME_add_entry_by_txt(
subject_name.get(), /*field=*/"O", MBSTRING_ASC,
reinterpret_cast<const unsigned char*>("Test Org"),
/*len=*/-1, /*loc=*/-1,
/*set=*/0));
ABSL_CHECK(X509_NAME_add_entry_by_txt(
subject_name.get(), /*field=*/"OU", MBSTRING_ASC,
reinterpret_cast<const unsigned char*>("Test Unit"),
/*len=*/-1, /*loc=*/-1,
/*set=*/0));
ABSL_CHECK(X509_set_subject_name(x509.get(), subject_name.get()));
// Set the issuer DN.
bssl::UniquePtr<X509_NAME> issuer_name(X509_NAME_new());
ABSL_CHECK(X509_NAME_add_entry_by_txt(issuer_name.get(), /*field=*/"CN",
MBSTRING_ASC,
reinterpret_cast<const unsigned char*>(
options.issuer_common_name.c_str()),
/*len=*/-1, /*loc=*/-1,
/*set=*/0));
ABSL_CHECK(X509_NAME_add_entry_by_txt(
issuer_name.get(), /*field=*/"O", MBSTRING_ASC,
reinterpret_cast<const unsigned char*>("Test Org"),
/*len=*/-1, /*loc=*/-1,
/*set=*/0));
ABSL_CHECK(X509_NAME_add_entry_by_txt(
issuer_name.get(), /*field=*/"OU", MBSTRING_ASC,
reinterpret_cast<const unsigned char*>("Test Unit"),
/*len=*/-1, /*loc=*/-1,
/*set=*/0));
ABSL_CHECK(X509_set_issuer_name(x509.get(), issuer_name.get()));
if (options.serial_number.has_value()) {
ABSL_CHECK(ASN1_INTEGER_set_int64(X509_get_serialNumber(x509.get()),
*options.serial_number));
}
if (options.is_ca.has_value()) {
bssl::UniquePtr<BASIC_CONSTRAINTS> basic_constraints(
BASIC_CONSTRAINTS_new());
basic_constraints->ca = *options.is_ca ? 1 : 0;
ABSL_CHECK(X509_add1_ext_i2d(x509.get(), NID_basic_constraints,
basic_constraints.get(), /*crit=*/1,
/*flags=*/0));
bssl::UniquePtr<ASN1_BIT_STRING> key_usage(ASN1_BIT_STRING_new());
if (*options.is_ca) {
ABSL_CHECK(
ASN1_BIT_STRING_set_bit(key_usage.get(), /*n=*/5, /*value=*/1));
ABSL_CHECK(
ASN1_BIT_STRING_set_bit(key_usage.get(), /*n=*/6, /*value=*/1));
} else {
ABSL_CHECK(
ASN1_BIT_STRING_set_bit(key_usage.get(), /*n=*/0, /*value=*/1));
}
ABSL_CHECK(X509_add1_ext_i2d(x509.get(), NID_key_usage, key_usage.get(),
/*crit=*/1, /*flags=*/0));
}
if (!options.extended_key_usage_oids.empty()) {
bssl::UniquePtr<EXTENDED_KEY_USAGE> eku(EXTENDED_KEY_USAGE_new());
for (const std::string& oid : options.extended_key_usage_oids) {
bssl::UniquePtr<ASN1_OBJECT> obj(
OBJ_txt2obj(oid.c_str(), /*dont_search_names=*/1));
ABSL_CHECK(obj != nullptr);
ABSL_CHECK(bssl::PushToStack(eku.get(), std::move(obj)));
}
ABSL_CHECK(X509_add1_ext_i2d(x509.get(), NID_ext_key_usage, eku.get(),
options.extended_key_usage_critical ? 1 : 0,
/*flags=*/0));
}
// Set the public key and sign the certificate.
ABSL_CHECK(X509_set_pubkey(x509.get(), options.private_key));
ABSL_CHECK(X509_sign(x509.get(), options.signing_key, EVP_sha256()));
return x509;
}
std::string KeyToPem(EVP_PKEY* key) {
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
ABSL_CHECK(bio != nullptr);
ABSL_CHECK(PEM_write_bio_PrivateKey(bio.get(), key, /*enc=*/nullptr,
/*pass=*/nullptr, /*pass_len=*/0,
/*cb=*/nullptr, /*userdata=*/nullptr));
const uint8_t* data = nullptr;
size_t len = 0;
ABSL_CHECK(BIO_mem_contents(bio.get(), &data, &len));
return std::string(reinterpret_cast<const char*>(data), len);
}
std::string CertToPem(X509* cert) {
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
ABSL_CHECK(bio != nullptr);
ABSL_CHECK(PEM_write_bio_X509(bio.get(), cert));
const uint8_t* data = nullptr;
size_t len = 0;
ABSL_CHECK(BIO_mem_contents(bio.get(), &data, &len));
return std::string(reinterpret_cast<const char*>(data), len);
}
std::string CertToDer(X509* cert) {
int der_len = i2d_X509(cert, nullptr);
ABSL_CHECK_GT(der_len, 0);
std::string der(der_len, '\0');
unsigned char* p = reinterpret_cast<unsigned char*>(&der[0]);
ABSL_CHECK_EQ(i2d_X509(cert, &p), der_len);
return der;
}
TestPki CreateTestPki() {
constexpr absl::string_view kRootCaCommonName = "Test Root CA";
bssl::UniquePtr<EVP_PKEY> ca_key = CreateTestEcKey(NID_X9_62_prime256v1);
bssl::UniquePtr<X509> ca_cert = CreateTestCertificate({
.subject_common_name = std::string(kRootCaCommonName),
.issuer_common_name = std::string(kRootCaCommonName),
.signing_key = ca_key.get(),
.private_key = ca_key.get(),
.serial_number = 1,
.is_ca = true,
});
bssl::UniquePtr<EVP_PKEY> claim_signer_key =
CreateTestEcKey(NID_X9_62_prime256v1);
bssl::UniquePtr<X509> claim_signer_cert = CreateTestCertificate({
.subject_common_name = "Test Claim Signer",
.issuer_common_name = std::string(kRootCaCommonName),
.signing_key = ca_key.get(),
.private_key = claim_signer_key.get(),
.serial_number = 2,
.is_ca = false,
.extended_key_usage_oids = {std::string(kC2paClaimSigningEkuOid)},
});
bssl::UniquePtr<EVP_PKEY> tsa_key = CreateTestEcKey(NID_X9_62_prime256v1);
bssl::UniquePtr<X509> tsa_cert = CreateTestCertificate({
.subject_common_name = "Test TSA",
.issuer_common_name = std::string(kRootCaCommonName),
.signing_key = ca_key.get(),
.private_key = tsa_key.get(),
.serial_number = 3,
.is_ca = false,
// The time stamping EKU must be marked critical per RFC 3161.
.extended_key_usage_oids = {std::string(kTimeStampingEkuOid)},
.extended_key_usage_critical = true,
});
return TestPki{
.root_ca =
{
.key_pem = KeyToPem(ca_key.get()),
.cert_pem = CertToPem(ca_cert.get()),
.cert_der = CertToDer(ca_cert.get()),
},
.claim_signer =
{
.key_pem = KeyToPem(claim_signer_key.get()),
.cert_pem = CertToPem(claim_signer_cert.get()),
.cert_der = CertToDer(claim_signer_cert.get()),
},
.tsa =
{
.key_pem = KeyToPem(tsa_key.get()),
.cert_pem = CertToPem(tsa_cert.get()),
.cert_der = CertToDer(tsa_cert.get()),
},
};
}
} // namespace credentio