Skip to content

Commit b9d8979

Browse files
committed
tls,quic: commonize TLS cert handling between tls, dtls & quic
Signed-off-by: Tim Perry <pimterry@gmail.com>
1 parent fe5037b commit b9d8979

8 files changed

Lines changed: 242 additions & 158 deletions

File tree

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@
411411
'src/crypto/crypto_timing.cc',
412412
'src/crypto/crypto_cipher.cc',
413413
'src/crypto/crypto_context.cc',
414+
'src/crypto/crypto_tls_context.cc',
414415
'src/crypto/crypto_ec.cc',
415416
'src/crypto/crypto_pqc.cc',
416417
'src/crypto/crypto_kem.cc',
@@ -449,6 +450,7 @@
449450
'src/crypto/crypto_tls.h',
450451
'src/crypto/crypto_clienthello.h',
451452
'src/crypto/crypto_context.h',
453+
'src/crypto/crypto_tls_context.h',
452454
'src/crypto/crypto_ec.h',
453455
'src/crypto/crypto_pqc.h',
454456
'src/crypto/crypto_hkdf.h',

src/crypto/crypto_context.cc

Lines changed: 13 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "base_object-inl.h"
33
#include "crypto/crypto_bio.h"
44
#include "crypto/crypto_common.h"
5+
#include "crypto/crypto_tls_context.h"
56
#include "crypto/crypto_util.h"
67
#include "env-inl.h"
78
#include "memory_tracker-inl.h"
@@ -1731,22 +1732,14 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
17311732
ByteSource passphrase;
17321733
if (args[1]->IsString())
17331734
passphrase = ByteSource::FromString(env, args[1].As<String>());
1734-
// This redirection is necessary because the PasswordCallback expects a
1735-
// pointer to a pointer to the passphrase ByteSource to allow passing in
1736-
// const ByteSources.
1737-
const ByteSource* pass_ptr = &passphrase;
1738-
1739-
EVPKeyPointer key(
1740-
PEM_read_bio_PrivateKey(bio.get(),
1741-
nullptr,
1742-
PasswordCallback,
1743-
&pass_ptr));
1744-
1745-
if (!key)
1746-
return ThrowCryptoError(env, ERR_get_error(), "PEM_read_bio_PrivateKey");
1747-
1748-
if (!SSL_CTX_use_PrivateKey(sc->ctx_.get(), key.get()))
1749-
return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_PrivateKey");
1735+
switch (UsePrivateKey(sc->ctx_.get(), bio, &passphrase)) {
1736+
case PrivateKeyResult::kSuccess:
1737+
break;
1738+
case PrivateKeyResult::kParseError:
1739+
return ThrowCryptoError(env, ERR_get_error(), "PEM_read_bio_PrivateKey");
1740+
case PrivateKeyResult::kApplyError:
1741+
return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_PrivateKey");
1742+
}
17501743
}
17511744

17521745
void SecureContext::SetSigalgs(const FunctionCallbackInfo<Value>& args) {
@@ -1849,16 +1842,7 @@ void SecureContext::SetX509StoreFlag(unsigned long flags) {
18491842
}
18501843

18511844
X509_STORE* SecureContext::GetCertStoreOwnedByThisSecureContext() {
1852-
Environment* env = this->env();
1853-
if (own_cert_store_cache_ != nullptr) return own_cert_store_cache_;
1854-
1855-
X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx_.get());
1856-
if (cert_store == GetOrCreateRootCertStore(env)) {
1857-
cert_store = NewRootCertStore(env);
1858-
SSL_CTX_set_cert_store(ctx_.get(), cert_store);
1859-
}
1860-
1861-
return own_cert_store_cache_ = cert_store;
1845+
return GetOrCreateOwnedCertStore(env(), ctx_.get(), &own_cert_store_cache_);
18621846
}
18631847

18641848
void SecureContext::SetAllowPartialTrustChain(
@@ -1870,13 +1854,7 @@ void SecureContext::SetAllowPartialTrustChain(
18701854

18711855
void SecureContext::SetCACert(const BIOPointer& bio) {
18721856
ClearErrorOnReturn clear_error_on_return;
1873-
if (!bio) return;
1874-
while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX(
1875-
bio.get(), nullptr, NoPasswordCallback, nullptr))) {
1876-
CHECK_EQ(1,
1877-
X509_STORE_add_cert(GetCertStoreOwnedByThisSecureContext(), x509));
1878-
CHECK_EQ(1, SSL_CTX_add_client_CA(ctx_.get(), x509));
1879-
}
1857+
AddCACertificates(env(), ctx_.get(), bio, &own_cert_store_cache_);
18801858
}
18811859

18821860
void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
@@ -1896,20 +1874,11 @@ Maybe<void> SecureContext::SetCRL(Environment* env, const BIOPointer& bio) {
18961874
// TODO(tniessen): this should be checked by the caller and not treated as ok
18971875
if (!bio) return JustVoid();
18981876

1899-
DeleteFnPtr<X509_CRL, X509_CRL_free> crl(
1900-
PEM_read_bio_X509_CRL(bio.get(), nullptr, NoPasswordCallback, nullptr));
1901-
1902-
if (!crl) {
1877+
if (!crypto::AddCRL(env, ctx_.get(), bio, &own_cert_store_cache_)) {
19031878
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL");
19041879
return Nothing<void>();
19051880
}
19061881

1907-
X509_STORE* cert_store = GetCertStoreOwnedByThisSecureContext();
1908-
1909-
CHECK_EQ(1, X509_STORE_add_crl(cert_store, crl.get()));
1910-
CHECK_EQ(1,
1911-
X509_STORE_set_flags(
1912-
cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL));
19131882
return JustVoid();
19141883
}
19151884

@@ -1927,12 +1896,7 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {
19271896

19281897
void SecureContext::SetRootCerts() {
19291898
ClearErrorOnReturn clear_error_on_return;
1930-
Environment* env = this->env();
1931-
auto store = GetOrCreateRootCertStore(env);
1932-
1933-
// Increment reference count so global store is not deleted along with CTX.
1934-
X509_STORE_up_ref(store);
1935-
SSL_CTX_set_cert_store(ctx_.get(), store);
1899+
UseDefaultRootCertStore(env(), ctx_.get());
19361900
}
19371901

19381902
void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {

src/crypto/crypto_context.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,6 @@ class SecureContext final : public BaseObject {
201201
#endif
202202
};
203203

204-
int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
205-
ncrypto::BIOPointer&& in,
206-
ncrypto::X509Pointer* cert,
207-
ncrypto::X509Pointer* issuer);
208-
209204
} // namespace crypto
210205
} // namespace node
211206

src/crypto/crypto_tls_context.cc

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include "crypto/crypto_tls_context.h"
2+
3+
#include "crypto/crypto_context.h"
4+
#include "crypto/crypto_keys.h"
5+
#include "crypto/crypto_util.h"
6+
7+
namespace node::crypto {
8+
9+
using ncrypto::DeleteFnPtr;
10+
using ncrypto::EVPKeyPointer;
11+
using ncrypto::X509Pointer;
12+
13+
X509_STORE* GetOrCreateOwnedCertStore(Environment* env,
14+
SSL_CTX* ctx,
15+
X509_STORE** cache) {
16+
if (cache != nullptr && *cache != nullptr) return *cache;
17+
18+
X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx);
19+
if (cert_store == GetOrCreateRootCertStore(env)) {
20+
cert_store = NewRootCertStore(env);
21+
SSL_CTX_set_cert_store(ctx, cert_store);
22+
}
23+
24+
if (cache != nullptr) *cache = cert_store;
25+
return cert_store;
26+
}
27+
28+
void UseDefaultRootCertStore(Environment* env, SSL_CTX* ctx) {
29+
X509_STORE* store = GetOrCreateRootCertStore(env);
30+
31+
// Increment reference count so global store is not deleted along with CTX.
32+
X509_STORE_up_ref(store);
33+
SSL_CTX_set_cert_store(ctx, store);
34+
}
35+
36+
size_t AddCACertificates(Environment* env,
37+
SSL_CTX* ctx,
38+
const ncrypto::BIOPointer& bio,
39+
X509_STORE** cache) {
40+
if (!bio) return 0;
41+
42+
size_t count = 0;
43+
while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX(
44+
bio.get(), nullptr, NoPasswordCallback, nullptr))) {
45+
CHECK_EQ(1,
46+
X509_STORE_add_cert(GetOrCreateOwnedCertStore(env, ctx, cache),
47+
x509.get()));
48+
CHECK_EQ(1, SSL_CTX_add_client_CA(ctx, x509.get()));
49+
count++;
50+
}
51+
return count;
52+
}
53+
54+
bool AddCRL(Environment* env,
55+
SSL_CTX* ctx,
56+
const ncrypto::BIOPointer& bio,
57+
X509_STORE** cache) {
58+
if (!bio) return false;
59+
60+
DeleteFnPtr<X509_CRL, X509_CRL_free> crl(
61+
PEM_read_bio_X509_CRL(bio.get(), nullptr, NoPasswordCallback, nullptr));
62+
if (!crl) return false;
63+
64+
X509_STORE* cert_store = GetOrCreateOwnedCertStore(env, ctx, cache);
65+
CHECK_EQ(1, X509_STORE_add_crl(cert_store, crl.get()));
66+
CHECK_EQ(1,
67+
X509_STORE_set_flags(
68+
cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL));
69+
return true;
70+
}
71+
72+
PrivateKeyResult UsePrivateKey(SSL_CTX* ctx,
73+
const ncrypto::BIOPointer& bio,
74+
const ByteSource* passphrase) {
75+
if (!bio) return PrivateKeyResult::kParseError;
76+
77+
ByteSource empty_passphrase;
78+
if (passphrase == nullptr) passphrase = &empty_passphrase;
79+
// This redirection is necessary because the PasswordCallback expects a
80+
// pointer to a pointer to the passphrase ByteSource to allow passing in
81+
// const ByteSources.
82+
const ByteSource* pass_ptr = passphrase;
83+
EVPKeyPointer key(
84+
PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback, &pass_ptr));
85+
if (!key) return PrivateKeyResult::kParseError;
86+
if (!SSL_CTX_use_PrivateKey(ctx, key.get()))
87+
return PrivateKeyResult::kApplyError;
88+
return PrivateKeyResult::kSuccess;
89+
}
90+
91+
bool UsePrivateKey(SSL_CTX* ctx, const KeyObjectData& key) {
92+
if (key.GetKeyType() != KeyType::kKeyTypePrivate) return false;
93+
return SSL_CTX_use_PrivateKey(ctx, key.GetAsymmetricKey().get());
94+
}
95+
96+
} // namespace node::crypto

src/crypto/crypto_tls_context.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#ifndef SRC_CRYPTO_CRYPTO_TLS_CONTEXT_H_
2+
#define SRC_CRYPTO_CRYPTO_TLS_CONTEXT_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "ncrypto.h"
7+
8+
namespace node {
9+
10+
class Environment;
11+
12+
namespace crypto {
13+
14+
class ByteSource;
15+
class KeyObjectData;
16+
17+
// Return a store that may be modified by this SSL_CTX. If the context uses
18+
// Node's shared root store, replace it with a private copy first.
19+
X509_STORE* GetOrCreateOwnedCertStore(Environment* env,
20+
SSL_CTX* ctx,
21+
X509_STORE** cache = nullptr);
22+
23+
// Attach Node's process-wide default root store to the SSL_CTX.
24+
void UseDefaultRootCertStore(Environment* env, SSL_CTX* ctx);
25+
26+
// Add every PEM certificate in |bio| to the context's certificate store and
27+
// acceptable-client-CA list. Returns the number of certificates read.
28+
size_t AddCACertificates(Environment* env,
29+
SSL_CTX* ctx,
30+
const ncrypto::BIOPointer& bio,
31+
X509_STORE** cache = nullptr);
32+
33+
// Add one PEM CRL and enable CRL checking.
34+
bool AddCRL(Environment* env,
35+
SSL_CTX* ctx,
36+
const ncrypto::BIOPointer& bio,
37+
X509_STORE** cache = nullptr);
38+
39+
enum class PrivateKeyResult {
40+
kSuccess,
41+
kParseError,
42+
kApplyError,
43+
};
44+
45+
PrivateKeyResult UsePrivateKey(SSL_CTX* ctx,
46+
const ncrypto::BIOPointer& bio,
47+
const ByteSource* passphrase = nullptr);
48+
49+
bool UsePrivateKey(SSL_CTX* ctx, const KeyObjectData& key);
50+
51+
int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
52+
ncrypto::BIOPointer&& in,
53+
ncrypto::X509Pointer* cert,
54+
ncrypto::X509Pointer* issuer);
55+
56+
} // namespace crypto
57+
} // namespace node
58+
59+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
60+
#endif // SRC_CRYPTO_CRYPTO_TLS_CONTEXT_H_

0 commit comments

Comments
 (0)