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