| // 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 |