Skip to content

Commit

Permalink
src: add additional utilities to crypto::SecureContext
Browse files Browse the repository at this point in the history
In preparation for use by the QUIC implementation.
  • Loading branch information
jasnell committed Jan 22, 2023
1 parent 8c4bdea commit 7b624da
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 49 deletions.
130 changes: 82 additions & 48 deletions src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ using v8::HandleScope;
using v8::Int32;
using v8::Integer;
using v8::Isolate;
using v8::Just;
using v8::Local;
using v8::Maybe;
using v8::Nothing;
using v8::Object;
using v8::PropertyAttribute;
using v8::ReadOnly;
Expand Down Expand Up @@ -574,6 +577,22 @@ void SecureContext::SetKeylogCallback(KeylogCb cb) {
SSL_CTX_set_keylog_callback(ctx_.get(), cb);
}

Maybe<bool> SecureContext::UseKey(Environment* env,
std::shared_ptr<KeyObjectData> key) {
if (key->GetKeyType() != KeyType::kKeyTypePrivate) {
THROW_ERR_CRYPTO_INVALID_KEYTYPE(env);
return Nothing<bool>();
}

ClearErrorOnReturn clear_error_on_return;
if (!SSL_CTX_use_PrivateKey(ctx_.get(), key->GetAsymmetricKey().get())) {
ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_PrivateKey");
return Nothing<bool>();
}

return Just(true);
}

void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand Down Expand Up @@ -662,97 +681,112 @@ void SecureContext::SetEngineKey(const FunctionCallbackInfo<Value>& args) {
}
#endif // !OPENSSL_NO_ENGINE

void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

SecureContext* sc;
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());

CHECK_GE(args.Length(), 1); // Certificate argument is mandator

BIOPointer bio(LoadBIO(env, args[0]));
if (!bio)
return;

sc->cert_.reset();
sc->issuer_.reset();
Maybe<bool> SecureContext::AddCert(Environment* env, BIOPointer&& bio) {
ClearErrorOnReturn clear_error_on_return;
if (!bio) return Just(false);
cert_.reset();
issuer_.reset();

if (!SSL_CTX_use_certificate_chain(
sc->ctx_.get(),
std::move(bio),
&sc->cert_,
&sc->issuer_)) {
return ThrowCryptoError(
env,
ERR_get_error(),
"SSL_CTX_use_certificate_chain");
// The SSL_CTX_use_certificate_chain call here is not from openssl, this is
// the method implemented elsewhere in this file. The naming is a bit
// confusing, unfortunately.
if (SSL_CTX_use_certificate_chain(
ctx_.get(), std::move(bio), &cert_, &issuer_) == 0) {
ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_certificate_chain");
return Nothing<bool>();
}
return Just(true);
}

void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
void SecureContext::SetCert(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

SecureContext* sc;
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
ClearErrorOnReturn clear_error_on_return;

CHECK_GE(args.Length(), 1); // CA certificate argument is mandatory
CHECK_GE(args.Length(), 1); // Certificate argument is mandatory

BIOPointer bio(LoadBIO(env, args[0]));
if (!bio)
return;
USE(sc->AddCert(env, std::move(bio)));
}

X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get());
void SecureContext::SetCACert(const BIOPointer& bio) {
ClearErrorOnReturn clear_error_on_return;
if (!bio) return;
X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx_.get());
while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX(
bio.get(), nullptr, NoPasswordCallback, nullptr))) {
if (cert_store == GetOrCreateRootCertStore()) {
cert_store = NewRootCertStore();
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
SSL_CTX_set_cert_store(ctx_.get(), cert_store);
}
X509_STORE_add_cert(cert_store, x509.get());
SSL_CTX_add_client_CA(sc->ctx_.get(), x509.get());
CHECK_EQ(1, X509_STORE_add_cert(cert_store, x509.get()));
CHECK_EQ(1, SSL_CTX_add_client_CA(ctx_.get(), x509.get()));
}
}

void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

SecureContext* sc;
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());

CHECK_GE(args.Length(), 1); // CRL argument is mandatory

ClearErrorOnReturn clear_error_on_return;
CHECK_GE(args.Length(), 1); // CA certificate argument is mandatory

BIOPointer bio(LoadBIO(env, args[0]));
if (!bio)
return;
sc->SetCACert(bio);
}

Maybe<bool> SecureContext::SetCRL(Environment* env, const BIOPointer& bio) {
ClearErrorOnReturn clear_error_on_return;
if (!bio) return Just(false);

DeleteFnPtr<X509_CRL, X509_CRL_free> crl(
PEM_read_bio_X509_CRL(bio.get(), nullptr, NoPasswordCallback, nullptr));

if (!crl)
return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL");
if (!crl) {
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL");
return Nothing<bool>();
}

X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get());
X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx_.get());
if (cert_store == GetOrCreateRootCertStore()) {
cert_store = NewRootCertStore();
SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store);
SSL_CTX_set_cert_store(ctx_.get(), cert_store);
}

X509_STORE_add_crl(cert_store, crl.get());
X509_STORE_set_flags(cert_store,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
CHECK_EQ(1, X509_STORE_add_crl(cert_store, crl.get()));
CHECK_EQ(1,
X509_STORE_set_flags(
cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL));
return Just(true);
}

void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

SecureContext* sc;
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());

CHECK_GE(args.Length(), 1); // CRL argument is mandatory

BIOPointer bio(LoadBIO(env, args[0]));
USE(sc->SetCRL(env, bio));
}

void SecureContext::SetRootCerts() {
ClearErrorOnReturn clear_error_on_return;
X509_STORE* store = GetOrCreateRootCertStore();
auto store = GetOrCreateRootCertStore();

// Increment reference count so global store is not deleted along with CTX.
X509_STORE_up_ref(store);
SSL_CTX_set_cert_store(sc->ctx_.get(), store);
SSL_CTX_set_cert_store(ctx_.get(), store);
}

void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
SecureContext* sc;
ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder());
sc->SetRootCerts();
}

void SecureContext::SetCipherSuites(const FunctionCallbackInfo<Value>& args) {
Expand Down
17 changes: 16 additions & 1 deletion src/crypto/crypto_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "crypto/crypto_util.h"
#include "base_object.h"
#include "crypto/crypto_keys.h"
#include "crypto/crypto_util.h"
#include "env.h"
#include "memory_tracker.h"
#include "v8.h"
Expand Down Expand Up @@ -43,13 +44,27 @@ class SecureContext final : public BaseObject {

const SSLCtxPointer& ctx() const { return ctx_; }

// Non-const ctx() that allows for non-default initialization of
// the SecureContext.
SSLCtxPointer& ctx() { return ctx_; }

SSLPointer CreateSSL();

void SetGetSessionCallback(GetSessionCb cb);
void SetKeylogCallback(KeylogCb cb);
void SetNewSessionCallback(NewSessionCb cb);
void SetSelectSNIContextCallback(SelectSNIContextCb cb);

inline const X509Pointer& issuer() const { return issuer_; }
inline const X509Pointer& cert() const { return cert_; }

v8::Maybe<bool> AddCert(Environment* env, BIOPointer&& bio);
v8::Maybe<bool> SetCRL(Environment* env, const BIOPointer& bio);
v8::Maybe<bool> UseKey(Environment* env, std::shared_ptr<KeyObjectData> key);

void SetCACert(const BIOPointer& bio);
void SetRootCerts();

// TODO(joyeecheung): track the memory used by OpenSSL types
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(SecureContext)
Expand Down

0 comments on commit 7b624da

Please sign in to comment.