blob: ad47c62d391cf50d0ecf114f875910f89a9666e5 [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 "crypto/default/cms/certificates.h"
#include <cstring>
#include <vector>
#include "crypto/default/cms/cms_parser.h"
#include "openssl/asn1.h"
#include "openssl/base.h"
#include "openssl/bytestring.h"
#include "openssl/obj_mac.h"
#include "openssl/safestack.h"
#include "openssl/stack.h"
#include "openssl/x509.h"
namespace credentio_cms {
namespace {
std::vector<X509*> MatchByIssuerNameAndSerialNumber(
const STACK_OF(X509) * certificates, const ByteString& issuer_name,
const ByteString& serial_number) {
std::vector<X509*> matches;
if (CBS_data(issuer_name.cbs_ptr()) == nullptr ||
CBS_data(serial_number.cbs_ptr()) == nullptr ||
CBS_len(issuer_name.cbs_ptr()) == 0 ||
CBS_len(serial_number.cbs_ptr()) == 0) {
return matches;
}
const unsigned char* p = CBS_data(issuer_name.cbs_ptr());
bssl::UniquePtr<X509_NAME> x509_issuer_name(
d2i_X509_NAME(nullptr, &p, CBS_len(issuer_name.cbs_ptr())));
p = CBS_data(serial_number.cbs_ptr());
bssl::UniquePtr<ASN1_INTEGER> x509_serial_number(
c2i_ASN1_INTEGER(nullptr, &p, CBS_len(serial_number.cbs_ptr())));
if (x509_issuer_name == nullptr || x509_serial_number == nullptr) {
return matches;
}
for (int i = 0; i < sk_X509_num(certificates); ++i) {
X509* cert = sk_X509_value(certificates, i);
if (cert == nullptr) {
continue;
}
const ASN1_INTEGER* certificate_serial = X509_get0_serialNumber(cert);
if (certificate_serial == nullptr ||
ASN1_INTEGER_cmp(x509_serial_number.get(), certificate_serial) != 0) {
continue;
}
if (X509_name_cmp(x509_issuer_name.get(), X509_get_issuer_name(cert)) ==
0) {
matches.push_back(cert);
}
}
return matches;
}
std::vector<X509*> MatchBySubjectKeyId(
const STACK_OF(X509) * certificates,
const ByteString& subject_key_identifier) {
std::vector<X509*> matches;
for (int i = 0; i < sk_X509_num(certificates); ++i) {
X509* cert = sk_X509_value(certificates, i);
if (cert == nullptr) {
continue;
}
int index = X509_get_ext_by_NID(cert, NID_subject_key_identifier, -1);
if (index < 0) {
continue;
}
const X509_EXTENSION* ext = X509_get_ext(cert, index);
if (ext == nullptr) {
continue;
}
const ASN1_OCTET_STRING* value = X509_EXTENSION_get_data(ext);
// https://tools.ietf.org/html/rfc5280#section-4.2.1.2
CBS extension;
CBS_init(&extension, ASN1_STRING_get0_data(value),
ASN1_STRING_length(value));
CBS ext_ski;
if (CBS_get_asn1(&extension, &ext_ski, CBS_ASN1_OCTETSTRING) == 1 &&
CBS_len(&ext_ski) == CBS_len(subject_key_identifier.cbs_ptr()) &&
memcmp(CBS_data(subject_key_identifier.cbs_ptr()), CBS_data(&ext_ski),
CBS_len(&ext_ski)) == 0) {
matches.push_back(cert);
}
}
return matches;
}
} // namespace
STACK_OF(X509) * GetAllCertificates(const Content& cms_content) {
bssl::UniquePtr<STACK_OF(X509)> stack(sk_X509_new_null());
for (const auto& certificate : cms_content.certificates) {
// Return null if there is an error parsing any certificate instead of
// returning partial results.
const unsigned char* udata = CBS_data(certificate.cbs_ptr());
if (udata == nullptr || CBS_len(certificate.cbs_ptr()) == 0) {
return nullptr;
}
X509* cert =
d2i_X509(nullptr /* unused */, &udata, CBS_len(certificate.cbs_ptr()));
if (cert == nullptr) {
return nullptr;
}
sk_X509_push(stack.get(), cert);
}
return stack.release();
}
std::vector<X509*> GetSignerCertificates(const STACK_OF(X509) * certificates,
const SignerInfo& signer) {
if (CBS_len(signer.subject_key_identifier.cbs_ptr()) > 0) {
return MatchBySubjectKeyId(certificates, signer.subject_key_identifier);
} else {
return MatchByIssuerNameAndSerialNumber(certificates, signer.issuer_name,
signer.serial_number);
}
}
} // namespace credentio_cms