| /* | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you 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 | |
| http://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. | |
| */ | |
| #pragma once | |
| #include <openssl/ssl.h> | |
| #include <openssl/x509.h> | |
| #include <openssl/x509v3.h> | |
| #include <openssl/bio.h> | |
| #include <vector> | |
| #include <algorithm> | |
| #include "cripts/Lulu.hpp" | |
| #include "cripts/Connections.hpp" | |
| namespace cripts::Certs | |
| { | |
| // These *must* match the values in x509v3.h. | |
| enum class SAN : std::uint8_t { | |
| OTHER = GEN_OTHERNAME, | |
| EMAIL = GEN_EMAIL, | |
| DNS = GEN_DNS, | |
| URI = GEN_URI, | |
| IPADD = GEN_IPADD, | |
| }; | |
| class String : public cripts::StringViewMixin<String> | |
| { | |
| using super_type = cripts::StringViewMixin<String>; | |
| using self_type = String; | |
| public: | |
| virtual ~String() = default; | |
| operator cripts::string_view() const { return GetSV(); } | |
| self_type & | |
| operator=(const cripts::string_view str) override | |
| { | |
| _setSV(str); | |
| return *this; | |
| } | |
| using super_type::StringViewMixin; | |
| }; // Class cripts::Certs::String | |
| } // namespace cripts::Certs | |
| namespace detail | |
| { | |
| class CertBase | |
| { | |
| using self_type = CertBase; | |
| using X509 = struct x509_st; | |
| using BIO = struct bio_st; | |
| public: | |
| class X509Value | |
| { | |
| using self_type = X509Value; | |
| public: | |
| explicit X509Value(CertBase *owner) : _owner(owner) {} | |
| virtual ~X509Value() = default; | |
| X509Value() = delete; | |
| X509Value(const self_type &) = delete; | |
| self_type &operator=(const self_type &) = delete; | |
| self_type &operator=(self_type &&) = delete; | |
| cripts::string_view | |
| GetSV() const | |
| { | |
| _load(); | |
| return _value; | |
| } | |
| operator cripts::string_view() const { return GetSV(); } | |
| protected: | |
| void _update_value() const; | |
| virtual void | |
| _load() const | |
| { | |
| if (!_bio) { | |
| _bio.reset(BIO_new(BIO_s_mem())); | |
| } | |
| } | |
| using NameGetter = decltype(&X509_get_subject_name); | |
| using IntegerGetter = decltype(&X509_get_serialNumber); | |
| using LongGetter = decltype(&X509_get_version); | |
| using TimeGetter = decltype(&X509_get_notBefore); | |
| void _load_name(NameGetter getter) const; | |
| void _load_integer(IntegerGetter getter) const; | |
| void _load_long(LongGetter getter) const; | |
| void _load_time(TimeGetter getter) const; | |
| CertBase *_owner = nullptr; | |
| mutable std::unique_ptr<BIO, decltype(&BIO_free)> _bio{nullptr, BIO_free}; | |
| mutable cripts::Certs::String _value; | |
| mutable bool _ready = false; | |
| }; // End class CertBase::X509Value | |
| // Here comes all the various X509 value fields that we support. | |
| class Certificate : public X509Value | |
| { | |
| using self_type = Certificate; | |
| using super_type = X509Value; | |
| public: | |
| explicit Certificate(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void _load() const override; | |
| }; // End class CertBase::Certificate | |
| class Signature : public X509Value | |
| { | |
| using self_type = Signature; | |
| using super_type = X509Value; | |
| public: | |
| explicit Signature(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void _load() const override; | |
| }; // End class CertBase::Signature | |
| class Subject : public X509Value | |
| { | |
| using self_type = Subject; | |
| using super_type = X509Value; | |
| public: | |
| explicit Subject(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void | |
| _load() const override | |
| { | |
| _load_name(X509_get_subject_name); | |
| } | |
| }; // End class CertBase::Subject | |
| class Issuer : public X509Value | |
| { | |
| using self_type = Issuer; | |
| using super_type = X509Value; | |
| public: | |
| explicit Issuer(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void | |
| _load() const override | |
| { | |
| _load_name(X509_get_issuer_name); | |
| } | |
| }; // End class CertBase::Issuer | |
| class SerialNumber : public X509Value | |
| { | |
| using self_type = SerialNumber; | |
| using super_type = X509Value; | |
| public: | |
| explicit SerialNumber(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void | |
| _load() const override | |
| { | |
| _load_integer(X509_get_serialNumber); | |
| } | |
| }; // End class CertBase::SerialNumer | |
| class NotBefore : public X509Value | |
| { | |
| using self_type = NotBefore; | |
| using super_type = X509Value; | |
| public: | |
| explicit NotBefore(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void | |
| _load() const override | |
| { | |
| _load_time(X509_get_notBefore); | |
| } | |
| }; // End class CertBase::NotBefore | |
| class NotAfter : public X509Value | |
| { | |
| using self_type = NotAfter; | |
| using super_type = X509Value; | |
| public: | |
| explicit NotAfter(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void | |
| _load() const override | |
| { | |
| _load_time(X509_get_notAfter); | |
| } | |
| }; // End class CertBase::NotAfter | |
| class Version : public X509Value | |
| { | |
| using self_type = Version; | |
| using super_type = X509Value; | |
| public: | |
| explicit Version(CertBase *owner) : super_type(owner) {} | |
| protected: | |
| void | |
| _load() const override | |
| { | |
| _load_long(X509_get_version); | |
| } | |
| }; // End class CertBase::SerialNumer | |
| class SAN | |
| { | |
| using self_type = SAN; | |
| class SANBase | |
| { | |
| using self_type = SANBase; | |
| public: | |
| using Container = std::vector<std::string>; | |
| explicit SANBase(SAN *owner, cripts::Certs::SAN san_id) : _san_id(san_id), _owner(owner) {} | |
| virtual ~SANBase() = default; | |
| SANBase() = delete; | |
| SANBase(const SANBase &) = delete; | |
| SANBase(SANBase &&) = delete; | |
| self_type &operator=(const self_type &) = delete; | |
| self_type &operator=(self_type &&) = delete; | |
| [[nodiscard]] cripts::Certs::SAN | |
| sanType() const | |
| { | |
| return _san_id; | |
| } | |
| class Iterator | |
| { | |
| using self_type = Iterator; | |
| public: | |
| using iterator_category = std::forward_iterator_tag; | |
| using base_iterator = Container::const_iterator; | |
| using value_type = cripts::Certs::String; | |
| using reference = cripts::Certs::String; | |
| explicit Iterator(base_iterator iter) : _iter(iter) {} | |
| ~Iterator() = default; | |
| Iterator(const Iterator &) = default; | |
| Iterator &operator=(const Iterator &) = default; | |
| Iterator(Iterator &&) = default; | |
| Iterator &operator=(Iterator &&) = default; | |
| [[nodiscard]] reference | |
| operator*() const | |
| { | |
| return {*_iter}; | |
| } | |
| self_type & | |
| operator++() | |
| { | |
| ++_iter; | |
| return *this; | |
| } | |
| [[nodiscard]] bool | |
| operator!=(const self_type &other) const | |
| { | |
| return _iter != other._iter; | |
| } | |
| [[nodiscard]] base_iterator | |
| base_iter() const | |
| { | |
| return _iter; | |
| } | |
| private: | |
| base_iterator _iter; | |
| }; // End class SAN::SANBase::Iterator | |
| void | |
| ensureLoaded() const | |
| { | |
| if (!_ready) { | |
| _load(); | |
| _ready = true; | |
| } | |
| } | |
| [[nodiscard]] Iterator | |
| begin() const | |
| { | |
| ensureLoaded(); | |
| return Iterator(_data.begin()); | |
| } | |
| [[nodiscard]] Iterator | |
| end() const | |
| { | |
| return Iterator(_data.end()); | |
| } | |
| [[nodiscard]] Container & | |
| Data() const | |
| { | |
| ensureLoaded(); | |
| return _data; | |
| } | |
| [[nodiscard]] size_t | |
| size() const | |
| { | |
| ensureLoaded(); | |
| return _data.size(); | |
| } | |
| [[nodiscard]] size_t | |
| Size() const | |
| { | |
| return size(); | |
| } | |
| [[nodiscard]] cripts::Certs::String | |
| operator[](size_t index) const | |
| { | |
| ensureLoaded(); | |
| if (index >= _data.size()) { | |
| return {""}; | |
| } | |
| return {_data[index]}; | |
| } | |
| [[nodiscard]] cripts::string Join(const char *delim = ",") const; | |
| protected: | |
| void _load() const; | |
| mutable Container _data; | |
| mutable bool _ready = false; | |
| cripts::Certs::SAN _san_id = cripts::Certs::SAN::OTHER; | |
| SAN *_owner = nullptr; | |
| }; // End class SAN::SANBase | |
| public: | |
| template <cripts::Certs::SAN ID> class SANType : public SANBase | |
| { | |
| using super_type = SANBase; | |
| using self_type = SANType; | |
| public: | |
| explicit SANType(SAN *owner) : SANBase(owner, ID) {} | |
| ~SANType() override = default; | |
| SANType() = delete; | |
| SANType(const SANType &) = delete; | |
| SANType(SANType &&) = delete; | |
| self_type &operator=(const self_type &) = delete; | |
| self_type &operator=(self_type &&) = delete; | |
| }; // End class SAN::SANType | |
| explicit SAN(CertBase *owner) : email(this), dns(this), uri(this), ipadd(this), _owner(owner) {} | |
| ~SAN() = default; | |
| SAN() = delete; | |
| SAN(const SAN &) = delete; | |
| SAN(SAN &&) = delete; | |
| self_type &operator=(const self_type &) = delete; | |
| self_type &operator=(self_type &&) = delete; | |
| SAN::SANType<cripts::Certs::SAN::EMAIL> email; | |
| SAN::SANType<cripts::Certs::SAN::DNS> dns; | |
| SAN::SANType<cripts::Certs::SAN::URI> uri; | |
| SAN::SANType<cripts::Certs::SAN::IPADD> ipadd; | |
| class Iterator | |
| { | |
| public: | |
| using iterator_category = std::forward_iterator_tag; | |
| using value_type = std::tuple<cripts::Certs::SAN, cripts::Certs::String>; | |
| using reference = value_type; // Return by value instead of const reference | |
| using base_iterator = SANBase::Container::const_iterator; | |
| explicit Iterator(std::nullptr_t) : _ended(true) {} | |
| explicit Iterator(const SAN *san) : _san(san) { _advance(); } | |
| Iterator() = default; | |
| Iterator(const Iterator &) = default; | |
| Iterator &operator=(const Iterator &) = default; | |
| Iterator(Iterator &&) = default; | |
| Iterator &operator=(Iterator &&) = default; | |
| ~Iterator() = default; | |
| [[nodiscard]] reference | |
| operator*() const | |
| { | |
| return {_current}; | |
| } | |
| Iterator & | |
| operator++() | |
| { | |
| if (_ended) { | |
| return *this; | |
| } | |
| ++_iter; | |
| if (_iter == _san->_sans[_index - 1]->Data().end()) { | |
| _advance(); | |
| } else { | |
| _update_current(); | |
| } | |
| return *this; | |
| } | |
| [[nodiscard]] bool | |
| operator!=(const Iterator &other) const | |
| { | |
| if (_ended && other._ended) { | |
| return false; | |
| } | |
| return _san != other._san || _index != other._index || _iter != other._iter; | |
| } | |
| [[nodiscard]] bool | |
| operator==(const Iterator &other) const | |
| { | |
| if (_ended && other._ended) { | |
| return true; | |
| } | |
| return _san == other._san && _index == other._index && _iter == other._iter; | |
| } | |
| private: | |
| void | |
| _update_current() const | |
| { | |
| if (_san && !_ended) { | |
| _current = std::make_tuple(_san->_sans[_index - 1]->sanType(), cripts::Certs::String(*_iter)); | |
| } | |
| } | |
| void _advance(); | |
| mutable value_type _current; // The current value of the iterator | |
| base_iterator _iter; // The current iterator within the SAN types | |
| bool _ended = false; | |
| const SAN *_san = nullptr; | |
| size_t _index = 0; | |
| }; // End class CertBase::SAN::Iterator | |
| [[nodiscard]] Iterator | |
| begin() const | |
| { | |
| auto it = Iterator(this); | |
| return it; | |
| } | |
| [[nodiscard]] Iterator | |
| end() const | |
| { | |
| Iterator it{nullptr}; | |
| return it; | |
| } | |
| [[nodiscard]] size_t | |
| size() const | |
| { | |
| size_t total = 0; | |
| for (const auto *san : _sans) { | |
| total += san->Size(); | |
| } | |
| return total; | |
| } | |
| [[nodiscard]] size_t | |
| Size() const | |
| { | |
| return size(); | |
| } | |
| [[nodiscard]] Iterator::value_type operator[](size_t index) const; | |
| private: | |
| const std::array<const SANBase *, 4> _sans = std::to_array<const SANBase *>({&email, &dns, &uri, &ipadd}); | |
| CertBase *_owner; | |
| }; // End class CertBase::SAN | |
| public: | |
| CertBase(detail::ConnBase &conn) | |
| : certificate(this), | |
| signature(this), | |
| subject(this), | |
| issuer(this), | |
| serialNumber(this), | |
| notBefore(this), | |
| notAfter(this), | |
| version(this), | |
| san(this), | |
| _conn(&conn) | |
| { | |
| } | |
| CertBase() = delete; | |
| CertBase(const self_type &) = delete; | |
| self_type &operator=(const self_type &) = delete; | |
| self_type &operator=(self_type &&) = delete; | |
| Certificate certificate; | |
| Signature signature; | |
| Subject subject; | |
| Issuer issuer; | |
| SerialNumber serialNumber; | |
| NotBefore notBefore; | |
| NotAfter notAfter; | |
| Version version; | |
| SAN san; | |
| protected: | |
| X509 *_x509 = nullptr; | |
| detail::ConnBase *_conn = nullptr; | |
| }; // End class detail::CertBase | |
| template <bool IsMutualTLS> class Cert : public detail::CertBase | |
| { | |
| using self_type = Cert<IsMutualTLS>; | |
| using super_type = detail::CertBase; | |
| public: | |
| explicit Cert(detail::ConnBase &conn) : super_type(conn) { _x509 = conn.tls.GetX509(IsMutualTLS); } | |
| }; // End class Cert | |
| } // namespace detail | |
| namespace cripts::Certs | |
| { | |
| using Client = detail::Cert<true>; | |
| using Server = detail::Cert<false>; | |
| } // namespace cripts::Certs | |
| namespace fmt | |
| { | |
| template <> struct formatter<cripts::Certs::SAN> { | |
| constexpr auto | |
| parse(format_parse_context &ctx) -> decltype(ctx.begin()) | |
| { | |
| return ctx.begin(); | |
| } | |
| template <typename FormatContext> | |
| auto | |
| format(cripts::Certs::SAN san, FormatContext &ctx) const -> decltype(ctx.out()) | |
| { | |
| return fmt::format_to(ctx.out(), "{}", static_cast<int>(san)); | |
| } | |
| }; | |
| template <> struct formatter<cripts::Certs::String> { | |
| constexpr auto | |
| parse(format_parse_context &ctx) -> decltype(ctx.begin()) | |
| { | |
| return ctx.begin(); | |
| } | |
| template <typename FormatContext> | |
| auto | |
| format(const cripts::Certs::String &str, FormatContext &ctx) const -> decltype(ctx.out()) | |
| { | |
| return fmt::format_to(ctx.out(), "{}", str.GetSV()); | |
| } | |
| }; | |
| } // namespace fmt |