Skip to content

Commit ec67c3a

Browse files
jasnelltargos
authored andcommitted
src: move some X509Certificate stuff to ncrypto
PR-URL: #54241 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 35e1b37 commit ec67c3a

File tree

7 files changed

+635
-295
lines changed

7 files changed

+635
-295
lines changed

deps/ncrypto/engine.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ EnginePointer EnginePointer::getEngineByName(const std::string_view name,
5858
}
5959
}
6060
}
61-
return std::move(engine);
61+
return engine;
6262
}
6363

6464
bool EnginePointer::setAsDefault(uint32_t flags, CryptoErrorList* errors) {

deps/ncrypto/ncrypto.cc

+224-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#include "ncrypto.h"
22
#include <algorithm>
33
#include <cstring>
4-
#include "openssl/bn.h"
5-
#include "openssl/evp.h"
6-
#include "openssl/pkcs12.h"
7-
#include "openssl/x509v3.h"
4+
#include <openssl/bn.h>
5+
#include <openssl/evp.h>
6+
#include <openssl/pkcs12.h>
7+
#include <openssl/x509v3.h>
88
#if OPENSSL_VERSION_MAJOR >= 3
9-
#include "openssl/provider.h"
9+
#include <openssl/provider.h>
1010
#endif
1111

1212
namespace ncrypto {
@@ -703,4 +703,223 @@ bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext) {
703703
return ok;
704704
}
705705

706+
// ============================================================================
707+
// X509Pointer
708+
709+
X509Pointer::X509Pointer(X509* x509) : cert_(x509) {}
710+
711+
X509Pointer::X509Pointer(X509Pointer&& other) noexcept
712+
: cert_(other.release()) {}
713+
714+
X509Pointer& X509Pointer::operator=(X509Pointer&& other) noexcept {
715+
if (this == &other) return *this;
716+
this->~X509Pointer();
717+
return *new (this) X509Pointer(std::move(other));
718+
}
719+
720+
X509Pointer::~X509Pointer() { reset(); }
721+
722+
void X509Pointer::reset(X509* x509) {
723+
cert_.reset(x509);
724+
}
725+
726+
X509* X509Pointer::release() {
727+
return cert_.release();
728+
}
729+
730+
X509View X509Pointer::view() const {
731+
return X509View(cert_.get());
732+
}
733+
734+
BIOPointer X509View::toPEM() const {
735+
ClearErrorOnReturn clearErrorOnReturn;
736+
if (cert_ == nullptr) return {};
737+
BIOPointer bio(BIO_new(BIO_s_mem()));
738+
if (!bio) return {};
739+
if (PEM_write_bio_X509(bio.get(), const_cast<X509*>(cert_)) <= 0) return {};
740+
return bio;
741+
}
742+
743+
BIOPointer X509View::toDER() const {
744+
ClearErrorOnReturn clearErrorOnReturn;
745+
if (cert_ == nullptr) return {};
746+
BIOPointer bio(BIO_new(BIO_s_mem()));
747+
if (!bio) return {};
748+
if (i2d_X509_bio(bio.get(), const_cast<X509*>(cert_)) <= 0) return {};
749+
return bio;
750+
}
751+
752+
BIOPointer X509View::getSubject() const {
753+
ClearErrorOnReturn clearErrorOnReturn;
754+
if (cert_ == nullptr) return {};
755+
BIOPointer bio(BIO_new(BIO_s_mem()));
756+
if (!bio) return {};
757+
if (X509_NAME_print_ex(bio.get(), X509_get_subject_name(cert_),
758+
0, kX509NameFlagsMultiline) <= 0) {
759+
return {};
760+
}
761+
return bio;
762+
}
763+
764+
BIOPointer X509View::getSubjectAltName() const {
765+
ClearErrorOnReturn clearErrorOnReturn;
766+
if (cert_ == nullptr) return {};
767+
BIOPointer bio(BIO_new(BIO_s_mem()));
768+
if (!bio) return {};
769+
int index = X509_get_ext_by_NID(cert_, NID_subject_alt_name, -1);
770+
if (index < 0 || !SafeX509SubjectAltNamePrint(bio, X509_get_ext(cert_, index))) {
771+
return {};
772+
}
773+
return bio;
774+
}
775+
776+
BIOPointer X509View::getIssuer() const {
777+
ClearErrorOnReturn clearErrorOnReturn;
778+
if (cert_ == nullptr) return {};
779+
BIOPointer bio(BIO_new(BIO_s_mem()));
780+
if (!bio) return {};
781+
if (X509_NAME_print_ex(bio.get(), X509_get_issuer_name(cert_), 0,
782+
kX509NameFlagsMultiline) <= 0) {
783+
return {};
784+
}
785+
return bio;
786+
}
787+
788+
BIOPointer X509View::getInfoAccess() const {
789+
ClearErrorOnReturn clearErrorOnReturn;
790+
if (cert_ == nullptr) return {};
791+
BIOPointer bio(BIO_new(BIO_s_mem()));
792+
if (!bio) return {};
793+
int index = X509_get_ext_by_NID(cert_, NID_info_access, -1);
794+
if (index < 0) return {};
795+
if (!SafeX509InfoAccessPrint(bio, X509_get_ext(cert_, index))) {
796+
return {};
797+
}
798+
return bio;
799+
}
800+
801+
BIOPointer X509View::getValidFrom() const {
802+
ClearErrorOnReturn clearErrorOnReturn;
803+
if (cert_ == nullptr) return {};
804+
BIOPointer bio(BIO_new(BIO_s_mem()));
805+
if (!bio) return {};
806+
ASN1_TIME_print(bio.get(), X509_get_notBefore(cert_));
807+
return bio;
808+
}
809+
810+
BIOPointer X509View::getValidTo() const {
811+
ClearErrorOnReturn clearErrorOnReturn;
812+
if (cert_ == nullptr) return {};
813+
BIOPointer bio(BIO_new(BIO_s_mem()));
814+
if (!bio) return {};
815+
ASN1_TIME_print(bio.get(), X509_get_notAfter(cert_));
816+
return bio;
817+
}
818+
819+
DataPointer X509View::getSerialNumber() const {
820+
ClearErrorOnReturn clearErrorOnReturn;
821+
if (cert_ == nullptr) return {};
822+
if (ASN1_INTEGER* serial_number = X509_get_serialNumber(const_cast<X509*>(cert_))) {
823+
if (auto bn = BignumPointer(ASN1_INTEGER_to_BN(serial_number, nullptr))) {
824+
return bn.toHex();
825+
}
826+
}
827+
return {};
828+
}
829+
830+
Result<EVPKeyPointer, int> X509View::getPublicKey() const {
831+
ClearErrorOnReturn clearErrorOnReturn;
832+
if (cert_ == nullptr) return Result<EVPKeyPointer, int>(EVPKeyPointer {});
833+
auto pkey = EVPKeyPointer(X509_get_pubkey(const_cast<X509*>(cert_)));
834+
if (!pkey) return Result<EVPKeyPointer, int>(ERR_get_error());
835+
return pkey;
836+
}
837+
838+
StackOfASN1 X509View::getKeyUsage() const {
839+
ClearErrorOnReturn clearErrorOnReturn;
840+
if (cert_ == nullptr) return {};
841+
return StackOfASN1(static_cast<STACK_OF(ASN1_OBJECT)*>(
842+
X509_get_ext_d2i(cert_, NID_ext_key_usage, nullptr, nullptr)));
843+
}
844+
845+
bool X509View::isCA() const {
846+
ClearErrorOnReturn clearErrorOnReturn;
847+
if (cert_ == nullptr) return false;
848+
return X509_check_ca(const_cast<X509*>(cert_)) == 1;
849+
}
850+
851+
bool X509View::isIssuedBy(const X509View& issuer) const {
852+
ClearErrorOnReturn clearErrorOnReturn;
853+
if (cert_ == nullptr || issuer.cert_ == nullptr) return false;
854+
return X509_check_issued(const_cast<X509*>(issuer.cert_),
855+
const_cast<X509*>(cert_)) == X509_V_OK;
856+
}
857+
858+
bool X509View::checkPrivateKey(const EVPKeyPointer& pkey) const {
859+
ClearErrorOnReturn clearErrorOnReturn;
860+
if (cert_ == nullptr || pkey == nullptr) return false;
861+
return X509_check_private_key(const_cast<X509*>(cert_), pkey.get()) == 1;
862+
}
863+
864+
bool X509View::checkPublicKey(const EVPKeyPointer& pkey) const {
865+
ClearErrorOnReturn clearErrorOnReturn;
866+
if (cert_ == nullptr || pkey == nullptr) return false;
867+
return X509_verify(const_cast<X509*>(cert_), pkey.get()) == 1;
868+
}
869+
870+
X509View::CheckMatch X509View::checkHost(const std::string_view host, int flags,
871+
DataPointer* peerName) const {
872+
ClearErrorOnReturn clearErrorOnReturn;
873+
if (cert_ == nullptr) return CheckMatch::NO_MATCH;
874+
char* peername;
875+
switch (X509_check_host(const_cast<X509*>(cert_), host.data(), host.size(), flags, &peername)) {
876+
case 0: return CheckMatch::NO_MATCH;
877+
case 1: {
878+
if (peername != nullptr) {
879+
DataPointer name(peername, strlen(peername));
880+
if (peerName != nullptr) *peerName = std::move(name);
881+
}
882+
return CheckMatch::MATCH;
883+
}
884+
case -2: return CheckMatch::INVALID_NAME;
885+
default: return CheckMatch::OPERATION_FAILED;
886+
}
887+
}
888+
889+
X509View::CheckMatch X509View::checkEmail(const std::string_view email, int flags) const {
890+
ClearErrorOnReturn clearErrorOnReturn;
891+
if (cert_ == nullptr) return CheckMatch::NO_MATCH;
892+
switch (X509_check_email(const_cast<X509*>(cert_), email.data(), email.size(), flags)) {
893+
case 0: return CheckMatch::NO_MATCH;
894+
case 1: return CheckMatch::MATCH;
895+
case -2: return CheckMatch::INVALID_NAME;
896+
default: return CheckMatch::OPERATION_FAILED;
897+
}
898+
}
899+
900+
X509View::CheckMatch X509View::checkIp(const std::string_view ip, int flags) const {
901+
ClearErrorOnReturn clearErrorOnReturn;
902+
if (cert_ == nullptr) return CheckMatch::NO_MATCH;
903+
switch (X509_check_ip_asc(const_cast<X509*>(cert_), ip.data(), flags)) {
904+
case 0: return CheckMatch::NO_MATCH;
905+
case 1: return CheckMatch::MATCH;
906+
case -2: return CheckMatch::INVALID_NAME;
907+
default: return CheckMatch::OPERATION_FAILED;
908+
}
909+
}
910+
911+
Result<X509Pointer, int> X509Pointer::Parse(Buffer<const unsigned char> buffer) {
912+
ClearErrorOnReturn clearErrorOnReturn;
913+
BIOPointer bio(BIO_new_mem_buf(buffer.data, buffer.len));
914+
if (!bio) return Result<X509Pointer, int>(ERR_get_error());
915+
916+
X509Pointer pem(PEM_read_bio_X509_AUX(bio.get(), nullptr, NoPasswordCallback, nullptr));
917+
if (pem) return Result<X509Pointer, int>(std::move(pem));
918+
BIO_reset(bio.get());
919+
920+
X509Pointer der(d2i_X509_bio(bio.get(), nullptr));
921+
if (der) return Result<X509Pointer, int>(std::move(der));
922+
923+
return Result<X509Pointer, int>(ERR_get_error());
924+
}
706925
} // namespace ncrypto

deps/ncrypto/ncrypto.h

+91-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <optional>
77
#include <string>
88
#include <string_view>
9-
#include "openssl/bn.h"
9+
#include <openssl/bn.h>
1010
#include <openssl/x509.h>
1111
#include <openssl/dh.h>
1212
#include <openssl/dsa.h>
@@ -91,6 +91,13 @@ namespace ncrypto {
9191
#endif
9292
}
9393

94+
static constexpr int kX509NameFlagsMultiline =
95+
ASN1_STRFLGS_ESC_2253 |
96+
ASN1_STRFLGS_ESC_CTRL |
97+
ASN1_STRFLGS_UTF8_CONVERT |
98+
XN_FLAG_SEP_MULTILINE |
99+
XN_FLAG_FN_SN;
100+
94101
// ============================================================================
95102
// Error handling utilities
96103

@@ -164,6 +171,14 @@ class MarkPopErrorOnReturn final {
164171
CryptoErrorList* errors_;
165172
};
166173

174+
template <typename T, typename E>
175+
struct Result final {
176+
T value;
177+
std::optional<E> error;
178+
Result(T&& value) : value(std::move(value)) {}
179+
Result(E&& error) : error(std::move(error)) {}
180+
};
181+
167182
// ============================================================================
168183
// Various smart pointer aliases for OpenSSL types.
169184

@@ -197,7 +212,13 @@ using RSAPointer = DeleteFnPtr<RSA, RSA_free>;
197212
using SSLCtxPointer = DeleteFnPtr<SSL_CTX, SSL_CTX_free>;
198213
using SSLPointer = DeleteFnPtr<SSL, SSL_free>;
199214
using SSLSessionPointer = DeleteFnPtr<SSL_SESSION, SSL_SESSION_free>;
200-
using X509Pointer = DeleteFnPtr<X509, X509_free>;
215+
216+
struct StackOfXASN1Deleter {
217+
void operator()(STACK_OF(ASN1_OBJECT)* p) const {
218+
sk_ASN1_OBJECT_pop_free(p, ASN1_OBJECT_free);
219+
}
220+
};
221+
using StackOfASN1 = std::unique_ptr<STACK_OF(ASN1_OBJECT), StackOfXASN1Deleter>;
201222

202223
// An unowned, unmanaged pointer to a buffer of data.
203224
template <typename T>
@@ -290,6 +311,74 @@ class BignumPointer final {
290311
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
291312
};
292313

314+
class X509View final {
315+
public:
316+
X509View() = default;
317+
inline explicit X509View(const X509* cert) : cert_(cert) {}
318+
X509View(const X509View& other) = default;
319+
X509View& operator=(const X509View& other) = default;
320+
NCRYPTO_DISALLOW_MOVE(X509View)
321+
322+
inline bool operator==(std::nullptr_t) noexcept { return cert_ == nullptr; }
323+
inline operator bool() const { return cert_ != nullptr; }
324+
325+
BIOPointer toPEM() const;
326+
BIOPointer toDER() const;
327+
328+
BIOPointer getSubject() const;
329+
BIOPointer getSubjectAltName() const;
330+
BIOPointer getIssuer() const;
331+
BIOPointer getInfoAccess() const;
332+
BIOPointer getValidFrom() const;
333+
BIOPointer getValidTo() const;
334+
DataPointer getSerialNumber() const;
335+
Result<EVPKeyPointer, int> getPublicKey() const;
336+
StackOfASN1 getKeyUsage() const;
337+
338+
bool isCA() const;
339+
bool isIssuedBy(const X509View& other) const;
340+
bool checkPrivateKey(const EVPKeyPointer& pkey) const;
341+
bool checkPublicKey(const EVPKeyPointer& pkey) const;
342+
343+
enum class CheckMatch {
344+
NO_MATCH,
345+
MATCH,
346+
INVALID_NAME,
347+
OPERATION_FAILED,
348+
};
349+
CheckMatch checkHost(const std::string_view host, int flags,
350+
DataPointer* peerName = nullptr) const;
351+
CheckMatch checkEmail(const std::string_view email, int flags) const;
352+
CheckMatch checkIp(const std::string_view ip, int flags) const;
353+
354+
private:
355+
const X509* cert_ = nullptr;
356+
};
357+
358+
class X509Pointer final {
359+
public:
360+
static Result<X509Pointer, int> Parse(Buffer<const unsigned char> buffer);
361+
362+
X509Pointer() = default;
363+
explicit X509Pointer(X509* cert);
364+
X509Pointer(X509Pointer&& other) noexcept;
365+
X509Pointer& operator=(X509Pointer&& other) noexcept;
366+
NCRYPTO_DISALLOW_COPY(X509Pointer)
367+
~X509Pointer();
368+
369+
inline bool operator==(std::nullptr_t) noexcept { return cert_ == nullptr; }
370+
inline operator bool() const { return cert_ != nullptr; }
371+
inline X509* get() const { return cert_.get(); }
372+
void reset(X509* cert = nullptr);
373+
X509* release();
374+
375+
X509View view() const;
376+
operator X509View() const { return view(); }
377+
378+
private:
379+
DeleteFnPtr<X509, X509_free> cert_;
380+
};
381+
293382
#ifndef OPENSSL_NO_ENGINE
294383
class EnginePointer final {
295384
public:

0 commit comments

Comments
 (0)