Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
src: shift even moar x509 stuff to ncrypto and consolidate
  • Loading branch information
jasnell committed Aug 13, 2024
commit 74c6839bf089c8e8e6a258c98168aea20abde8ea
41 changes: 41 additions & 0 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,24 @@ X509View::CheckMatch X509View::checkIp(const std::string_view ip, int flags) con
}
}

X509View X509View::From(const SSLPointer& ssl) {
ClearErrorOnReturn clear_error_on_return;
if (!ssl) return {};
return X509View(SSL_get_certificate(ssl.get()));
}

X509View X509View::From(const SSLCtxPointer& ctx) {
ClearErrorOnReturn clear_error_on_return;
if (!ctx) return {};
return X509View(SSL_CTX_get0_certificate(ctx.get()));
}

X509Pointer X509View::clone() const {
ClearErrorOnReturn clear_error_on_return;
if (!cert_) return {};
return X509Pointer(X509_dup(const_cast<X509*>(cert_)));
}

Result<X509Pointer, int> X509Pointer::Parse(Buffer<const unsigned char> buffer) {
ClearErrorOnReturn clearErrorOnReturn;
BIOPointer bio(BIO_new_mem_buf(buffer.data, buffer.len));
Expand All @@ -922,4 +940,27 @@ Result<X509Pointer, int> X509Pointer::Parse(Buffer<const unsigned char> buffer)

return Result<X509Pointer, int>(ERR_get_error());
}


X509Pointer X509Pointer::IssuerFrom(const SSLPointer& ssl, const X509View& view) {
return IssuerFrom(SSL_get_SSL_CTX(ssl.get()), view);
}

X509Pointer X509Pointer::IssuerFrom(const SSL_CTX* ctx, const X509View& cert) {
X509_STORE* store = SSL_CTX_get_cert_store(ctx);
DeleteFnPtr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx(
X509_STORE_CTX_new());
X509Pointer result;
X509* issuer;
if (store_ctx.get() != nullptr &&
X509_STORE_CTX_init(store_ctx.get(), store, nullptr, nullptr) == 1 &&
X509_STORE_CTX_get1_issuer(&issuer, store_ctx.get(), cert.get()) == 1) {
result.reset(issuer);
}
return result;
}

X509Pointer X509Pointer::PeerFrom(const SSLPointer& ssl) {
return X509Pointer(SSL_get_peer_certificate(ssl.get()));
}
} // namespace ncrypto
10 changes: 10 additions & 0 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,13 @@ class BignumPointer final {
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
};

class X509Pointer;

class X509View final {
public:
static X509View From(const SSLPointer& ssl);
static X509View From(const SSLCtxPointer& ctx);

X509View() = default;
inline explicit X509View(const X509* cert) : cert_(cert) {}
X509View(const X509View& other) = default;
Expand Down Expand Up @@ -342,6 +347,8 @@ class X509View final {
bool checkPrivateKey(const EVPKeyPointer& pkey) const;
bool checkPublicKey(const EVPKeyPointer& pkey) const;

X509Pointer clone() const;

enum class CheckMatch {
NO_MATCH,
MATCH,
Expand All @@ -360,6 +367,9 @@ class X509View final {
class X509Pointer final {
public:
static Result<X509Pointer, int> Parse(Buffer<const unsigned char> buffer);
static X509Pointer IssuerFrom(const SSLPointer& ssl, const X509View& view);
static X509Pointer IssuerFrom(const SSL_CTX* ctx, const X509View& view);
static X509Pointer PeerFrom(const SSLPointer& ssl);

X509Pointer() = default;
explicit X509Pointer(X509* cert);
Expand Down
49 changes: 5 additions & 44 deletions src/crypto/crypto_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "node_buffer.h"
#include "node_crypto.h"
#include "node_internals.h"
#include "openssl/types.h"
#include "string_bytes.h"
#include "v8.h"

Expand All @@ -31,40 +30,17 @@ namespace node {
using v8::Array;
using v8::ArrayBuffer;
using v8::BackingStore;
using v8::Boolean;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Integer;
using v8::Local;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Undefined;
using v8::Value;

namespace crypto {
static constexpr int kX509NameFlagsMultiline =
ASN1_STRFLGS_ESC_2253 |
ASN1_STRFLGS_ESC_CTRL |
ASN1_STRFLGS_UTF8_CONVERT |
XN_FLAG_SEP_MULTILINE |
XN_FLAG_FN_SN;

X509Pointer SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert) {
X509_STORE* store = SSL_CTX_get_cert_store(ctx);
DeleteFnPtr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx(
X509_STORE_CTX_new());
X509Pointer result;
X509* issuer;
if (store_ctx.get() != nullptr &&
X509_STORE_CTX_init(store_ctx.get(), store, nullptr, nullptr) == 1 &&
X509_STORE_CTX_get1_issuer(&issuer, store_ctx.get(), cert) == 1) {
result.reset(issuer);
}
return result;
}

void LogSecret(
const SSLPointer& ssl,
const char* name,
Expand Down Expand Up @@ -122,8 +98,7 @@ long VerifyPeerCertificate( // NOLINT(runtime/int)
const SSLPointer& ssl,
long def) { // NOLINT(runtime/int)
long err = def; // NOLINT(runtime/int)
if (X509* peer_cert = SSL_get_peer_certificate(ssl.get())) {
X509_free(peer_cert);
if (X509Pointer::PeerFrom(ssl)) {
err = SSL_get_verify_result(ssl.get());
} else {
const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl.get());
Expand All @@ -142,13 +117,14 @@ long VerifyPeerCertificate( // NOLINT(runtime/int)

bool UseSNIContext(
const SSLPointer& ssl, BaseObjectPtr<SecureContext> context) {
auto x509 = ncrypto::X509View::From(context->ctx());
if (!x509) return false;
SSL_CTX* ctx = context->ctx().get();
X509* x509 = SSL_CTX_get0_certificate(ctx);
EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx);
STACK_OF(X509)* chain;

int err = SSL_CTX_get0_chain_certs(ctx, &chain);
if (err == 1) err = SSL_use_certificate(ssl.get(), x509);
if (err == 1) err = SSL_use_certificate(ssl.get(), x509.get());
if (err == 1) err = SSL_use_PrivateKey(ssl.get(), pkey);
if (err == 1 && chain != nullptr) err = SSL_set1_chain(ssl.get(), chain);
return err == 1;
Expand Down Expand Up @@ -270,19 +246,6 @@ MaybeLocal<Value> GetCert(Environment* env, const SSLPointer& ssl) {
return X509Certificate::toObject(env, cert);
}

Local<Value> ToV8Value(Environment* env, const BIOPointer& bio) {
BUF_MEM* mem;
BIO_get_mem_ptr(bio.get(), &mem);
MaybeLocal<String> ret =
String::NewFromUtf8(
env->isolate(),
mem->data,
NewStringType::kNormal,
mem->length);
CHECK_EQ(BIO_reset(bio.get()), 1);
return ret.FromMaybe(Local<Value>());
}

namespace {
template <typename T>
bool Set(
Expand Down Expand Up @@ -351,7 +314,6 @@ MaybeLocal<Object> AddIssuerChainToObject(X509Pointer* cert,
return {};
}
object = ca_info.As<Object>();
;

// NOTE: Intentionally freeing cert that is not used anymore.
// Delete cert and continue aggregating issuers.
Expand All @@ -373,8 +335,7 @@ MaybeLocal<Object> GetLastIssuedCert(
Environment* const env) {
Local<Value> ca_info;
while (!cert->view().isIssuedBy(cert->view())) {
X509Pointer ca =
SSL_CTX_get_issuer(SSL_get_SSL_CTX(ssl.get()), cert->get());
auto ca = X509Pointer::IssuerFrom(ssl, cert->view());
if (!ca) break;

if (!X509Certificate::toObject(env, ca.view()).ToLocal(&ca_info)) return {};
Expand Down
6 changes: 0 additions & 6 deletions src/crypto/crypto_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ struct StackOfX509Deleter {
};
using StackOfX509 = std::unique_ptr<STACK_OF(X509), StackOfX509Deleter>;

using StackOfASN1 = ncrypto::StackOfASN1;

X509Pointer SSL_CTX_get_issuer(SSL_CTX* ctx, X509* cert);

void LogSecret(
const SSLPointer& ssl,
const char* name,
Expand Down Expand Up @@ -100,8 +96,6 @@ v8::MaybeLocal<v8::Value> GetCurrentCipherName(Environment* env,
v8::MaybeLocal<v8::Value> GetCurrentCipherVersion(Environment* env,
const SSLPointer& ssl);

v8::Local<v8::Value> ToV8Value(Environment* env, const BIOPointer& bio);

} // namespace crypto
} // namespace node

Expand Down
2 changes: 1 addition & 1 deletion src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
// TODO(tniessen): SSL_CTX_get_issuer does not allow the caller to
// distinguish between a failed operation and an empty result. Fix that
// and then handle the potential error properly here (set ret to 0).
*issuer_ = SSL_CTX_get_issuer(ctx, x.get());
*issuer_ = X509Pointer::IssuerFrom(ctx, x.view());
// NOTE: get_cert_store doesn't increment reference count,
// no need to free `store`
} else {
Expand Down
86 changes: 41 additions & 45 deletions src/crypto/crypto_x509.cc
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
#include "crypto_x509.h"
#include "crypto/crypto_x509.h"
#include "base_object-inl.h"
#include "crypto_bio.h"
#include "crypto_common.h"
#include "crypto_context.h"
#include "crypto_keys.h"
#include "crypto/crypto_common.h"
#include "crypto/crypto_keys.h"
#include "crypto/crypto_util.h"
#include "env-inl.h"
#include "env.h"
#include "memory_tracker-inl.h"
#include "ncrypto.h"
#include "node_errors.h"
#include "util-inl.h"
#include "v8-primitive.h"
#include "v8.h"

#include <string>
Expand All @@ -32,7 +29,6 @@ using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
using v8::Name;
using v8::NewStringType;
using v8::Object;
using v8::String;
Expand Down Expand Up @@ -67,7 +63,7 @@ void AddFingerprintDigest(const unsigned char* md,
unsigned int md_size,
char fingerprint[3 * EVP_MAX_MD_SIZE]) {
unsigned int i;
const char hex[] = "0123456789ABCDEF";
static constexpr char hex[] = "0123456789ABCDEF";

for (i = 0; i < md_size; i++) {
fingerprint[3 * i] = hex[(md[i] & 0xf0) >> 4];
Expand Down Expand Up @@ -255,7 +251,7 @@ MaybeLocal<Value> GetSerialNumber(Environment* env,
}

MaybeLocal<Value> GetKeyUsage(Environment* env, const ncrypto::X509View& cert) {
StackOfASN1 eku(static_cast<STACK_OF(ASN1_OBJECT)*>(
ncrypto::StackOfASN1 eku(static_cast<STACK_OF(ASN1_OBJECT)*>(
X509_get_ext_d2i(cert.get(), NID_ext_key_usage, nullptr, nullptr)));
if (eku) {
const int count = sk_ASN1_OBJECT_num(eku.get());
Expand Down Expand Up @@ -832,29 +828,33 @@ Local<FunctionTemplate> X509Certificate::GetConstructorTemplate(
BaseObject::kInternalFieldCount);
tmpl->SetClassName(
FIXED_ONE_BYTE_STRING(env->isolate(), "X509Certificate"));
SetProtoMethod(isolate, tmpl, "subject", Subject);
SetProtoMethod(isolate, tmpl, "subjectAltName", SubjectAltName);
SetProtoMethod(isolate, tmpl, "infoAccess", InfoAccess);
SetProtoMethod(isolate, tmpl, "issuer", Issuer);
SetProtoMethod(isolate, tmpl, "validTo", ValidTo);
SetProtoMethod(isolate, tmpl, "validFrom", ValidFrom);
SetProtoMethod(isolate, tmpl, "fingerprint", Fingerprint<EVP_sha1>);
SetProtoMethod(isolate, tmpl, "fingerprint256", Fingerprint<EVP_sha256>);
SetProtoMethod(isolate, tmpl, "fingerprint512", Fingerprint<EVP_sha512>);
SetProtoMethod(isolate, tmpl, "keyUsage", KeyUsage);
SetProtoMethod(isolate, tmpl, "serialNumber", SerialNumber);
SetProtoMethod(isolate, tmpl, "pem", Pem);
SetProtoMethod(isolate, tmpl, "raw", Der);
SetProtoMethod(isolate, tmpl, "publicKey", PublicKey);
SetProtoMethod(isolate, tmpl, "checkCA", CheckCA);
SetProtoMethod(isolate, tmpl, "checkHost", CheckHost);
SetProtoMethod(isolate, tmpl, "checkEmail", CheckEmail);
SetProtoMethod(isolate, tmpl, "checkIP", CheckIP);
SetProtoMethod(isolate, tmpl, "checkIssued", CheckIssued);
SetProtoMethod(isolate, tmpl, "checkPrivateKey", CheckPrivateKey);
SetProtoMethod(isolate, tmpl, "verify", CheckPublicKey);
SetProtoMethod(isolate, tmpl, "toLegacy", ToLegacy);
SetProtoMethod(isolate, tmpl, "getIssuerCert", GetIssuerCert);
SetProtoMethodNoSideEffect(isolate, tmpl, "subject", Subject);
SetProtoMethodNoSideEffect(isolate, tmpl, "subjectAltName", SubjectAltName);
SetProtoMethodNoSideEffect(isolate, tmpl, "infoAccess", InfoAccess);
SetProtoMethodNoSideEffect(isolate, tmpl, "issuer", Issuer);
SetProtoMethodNoSideEffect(isolate, tmpl, "validTo", ValidTo);
SetProtoMethodNoSideEffect(isolate, tmpl, "validFrom", ValidFrom);
SetProtoMethodNoSideEffect(
isolate, tmpl, "fingerprint", Fingerprint<EVP_sha1>);
SetProtoMethodNoSideEffect(
isolate, tmpl, "fingerprint256", Fingerprint<EVP_sha256>);
SetProtoMethodNoSideEffect(
isolate, tmpl, "fingerprint512", Fingerprint<EVP_sha512>);
SetProtoMethodNoSideEffect(isolate, tmpl, "keyUsage", KeyUsage);
SetProtoMethodNoSideEffect(isolate, tmpl, "serialNumber", SerialNumber);
SetProtoMethodNoSideEffect(isolate, tmpl, "pem", Pem);
SetProtoMethodNoSideEffect(isolate, tmpl, "raw", Der);
SetProtoMethodNoSideEffect(isolate, tmpl, "publicKey", PublicKey);
SetProtoMethodNoSideEffect(isolate, tmpl, "checkCA", CheckCA);
SetProtoMethodNoSideEffect(isolate, tmpl, "checkHost", CheckHost);
SetProtoMethodNoSideEffect(isolate, tmpl, "checkEmail", CheckEmail);
SetProtoMethodNoSideEffect(isolate, tmpl, "checkIP", CheckIP);
SetProtoMethodNoSideEffect(isolate, tmpl, "checkIssued", CheckIssued);
SetProtoMethodNoSideEffect(
isolate, tmpl, "checkPrivateKey", CheckPrivateKey);
SetProtoMethodNoSideEffect(isolate, tmpl, "verify", CheckPublicKey);
SetProtoMethodNoSideEffect(isolate, tmpl, "toLegacy", ToLegacy);
SetProtoMethodNoSideEffect(isolate, tmpl, "getIssuerCert", GetIssuerCert);
env->set_x509_constructor_template(tmpl);
}
return tmpl;
Expand Down Expand Up @@ -889,12 +889,9 @@ MaybeLocal<Object> X509Certificate::New(Environment* env,

MaybeLocal<Object> X509Certificate::GetCert(Environment* env,
const SSLPointer& ssl) {
ClearErrorOnReturn clear_error_on_return;
X509* cert = SSL_get_certificate(ssl.get());
if (cert == nullptr) return MaybeLocal<Object>();

X509Pointer ptr(X509_dup(cert));
return New(env, std::move(ptr));
auto cert = ncrypto::X509View::From(ssl);
if (!cert) return {};
return New(env, cert.clone());
}

MaybeLocal<Object> X509Certificate::GetPeerCert(Environment* env,
Expand All @@ -903,16 +900,16 @@ MaybeLocal<Object> X509Certificate::GetPeerCert(Environment* env,
ClearErrorOnReturn clear_error_on_return;
MaybeLocal<Object> maybe_cert;

bool is_server =
static_cast<int>(flag) & static_cast<int>(GetPeerCertificateFlag::SERVER);
X509Pointer cert;
if ((flag & GetPeerCertificateFlag::SERVER) ==
GetPeerCertificateFlag::SERVER) {
cert = X509Pointer::PeerFrom(ssl);
}

X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl.get()) : nullptr);
STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl.get());
if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0))
return MaybeLocal<Object>();

std::vector<Local<Value>> certs;

if (!cert) {
cert.reset(sk_X509_value(ssl_certs, 0));
sk_X509_delete(ssl_certs, 0);
Expand Down Expand Up @@ -984,7 +981,6 @@ std::unique_ptr<worker::TransferData> X509Certificate::CloneForMessaging()
return std::make_unique<X509CertificateTransferData>(cert_);
}


void X509Certificate::Initialize(Environment* env, Local<Object> target) {
SetMethod(env->context(), target, "parseX509", Parse);

Expand Down
14 changes: 14 additions & 0 deletions src/crypto/crypto_x509.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ class X509Certificate final : public BaseObject {
BaseObjectPtr<X509Certificate> issuer_cert_;
};

inline X509Certificate::GetPeerCertificateFlag operator|(
X509Certificate::GetPeerCertificateFlag lhs,
X509Certificate::GetPeerCertificateFlag rhs) {
return static_cast<X509Certificate::GetPeerCertificateFlag>(
static_cast<int>(lhs) | static_cast<int>(rhs));
}

inline X509Certificate::GetPeerCertificateFlag operator&(
X509Certificate::GetPeerCertificateFlag lhs,
X509Certificate::GetPeerCertificateFlag rhs) {
return static_cast<X509Certificate::GetPeerCertificateFlag>(
static_cast<int>(lhs) & static_cast<int>(rhs));
}

} // namespace crypto
} // namespace node

Expand Down
Loading