@@ -46,6 +46,15 @@ using BignumCtxPointer = DeleteFnPtr<BN_CTX, BN_CTX_free>;
4646using BignumGenCallbackPointer = DeleteFnPtr<BN_GENCB , BN_GENCB_free>;
4747using NetscapeSPKIPointer = DeleteFnPtr<NETSCAPE_SPKI , NETSCAPE_SPKI_free>;
4848
49+ struct ASN1SequenceDeleter final {
50+ void operator ()(ASN1_SEQUENCE_ANY * sequence) const {
51+ sk_ASN1_TYPE_pop_free (sequence, ASN1_TYPE_free);
52+ }
53+ };
54+
55+ using ASN1SequencePointer =
56+ std::unique_ptr<ASN1_SEQUENCE_ANY , ASN1SequenceDeleter>;
57+
4958#if NCRYPTO_USE_OPENSSL_PROVIDER
5059using X509PubKeyPointer = DeleteFnPtr<X509_PUBKEY , X509_PUBKEY_free>;
5160// OSSL_STORE_close() returns int, so it needs a void-returning adapter to be
@@ -1606,6 +1615,164 @@ bool X509View::isCA() const {
16061615 return X509_check_ca (const_cast <X509 *>(cert_)) == 1 ;
16071616}
16081617
1618+ namespace {
1619+ bool CopyStringBytes (const ASN1_STRING * value,
1620+ std::vector<unsigned char >* output) {
1621+ if (value == nullptr ) return false ;
1622+ const int length = ASN1_STRING_length (value);
1623+ const unsigned char * data = ASN1_STRING_get0_data (value);
1624+ if (length < 0 || (length > 0 && data == nullptr )) return false ;
1625+ if (length == 0 ) {
1626+ output->clear ();
1627+ } else {
1628+ output->assign (data, data + static_cast <size_t >(length));
1629+ }
1630+ return true ;
1631+ }
1632+
1633+ bool CanConvertNameValueToUtf8 (const ASN1_STRING * value) {
1634+ if (value == nullptr ) return false ;
1635+ switch (ASN1_STRING_type (value)) {
1636+ case V_ASN1_UTF8STRING :
1637+ case V_ASN1_NUMERICSTRING :
1638+ case V_ASN1_PRINTABLESTRING :
1639+ case V_ASN1_T61STRING :
1640+ case V_ASN1_IA5STRING :
1641+ case V_ASN1_UTCTIME :
1642+ case V_ASN1_GENERALIZEDTIME :
1643+ case V_ASN1_ISO64STRING :
1644+ case V_ASN1_UNIVERSALSTRING :
1645+ case V_ASN1_BMPSTRING :
1646+ return true ;
1647+ default :
1648+ return false ;
1649+ }
1650+ }
1651+
1652+ std::optional<size_t > GetEncodedRdnCount (const X509_NAME * name) {
1653+ const unsigned char * der = nullptr ;
1654+ size_t der_length = 0 ;
1655+ if (X509_NAME_get0_der (name, &der, &der_length) != 1 || der == nullptr ||
1656+ der_length > static_cast <size_t >(LONG_MAX )) {
1657+ return std::nullopt ;
1658+ }
1659+
1660+ const unsigned char * cursor = der;
1661+ ASN1SequencePointer rdns (d2i_ASN1_SEQUENCE_ANY (
1662+ nullptr , &cursor, static_cast <long >(der_length))); // NOLINT(runtime/int)
1663+ if (!rdns || cursor != der + der_length) return std::nullopt ;
1664+
1665+ const OPENSSL_SIZE_T count = sk_ASN1_TYPE_num (rdns.get ());
1666+ for (OPENSSL_SIZE_T i = 0 ; i < count; i++) {
1667+ const ASN1_TYPE * rdn = sk_ASN1_TYPE_value (rdns.get (), i);
1668+ if (rdn == nullptr || ASN1_TYPE_get (rdn) != V_ASN1_SET ) {
1669+ return std::nullopt ;
1670+ }
1671+ }
1672+ return static_cast <size_t >(count);
1673+ }
1674+
1675+ std::optional<X509NameInspection> InspectName (const X509_NAME * name) {
1676+ if (name == nullptr ) return std::nullopt ;
1677+ const int count = X509_NAME_entry_count (name);
1678+ if (count < 0 ) return std::nullopt ;
1679+ const auto encoded_rdn_count = GetEncodedRdnCount (name);
1680+ if (!encoded_rdn_count.has_value ()) return std::nullopt ;
1681+
1682+ X509NameInspection output;
1683+ output.reserve (static_cast <size_t >(count));
1684+ int previous_set = -1 ;
1685+ for (int i = 0 ; i < count; i++) {
1686+ const X509_NAME_ENTRY * entry = X509_NAME_get_entry (name, i);
1687+ if (entry == nullptr ) return std::nullopt ;
1688+
1689+ const ASN1_OBJECT * object = X509_NAME_ENTRY_get_object (entry);
1690+ const ASN1_STRING * value = X509_NAME_ENTRY_get_data (entry);
1691+ auto oid = ObjectToText (object, true );
1692+ const int set = X509_NAME_ENTRY_set (entry);
1693+ if (!oid.has_value () || value == nullptr || set < 0 ||
1694+ (set != previous_set &&
1695+ (previous_set == INT_MAX || set != previous_set + 1 ))) {
1696+ return std::nullopt ;
1697+ }
1698+ previous_set = set;
1699+
1700+ X509NameAttribute attribute{
1701+ .set = set,
1702+ .oid = std::move (oid.value ()),
1703+ };
1704+ if (CanConvertNameValueToUtf8 (value)) {
1705+ unsigned char * data = nullptr ;
1706+ const int length = ASN1_STRING_to_UTF8 (&data, value);
1707+ DataPointer owned (data, length > 0 ? static_cast <size_t >(length) : 0 );
1708+ if (length < 0 || (length > 0 && !owned)) return std::nullopt ;
1709+ attribute.value =
1710+ length == 0 ? std::string ()
1711+ : std::string (reinterpret_cast <const char *>(owned.get ()),
1712+ static_cast <size_t >(length));
1713+ }
1714+ output.push_back (std::move (attribute));
1715+ }
1716+ const size_t inspected_rdn_count =
1717+ previous_set < 0 ? 0 : static_cast <size_t >(previous_set) + 1 ;
1718+ if (inspected_rdn_count != encoded_rdn_count.value ()) return std::nullopt ;
1719+ return output;
1720+ }
1721+
1722+ std::optional<std::vector<X509RawExtension>> InspectExtensions (
1723+ const X509 * certificate) {
1724+ if (certificate == nullptr ) return std::nullopt ;
1725+ const int count = X509_get_ext_count (certificate);
1726+ if (count < 0 ) return std::nullopt ;
1727+
1728+ std::vector<X509RawExtension> output;
1729+ output.reserve (static_cast <size_t >(count));
1730+ for (int i = 0 ; i < count; i++) {
1731+ const X509_EXTENSION * extension = X509_get_ext (certificate, i);
1732+ if (extension == nullptr ) return std::nullopt ;
1733+
1734+ auto * mutable_extension = const_cast <X509_EXTENSION *>(extension);
1735+ auto oid = ObjectToText (X509_EXTENSION_get_object (mutable_extension), true );
1736+ const ASN1_OCTET_STRING * value = X509_EXTENSION_get_data (mutable_extension);
1737+ if (!oid.has_value () || value == nullptr ) return std::nullopt ;
1738+
1739+ X509RawExtension inspected{
1740+ .oid = std::move (oid.value ()),
1741+ .critical = X509_EXTENSION_get_critical (mutable_extension) != 0 ,
1742+ };
1743+ if (!CopyStringBytes (value, &inspected.value )) return std::nullopt ;
1744+ output.push_back (std::move (inspected));
1745+ }
1746+ return output;
1747+ }
1748+ } // namespace
1749+
1750+ std::optional<int64_t > X509View::getVersion () const {
1751+ MarkPopErrorOnReturn mark_pop_error_on_return;
1752+ if (cert_ == nullptr ) return std::nullopt ;
1753+ const long version = X509_get_version (cert_); // NOLINT(runtime/int)
1754+ if (version < 0 || version > 2 ) return std::nullopt ;
1755+ return static_cast <int64_t >(version) + 1 ;
1756+ }
1757+
1758+ std::optional<X509NameInspection> X509View::inspectSubjectName () const {
1759+ MarkPopErrorOnReturn mark_pop_error_on_return;
1760+ if (cert_ == nullptr ) return std::nullopt ;
1761+ return InspectName (X509_get_subject_name (cert_));
1762+ }
1763+
1764+ std::optional<X509NameInspection> X509View::inspectIssuerName () const {
1765+ MarkPopErrorOnReturn mark_pop_error_on_return;
1766+ if (cert_ == nullptr ) return std::nullopt ;
1767+ return InspectName (X509_get_issuer_name (cert_));
1768+ }
1769+
1770+ std::optional<std::vector<X509RawExtension>> X509View::inspectExtensions ()
1771+ const {
1772+ MarkPopErrorOnReturn mark_pop_error_on_return;
1773+ return InspectExtensions (cert_);
1774+ }
1775+
16091776bool X509View::isIssuedBy (const X509View& issuer) const {
16101777 MarkPopErrorOnReturn mark_pop_error_on_return;
16111778 if (cert_ == nullptr || issuer.cert_ == nullptr ) return false ;
0 commit comments