blob: 9332f8db9b71a8450ce4d0f75e70b432b5f1bd82 [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/cms/certificates.h"
#include <memory>
#include <string>
#include <vector>
#include "absl/log/check.h"
#include "absl/log/die_if_null.h"
#include "absl/strings/escaping.h"
#include "crypto/default/cms/cms_error_code.h"
#include "crypto/default/cms/cms_parser.h"
#include "crypto/default/cms/cms_test_helper.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "openssl/base.h"
#include "openssl/bio.h"
#include "openssl/bytestring.h"
#include "openssl/safestack.h"
#include "openssl/stack.h"
#include "openssl/x509.h"
using testing::ElementsAre;
namespace credentio_cms {
namespace {
std::string X509ToDer(X509* x509) {
const int len = i2d_X509(x509, nullptr /* buffer */);
CHECK_GT(len, 0);
std::unique_ptr<char[]> data(new char[len]);
unsigned char* dataptr = reinterpret_cast<unsigned char*>(data.get());
const int len2 = i2d_X509(x509, &dataptr);
CHECK_EQ(len, len2);
std::string der_output;
der_output.assign(data.get(), len);
return der_output;
}
ByteString ByteStringFromString(const std::string& value) {
ByteString bs;
CBS_init(bs.cbs_ptr(), reinterpret_cast<const unsigned char*>(value.data()),
value.length());
return bs;
}
class CertificateTest : public testing::Test {
protected:
void SetUp() override {
user1_rsa_.reset(
ABSL_DIE_IF_NULL(PemToCertificate(GetFileContents("user1_both.pem"))));
user1_ecc_.reset(
ABSL_DIE_IF_NULL(PemToCertificate(GetFileContents("user1_ecc.pem"))));
user2_rsa_.reset(
ABSL_DIE_IF_NULL(PemToCertificate(GetFileContents("user2_both.pem"))));
user1_rsa_der_ = X509ToDer(user1_rsa_.get());
user1_ecc_der_ = X509ToDer(user1_ecc_.get());
user2_rsa_der_ = X509ToDer(user2_rsa_.get());
content_.certificates.push_back(ByteStringFromString(user1_rsa_der_));
content_.certificates.push_back(ByteStringFromString(user1_ecc_der_));
content_.certificates.push_back(ByteStringFromString(user2_rsa_der_));
test_stack_ = sk_X509_new_null();
sk_X509_push(test_stack_, user1_rsa_.get());
sk_X509_push(test_stack_, user1_ecc_.get());
sk_X509_push(test_stack_, user2_rsa_.get());
}
void TearDown() override {
if (test_stack_ != nullptr) {
sk_X509_free(test_stack_);
}
}
STACK_OF(X509) * test_stack_ = nullptr;
Content content_;
bssl::UniquePtr<X509> user1_rsa_;
bssl::UniquePtr<X509> user1_ecc_;
bssl::UniquePtr<X509> user2_rsa_;
std::string user1_rsa_der_;
std::string user1_ecc_der_;
std::string user2_rsa_der_;
};
TEST_F(CertificateTest, GetAllCertificates) {
bssl::UniquePtr<STACK_OF(X509)> stack(GetAllCertificates(content_));
ASSERT_EQ(3, sk_X509_num(stack.get()));
EXPECT_EQ(0, X509_cmp(sk_X509_value(stack.get(), 0), user1_rsa_.get()));
EXPECT_EQ(0, X509_cmp(sk_X509_value(stack.get(), 1), user1_ecc_.get()));
EXPECT_EQ(0, X509_cmp(sk_X509_value(stack.get(), 2), user2_rsa_.get()));
}
TEST_F(CertificateTest, GetByName) {
X509_NAME* issuer = X509_get_issuer_name(user1_rsa_.get());
const int len = i2d_X509_NAME(issuer, nullptr /* buffer */);
CHECK_GT(len, 0);
std::unique_ptr<char[]> data(new char[len]);
unsigned char* dataptr = reinterpret_cast<unsigned char*>(data.get());
const int len2 = i2d_X509_NAME(issuer, &dataptr);
CHECK_EQ(len, len2);
std::string name_der;
name_der.assign(data.get(), len);
SignerInfo signer_info;
signer_info.issuer_name = ByteStringFromString(name_der);
std::string serial = "\x12\x3b";
signer_info.serial_number = ByteStringFromString(serial);
std::vector<X509*> matching = GetSignerCertificates(test_stack_, signer_info);
EXPECT_THAT(matching, ElementsAre(user1_ecc_.get()));
CHECK(absl::HexStringToBytes("123f", &serial));
signer_info.serial_number = ByteStringFromString(serial);
matching = GetSignerCertificates(test_stack_, signer_info);
EXPECT_THAT(matching, ElementsAre());
}
TEST_F(CertificateTest, GetBySki) {
SignerInfo signer_info;
std::string ski;
CHECK(
absl::HexStringToBytes("9B6EAE67945D61FB736371448CE0F2ECBA43CBA0", &ski));
signer_info.subject_key_identifier = ByteStringFromString(ski);
std::vector<X509*> matching = GetSignerCertificates(test_stack_, signer_info);
EXPECT_THAT(matching, ElementsAre(user1_ecc_.get()));
}
TEST_F(CertificateTest, RFCSkiExample) {
// The RFC 4134 4.7 example uses the Subject Key Id to match the signer's
// certificate.
std::string cms = GetCms(GetFileContents("rfc4134-4.7.msg"));
Content contents;
std::string error_message;
ASSERT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &contents, &error_message))
<< error_message;
bssl::UniquePtr<STACK_OF(X509)> certs(GetAllCertificates(contents));
ASSERT_EQ(1, sk_X509_num(certs.get()));
ASSERT_EQ(1, contents.signers.size());
std::vector<X509*> matching =
GetSignerCertificates(certs.get(), contents.signers[0]);
EXPECT_THAT(matching, ElementsAre(sk_X509_value(certs.get(), 0)));
}
TEST_F(CertificateTest, GetAllCertificatesWithEmptyCert) {
// Zero out the 2nd certificate in the stack.
CBS_init(content_.certificates[1].cbs_ptr(), nullptr, 0);
bssl::UniquePtr<STACK_OF(X509)> stack(GetAllCertificates(content_));
ASSERT_TRUE(stack == nullptr);
}
TEST_F(CertificateTest, GetAllCertificatesWithBrokenCert) {
// A valid X509 certificate should start with a 0x30 (Constructed Sequence)
// tag.
uint8_t kBrokenCert[] = {0xFF};
// Make the second certificate in the stack broken.
CBS_init(content_.certificates[1].cbs_ptr(), kBrokenCert,
sizeof(kBrokenCert));
bssl::UniquePtr<STACK_OF(X509)> stack(GetAllCertificates(content_));
ASSERT_TRUE(stack == nullptr);
}
} // namespace
} // namespace credentio_cms