blob: 0cd976e33f079f0121df4c1b6bd873c160f7aeb3 [file] [edit]
// 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 "crypto/default/x509_certificate.h"
#include <string>
#include <utility>
#include <vector>
#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "crypto/algorithms.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "openssl/asn1.h"
#include "openssl/base.h"
#include "openssl/mem.h"
#include "openssl/obj.h"
#include "openssl/x509.h"
#include "proto/common.pb.h"
#include "tink/signature/signature_pem_keyset_reader.h"
namespace credentio {
namespace {
using ::absl_testing::IsOk;
using ::absl_testing::IsOkAndHolds;
using ::absl_testing::StatusIs;
using ::testing::HasSubstr;
using ::testing::IsEmpty;
using ::testing::NotNull;
using ::testing::SizeIs;
constexpr absl::string_view kDerRsaBase64 =
"MIIGDDCCA/SgAwIBAgIQf/"
"J0H9HG3kO1lHqOZXCenjANBgkqhkiG9w0BAQsFADB1MQswCQYDVQQGEwJVUzEjMCEGA1UEChMa"
"QWRvYmUgU3lzdGVtcyBJbmNvcnBvcmF0ZWQxHTAbBgNVBAsTFEFkb2JlIFRydXN0IFNlcnZpY2"
"VzMSIwIAYDVQQDExlBZG9iZSBQcm9kdWN0IFNlcnZpY2VzIEczMB4XDTI0MDExMTAwMDAwMFoX"
"DTI1MDExMDIzNTk1OVowfzERMA8GA1UEAwwIY2FpLXByb2QxEzARBgNVBAoMCkFkb2JlIEluYy"
"4xETAPBgNVBAcMCFNhbiBKb3NlMRMwEQYDVQQIDApDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzEg"
"MB4GCSqGSIb3DQEJARYRY2FpLW9wc0BhZG9iZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDw"
"AwggEKAoIBAQDv0wCnfYY9lkHDsykrTG7FYlnYHBcyualuD5u9ULjzv+"
"FsTWOLg8Nhrz9RTxI9EeBbu3Wm7f4vqPsQYfxkEp0wIa6DVmEOZhbAeRfbhybWQ/"
"D4Wvvck4mEZR+QZmTpIn7HX61gbtMFHlJUxkYBctIoAt9TBAFbz9d5Ig2WG0bsH39b6/"
"8USXnIIk4FB6dtBRT1xeEwSDtwOd1YBpXXNBJmRWHXNJZZ85of2tBTesg/"
"kMvTrOJJxYDZ97U416wcvbVomlees+NXUY3/"
"ZB4xH+"
"NsZils7m1bL2v2hSnHiLoqZeUKklbHUmT4ZpjrZCdtuIsWm5j2PPXKr7HKUuCbZSXBAgMBAAWj"
"ggGMMIIBiDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/"
"wQEAwIHgDAeBgNVHSUEFzAVBgkqhkiG9y8BAQwGCCsGAQUFBwMEMIGOBgNVHSAEgYYwgYMwgYA"
"GCSqGSIb3LwECAzBzMHEGCCsGAQUFBwICMGUMY1lvdSBhcmUgbm90IHBlcm1pdHRlZCB0byB1c"
"2UgdGhpcyBMaWNlbnNlIENlcnRpZmljYXRlIGV4Y2VwdCBhcyBwZXJtaXR0ZWQgYnkgdGhlIGx"
"pY2Vuc2UgYWdyZWVtZW50LjBdBgNVHR8EVjBUMFKgUKBOhkxodHRwOi8vcGtpLWNybC5zeW1hd"
"XRoLmNvbS9jYV83YTVjM2EwYzczMTE3NDA2YWRkMTkzMTJiYzFiYzIzZi9MYXRlc3RDUkwuY3J"
"sMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1dGguY"
"29tMB8GA1UdIwQYMBaAFFcpejJNzP7kNU7AHyRzznNTq99qMA0GCSqGSIb3DQEBCwUAA4ICAQA"
"hY9X822Lvow9+KlSdkAI/95WsS/"
"n1Y5se5YJkePUHLxsablSaNOA9BiVfJOPxcT+JjbumGCseDSmIOxYPO7mzwHpMqRoyZS+"
"C2T5SC6TJtt920ELoq+"
"0aDLjRw7gOQRSg8OK0PFPWKBtCp0qiAChUqoMRWqcX4IVBDO1sZxmEUj1cpOwJDB/"
"2HDsuEkKjqMA0EgAE3JC1LBYik95IQmE9WBYvIp6mBDEkyovqyBhnEar2kJJCfaBLylp/"
"JAUsHJnAQEP8PHK1FlkvpvdleoX59lmwT1+wHGlxYWK/"
"ykW+AFA60LOVJt8byjtuAE8lebuYADyPe3eEMR4THQbHwH/"
"1rz2nNN5tZWsPdIR7S5mX0RtUL74XgSixEkV1jDO4/"
"WIk7V3kjdXEZr0WtWr8VgEY5G+"
"8NpPBqd2LMLBp7jejMIj3tA8RS1L6MnO3PWRqkdwGwsCHsvGcTzauWarWji990WxBTPscSk6Q8"
"WTIqiHI9OfUUiriWQkpfeMEDo1r+zHJRmj26+"
"zgID7QB41xNeV3K6BFQX6ayl1swuIh26KB0Mu74sndRzG+8tMTffvjBoO9QPW/"
"OtdWyYmsLGT0Kcj9EaaIg1JmgNtRBwo9f89eYJSYwenQ+GddpqdnBIjk63Q+K3gCdlpTs++"
"tJCZ7DX9yndmYlx94HgUZFPcNimun6Q==";
constexpr absl::string_view kDerEcdsaBase64 =
"MIIChzCCAi6gAwIBAgIUcCTmJHYF8dZfG0d1UdT6/"
"LXtkeYwCgYIKoZIzj0EAwIwgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBww"
"JU29tZXdoZXJlMScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXB"
"gNVBAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAeFw0yMjA"
"2MTAxODQ2NDBaFw0zMDA4MjYxODQ2NDBaMIGAMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExE"
"jAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBUZXN0IFNpZ25pbmcgQ2VydDEZMBc"
"GA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIGA1UEAwwLQzJQQSBTaWduZXIwWTATBgcqhkjOP"
"QIBBggqhkjOPQMBBwNCAAQPaL6RkAkYkKU4+IryBSYxJM3h77sFiMrbvbI8fG7w2Bbl9otNG/"
"cch3DAw5rGAPV7NWkyl3QGuV/wt0MrAPDoo3gwdjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/"
"wQMMAoGCCsGAQUFBwMEMA4GA1UdDwEB/"
"wQEAwIGwDAdBgNVHQ4EFgQUFznP0y83joiNOCedQkxTtAMyNcowHwYDVR0jBBgwFoAUDnyNcma"
"/osnlAJTvtW6A4rYOL2swCgYIKoZIzj0EAwIDRwAwRAIgOY/2szXjslg/"
"MyJFZ2y7OH8giPYTsvS7UPRP9GI9NgICIDQPMKrELQUJEtipZ0TqvI/"
"4mieoyRCeIiQtyuS0LACz";
// Base64 encoded string of DER bytes of an X.509 certificate signed with
// Ed25519 algorithm, which is generated by following commands:
//
// openssl genpkey -algorithm Ed25519 -outform PEM -out private.pem
// openssl pkey -in private.pem -pubout -outform PEM -out public.pem
// openssl req -x509 -key private.pem -out cert.pem -days 99999
// openssl x509 -in cert.pem -outform DER -out cert.der
// cat cert.der | base64
//
// This certificate is self-signed on 2024-09-18 and it will expire on
// 2298-07-03. So, if this code is still around in 2298 and you wonder why your
// test that relies on this certificate being valid fails, please update your
// test certificate.
constexpr absl::string_view kDerEdBase64 =
"MIIBoTCCAVOgAwIBAgIUZnrWsn3k+"
"VyZMdOt2jHxA4Z6QNIwBQYDK2VwMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXR"
"lMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjQwOTE4MTkxMTMyWhgPM"
"jI5ODA3MDMxOTExMzJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwY"
"DVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwKjAFBgMrZXADIQAYkLGZHs4BuUq11RLkR"
"V3lX6U1CMxpkKMI3dFBkQjDFKNTMFEwHQYDVR0OBBYEFPl9A331I54qf4vhLTs5KZqsJznSMB8"
"GA1UdIwQYMBaAFPl9A331I54qf4vhLTs5KZqsJznSMA8GA1UdEwEB/"
"wQFMAMBAf8wBQYDK2VwA0EAxWKyUVcH4hoW/"
"mS66Ot4UGqaEKUANiyT6khvcUZLUpM2m7Cmh7ZwmjBz7b6X9nqHwd+8WnSMUZHKL5bLmZmsDw="
"=";
// Base64 encoded string of the signature obtained by running the following
// commands:
//
// openssl pkeyutl -sign -inkey private.pem -rawin -in data.txt -out sig.bin
// cat sig.bin | base64
//
// where data.txt contains the text "test data" (without a new line).
constexpr absl::string_view kEdSignatureBase64 =
"wB5+rUKWp47TZ8VPpUlA32IO033ggYB63qXLPyb8bDEGJyD8TBM/"
"psRxl5Z8EsglhzdmvB29VL62xp4+3On/Bg==";
std::string Base64Unescape(absl::string_view base64) {
std::string bytes;
CHECK(absl::Base64Unescape(base64, &bytes));
return bytes;
}
TEST(X509CertificateTest, GetPemKeyParamsForRsaKey) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
std::vector<crypto::tink::PemKeyParams> params;
auto params_ps256 = (*x509)->GetPemKeyParams(SigningAlgorithm::kPs256);
ASSERT_THAT(params_ps256.status(), IsOk());
params = *params_ps256;
ASSERT_THAT(params, SizeIs(1));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_RSA);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::RSASSA_PSS);
EXPECT_EQ(params[0].key_size_in_bits, 2048);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA256);
auto params_ps384 = (*x509)->GetPemKeyParams(SigningAlgorithm::kPs384);
ASSERT_THAT(params_ps384.status(), IsOk());
params = *params_ps384;
ASSERT_THAT(params, SizeIs(1));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_RSA);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::RSASSA_PSS);
EXPECT_EQ(params[0].key_size_in_bits, 2048);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA384);
auto params_ps512 = (*x509)->GetPemKeyParams(SigningAlgorithm::kPs512);
ASSERT_THAT(params_ps512.status(), IsOk());
params = *params_ps512;
ASSERT_THAT(params, SizeIs(1));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_RSA);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::RSASSA_PSS);
EXPECT_EQ(params[0].key_size_in_bits, 2048);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA512);
EXPECT_THAT((*x509)->GetPemKeyParams(SigningAlgorithm::kEs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Wrong key type")));
EXPECT_THAT((*x509)->GetPemKeyParams(SigningAlgorithm::kEdDsa),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Wrong key type")));
}
TEST(X509CertificateTest, GetPemKeyParamsForEcdsaKey) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerEcdsaBase64));
ASSERT_THAT(x509.status(), IsOk());
std::vector<crypto::tink::PemKeyParams> params;
auto params_es256 = (*x509)->GetPemKeyParams(SigningAlgorithm::kEs256);
ASSERT_THAT(params_es256.status(), IsOk());
params = *params_es256;
ASSERT_THAT(params, SizeIs(2));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::ECDSA_IEEE);
EXPECT_EQ(params[0].key_size_in_bits, 256);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA256);
EXPECT_EQ(params[1].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[1].algorithm, crypto::tink::PemAlgorithm::ECDSA_DER);
EXPECT_EQ(params[1].key_size_in_bits, 256);
EXPECT_EQ(params[1].hash_type, google::crypto::tink::HashType::SHA256);
auto params_es384 = (*x509)->GetPemKeyParams(SigningAlgorithm::kEs384);
ASSERT_THAT(params_es384.status(), IsOk());
params = *params_es384;
ASSERT_THAT(params, SizeIs(2));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::ECDSA_IEEE);
EXPECT_EQ(params[0].key_size_in_bits, 256);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA384);
EXPECT_EQ(params[1].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[1].algorithm, crypto::tink::PemAlgorithm::ECDSA_DER);
EXPECT_EQ(params[1].key_size_in_bits, 256);
EXPECT_EQ(params[1].hash_type, google::crypto::tink::HashType::SHA384);
auto params_es512 = (*x509)->GetPemKeyParams(SigningAlgorithm::kEs512);
ASSERT_THAT(params_es512.status(), IsOk());
params = *params_es512;
ASSERT_THAT(params, SizeIs(2));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::ECDSA_IEEE);
EXPECT_EQ(params[0].key_size_in_bits, 256);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA512);
EXPECT_EQ(params[1].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[1].algorithm, crypto::tink::PemAlgorithm::ECDSA_DER);
EXPECT_EQ(params[1].key_size_in_bits, 256);
EXPECT_EQ(params[1].hash_type, google::crypto::tink::HashType::SHA512);
EXPECT_THAT((*x509)->GetPemKeyParams(SigningAlgorithm::kPs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Wrong key type")));
EXPECT_THAT((*x509)->GetPemKeyParams(SigningAlgorithm::kEdDsa),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Wrong key type")));
}
TEST(X509CertificateTest, GetPemKeyParamsForEdKey) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerEdBase64));
ASSERT_THAT(x509.status(), IsOk());
std::vector<crypto::tink::PemKeyParams> params;
auto params_eddsa = (*x509)->GetPemKeyParams(SigningAlgorithm::kEdDsa);
ASSERT_THAT(params_eddsa.status(), IsOk());
params = *params_eddsa;
ASSERT_THAT(params, SizeIs(1));
EXPECT_EQ(params[0].key_type, crypto::tink::PemKeyType::PEM_EC);
EXPECT_EQ(params[0].algorithm, crypto::tink::PemAlgorithm::ED25519);
// BoringSSL will return 253 bits here instead of 256 bits because the lower
// three bits of the private key scalar are cleared.
EXPECT_EQ(params[0].key_size_in_bits, 253);
EXPECT_EQ(params[0].hash_type, google::crypto::tink::HashType::SHA512);
EXPECT_THAT((*x509)->GetPemKeyParams(SigningAlgorithm::kPs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Wrong key type")));
EXPECT_THAT((*x509)->GetPemKeyParams(SigningAlgorithm::kEs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Wrong key type")));
}
TEST(X509CertificateTest, VerifyEdSignedData) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerEdBase64));
ASSERT_THAT(x509.status(), IsOk());
EXPECT_THAT(
(*x509)->VerifySignature(Base64Unescape(kEdSignatureBase64),
"this is a test", SigningAlgorithm::kEdDsa),
IsOk());
}
TEST(X509CertificateTest, VerifySignatureWithRsaKey) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
// The actual signature and data don't matter for covering the lines in
// MaybeConvertPssToRsa, as the key conversion happens before signature
// verification. We expect a failure because the signature is invalid.
EXPECT_THAT((*x509)->VerifySignature("fake_signature", "fake_data",
SigningAlgorithm::kPs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Tink verification failed")));
}
TEST(X509CertificateTest, GetStartTime) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
auto start_time = (*x509)->StartTime();
ASSERT_THAT(start_time.status(), IsOk());
EXPECT_EQ(absl::ToUnixSeconds(*start_time), 1704931200);
}
TEST(X509CertificateTest, GetEndTime) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
auto end_time = (*x509)->EndTime();
ASSERT_THAT(end_time.status(), IsOk());
EXPECT_EQ(absl::ToUnixSeconds(*end_time), 1736553599);
}
TEST(X509CertificateTest, GetSubjectIssuerNullName) {
bssl::UniquePtr<X509> x509(X509_new());
X509Certificate cert(std::move(x509));
EXPECT_THAT(cert.GetSubject(), IsEmpty());
EXPECT_THAT(cert.GetIssuer(), IsEmpty());
}
TEST(X509CertificateTest, GetSerialNumberHexRsa) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
auto serial_hex = (*x509)->GetSerialNumberHex();
ASSERT_THAT(serial_hex.status(), IsOk());
EXPECT_EQ(*serial_hex, "7ff2741fd1c6de43b5947a8e65709e9e");
}
TEST(X509CertificateTest, GetSerialNumberHexEcdsa) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerEcdsaBase64));
ASSERT_THAT(x509.status(), IsOk());
auto serial_hex = (*x509)->GetSerialNumberHex();
ASSERT_THAT(serial_hex.status(), IsOk());
EXPECT_EQ(*serial_hex, "7024e6247605f1d65f1b477551d4fafcb5ed91e6");
}
TEST(X509CertificateTest, GetSerialNumberHexEdDsa) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerEdBase64));
ASSERT_THAT(x509.status(), IsOk());
auto serial_hex = (*x509)->GetSerialNumberHex();
ASSERT_THAT(serial_hex.status(), IsOk());
EXPECT_EQ(*serial_hex, "667ad6b27de4f95c9931d3adda31f103867a40d2");
}
TEST(X509CertificateTest, GetAssuranceLevelNotFound) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
EXPECT_THAT((*x509)->GetAssuranceLevel(),
StatusIs(absl::StatusCode::kNotFound));
}
TEST(X509CertificateTest, GetAssuranceLevelCorrectlyDecodesExtension) {
constexpr absl::string_view kAssuranceLevelValueOid =
"1.3.6.1.4.1.62558.3.20";
constexpr absl::string_view kAssuranceLevelExtensionOid =
"1.3.6.1.4.1.62558.3";
bssl::UniquePtr<X509> x509(X509_new());
ASSERT_NE(x509, nullptr);
bssl::UniquePtr<ASN1_OBJECT> ext_oid(
OBJ_txt2obj(kAssuranceLevelExtensionOid.data(), /*dont_search_names=*/1));
ASSERT_NE(ext_oid, nullptr);
bssl::UniquePtr<ASN1_OBJECT> val_oid(
OBJ_txt2obj(kAssuranceLevelValueOid.data(), /*dont_search_names=*/1));
ASSERT_NE(val_oid, nullptr);
unsigned char* der_data = nullptr;
int der_len = i2d_ASN1_OBJECT(val_oid.get(), &der_data);
ASSERT_GT(der_len, 0);
bssl::UniquePtr<ASN1_OCTET_STRING> octet_str(ASN1_OCTET_STRING_new());
ASSERT_TRUE(ASN1_OCTET_STRING_set(octet_str.get(), der_data, der_len));
OPENSSL_free(der_data);
bssl::UniquePtr<X509_EXTENSION> ext(X509_EXTENSION_create_by_OBJ(
nullptr, ext_oid.get(), /*crit=*/0, octet_str.get()));
ASSERT_NE(ext, nullptr);
ASSERT_TRUE(X509_add_ext(x509.get(), ext.get(), -1));
X509Certificate cert(std::move(x509));
EXPECT_THAT(cert.GetAssuranceLevel(), IsOkAndHolds(kAssuranceLevelValueOid));
}
TEST(X509CertificateTest, GetConformingProductIdNotFound) {
auto x509 = X509Certificate::Create(Base64Unescape(kDerRsaBase64));
ASSERT_THAT(x509.status(), IsOk());
EXPECT_THAT((*x509)->GetConformingProductId(),
StatusIs(absl::StatusCode::kNotFound));
}
TEST(X509CertificateTest, GetConformingProductIdCorrectlyDecodesExtension) {
// Create a cert with the conforming product id extension.
bssl::UniquePtr<X509> x509(X509_new());
bssl::UniquePtr<ASN1_OBJECT> ext_oid(
OBJ_txt2obj("1.3.6.1.4.1.62558.4", /*dont_search_names=*/1));
ASSERT_THAT(ext_oid, NotNull());
bssl::UniquePtr<ASN1_UTF8STRING> val_str(ASN1_UTF8STRING_new());
constexpr absl::string_view kUuid = "c25c0e15-84dc-4cd7-95de-1d9ea4c7f0db";
ASSERT_TRUE(ASN1_STRING_set(val_str.get(), kUuid.data(), kUuid.size()));
unsigned char* der_data = nullptr;
int der_len = i2d_ASN1_UTF8STRING(val_str.get(), &der_data);
ASSERT_GT(der_len, 0);
bssl::UniquePtr<ASN1_OCTET_STRING> octet_str(ASN1_OCTET_STRING_new());
ASSERT_TRUE(ASN1_OCTET_STRING_set(octet_str.get(), der_data, der_len));
OPENSSL_free(der_data);
bssl::UniquePtr<X509_EXTENSION> ext(X509_EXTENSION_create_by_OBJ(
nullptr, ext_oid.get(), /*crit=*/0, octet_str.get()));
ASSERT_THAT(ext, NotNull());
ASSERT_TRUE(X509_add_ext(x509.get(), ext.get(), -1));
// Parse cert and extract the conforming product id.
X509Certificate cert(std::move(x509));
EXPECT_THAT(cert.GetConformingProductId(), IsOkAndHolds(kUuid));
}
TEST(X509CertificateTest, CertificateWithoutValidPublicKey) {
bssl::UniquePtr<X509> x509(X509_new());
ASSERT_NE(x509, nullptr);
X509Certificate cert(std::move(x509));
EXPECT_THAT(
cert.VerifySignature("sig", "data", SigningAlgorithm::kPs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Certificate does not have a valid public key")));
EXPECT_THAT(
cert.GetPemKeyParams(SigningAlgorithm::kPs256),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Certificate does not have a valid public key")));
}
} // namespace
} // namespace credentio