Coverage Report

Created: 2026-01-21 08:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/src/quic/tlscontext.cc
Line
Count
Source
1
#if HAVE_OPENSSL
2
#include "guard.h"
3
#ifndef OPENSSL_NO_QUIC
4
#include <async_wrap-inl.h>
5
#include <base_object-inl.h>
6
#include <crypto/crypto_util.h>
7
#include <debug_utils-inl.h>
8
#include <env-inl.h>
9
#include <memory_tracker-inl.h>
10
#include <ngtcp2/ngtcp2.h>
11
#include <ngtcp2/ngtcp2_crypto.h>
12
#include <ngtcp2/ngtcp2_crypto_ossl.h>
13
#include <node_sockaddr-inl.h>
14
#include <openssl/ssl.h>
15
#include <v8.h>
16
#include "bindingdata.h"
17
#include "defs.h"
18
#include "session.h"
19
#include "tlscontext.h"
20
#include "transportparams.h"
21
22
namespace node {
23
24
using ncrypto::BIOPointer;
25
using ncrypto::ClearErrorOnReturn;
26
using ncrypto::MarkPopErrorOnReturn;
27
using ncrypto::SSLCtxPointer;
28
using ncrypto::SSLPointer;
29
using ncrypto::SSLSessionPointer;
30
using ncrypto::X509Pointer;
31
using v8::ArrayBuffer;
32
using v8::Just;
33
using v8::Local;
34
using v8::Maybe;
35
using v8::MaybeLocal;
36
using v8::Nothing;
37
using v8::Object;
38
using v8::Undefined;
39
using v8::Value;
40
41
namespace quic {
42
43
// ============================================================================
44
45
namespace {
46
// Performance optimization recommended by ngtcp2. Need to investigate why
47
// this causes some tests to fail.
48
// auto _ = []() {
49
//   if (ngtcp2_crypto_ossl_init() != 0) {
50
//     assert(0);
51
//     abort();
52
//   }
53
54
//   return 0;
55
// }();
56
57
// Temporarily wraps an SSL pointer but does not take ownership.
58
// Use by a few of the TLSSession methods that need access to the SSL*
59
// pointer held by the OSSLContext but cannot take ownership of it.
60
class SSLPointerRef final {
61
 public:
62
0
  inline SSLPointerRef(SSL* ssl) : temp_(ssl) { CHECK(temp_); }
63
0
  inline ~SSLPointerRef() { release(); }
64
  DISALLOW_COPY_AND_MOVE(SSLPointerRef)
65
0
  inline operator const SSLPointer&() const { return temp_; }
66
0
  inline const SSLPointer* operator->() const { return &temp_; }
67
0
  inline const SSLPointer& operator*() const { return temp_; }
68
0
  inline void release() { temp_.release(); }
69
70
 private:
71
  SSLPointer temp_;
72
};
73
74
0
void EnableTrace(Environment* env, BIOPointer* bio, SSL* ssl) {
75
#if HAVE_SSL_TRACE
76
  static bool warn_trace_tls = true;
77
  if (warn_trace_tls) {
78
    warn_trace_tls = false;
79
    ProcessEmitWarning(env,
80
                       "Enabling --trace-tls can expose sensitive data in "
81
                       "the resulting log");
82
  }
83
  if (!*bio) {
84
    bio->reset(BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT));
85
    SSL_set_msg_callback(
86
        ssl,
87
        [](int write_p,
88
           int version,
89
           int content_type,
90
           const void* buf,
91
           size_t len,
92
           SSL* ssl,
93
           void* arg) -> void {
94
          MarkPopErrorOnReturn mark_pop_error_on_return;
95
          SSL_trace(write_p, version, content_type, buf, len, ssl, arg);
96
        });
97
    SSL_set_msg_callback_arg(ssl, bio->get());
98
  }
99
#endif
100
0
}
101
102
template <typename T, typename Opt, std::vector<T> Opt::*member>
103
bool SetOption(Environment* env,
104
               Opt* options,
105
               const Local<Object>& object,
106
0
               const Local<v8::String>& name) {
107
0
  Local<Value> value;
108
0
  if (!object->Get(env->context(), name).ToLocal(&value)) return false;
109
110
0
  if (value->IsUndefined()) return true;
111
112
  // The value can be either a single item or an array of items.
113
114
0
  if (value->IsArray()) {
115
0
    auto context = env->context();
116
0
    auto values = value.As<v8::Array>();
117
0
    uint32_t count = values->Length();
118
0
    for (uint32_t n = 0; n < count; n++) {
119
0
      Local<Value> item;
120
0
      if (!values->Get(context, n).ToLocal(&item)) {
121
0
        return false;
122
0
      }
123
0
      if constexpr (std::is_same<T, crypto::KeyObjectData>::value) {
124
0
        if (crypto::KeyObjectHandle::HasInstance(env, item)) {
125
0
          crypto::KeyObjectHandle* handle;
126
0
          ASSIGN_OR_RETURN_UNWRAP(&handle, item, false);
127
0
          (options->*member).push_back(handle->Data().addRef());
128
0
        } else {
129
0
          Utf8Value namestr(env->isolate(), name);
130
0
          THROW_ERR_INVALID_ARG_TYPE(
131
0
              env, "%s value must be a key object", namestr);
132
0
          return false;
133
0
        }
134
0
      } else if constexpr (std::is_same<T, Store>::value) {
135
0
        if (item->IsArrayBufferView()) {
136
0
          Store store;
137
0
          if (!Store::From(item.As<v8::ArrayBufferView>()).To(&store)) {
138
0
            return false;
139
0
          }
140
0
          (options->*member).push_back(std::move(store));
141
0
        } else if (item->IsArrayBuffer()) {
142
0
          Store store;
143
0
          if (!Store::From(item.As<ArrayBuffer>()).To(&store)) {
144
0
            return false;
145
0
          }
146
0
          (options->*member).push_back(std::move(store));
147
0
        } else {
148
0
          Utf8Value namestr(env->isolate(), name);
149
0
          THROW_ERR_INVALID_ARG_TYPE(
150
0
              env,
151
0
              "%s value must be an array buffer or array buffer view",
152
0
              *namestr);
153
0
          return false;
154
0
        }
155
0
      }
156
0
    }
157
0
  } else {
158
0
    if constexpr (std::is_same<T, crypto::KeyObjectData>::value) {
159
0
      if (crypto::KeyObjectHandle::HasInstance(env, value)) {
160
0
        crypto::KeyObjectHandle* handle;
161
0
        ASSIGN_OR_RETURN_UNWRAP(&handle, value, false);
162
0
        (options->*member).push_back(handle->Data().addRef());
163
0
      } else {
164
0
        Utf8Value namestr(env->isolate(), name);
165
0
        THROW_ERR_INVALID_ARG_TYPE(
166
0
            env, "%s value must be a key object", namestr);
167
0
        return false;
168
0
      }
169
0
    } else if constexpr (std::is_same<T, Store>::value) {
170
0
      if (value->IsArrayBufferView()) {
171
0
        Store store;
172
0
        if (!Store::From(value.As<v8::ArrayBufferView>()).To(&store)) {
173
0
          return false;
174
0
        }
175
0
        (options->*member).push_back(std::move(store));
176
0
      } else if (value->IsArrayBuffer()) {
177
0
        Store store;
178
0
        if (!Store::From(value.As<ArrayBuffer>()).To(&store)) {
179
0
          return false;
180
0
        }
181
0
        (options->*member).push_back(std::move(store));
182
0
      } else {
183
0
        Utf8Value namestr(env->isolate(), name);
184
0
        THROW_ERR_INVALID_ARG_TYPE(
185
0
            env,
186
0
            "%s value must be an array buffer or array buffer view",
187
0
            *namestr);
188
0
        return false;
189
0
      }
190
0
    }
191
0
  }
192
0
  return true;
193
0
}
Unexecuted instantiation: tlscontext.cc:_ZN4node4quic12_GLOBAL__N_19SetOptionINS_6crypto13KeyObjectDataENS0_10TLSContext7OptionsETnMT0_NSt3__16vectorIT_NS8_9allocatorISA_EEEEXadL_ZNS6_4keysEEEEEbPNS_11EnvironmentEPS7_RKN2v85LocalINSI_6ObjectEEERKNSJ_INSI_6StringEEE
Unexecuted instantiation: tlscontext.cc:_ZN4node4quic12_GLOBAL__N_19SetOptionINS0_5StoreENS0_10TLSContext7OptionsETnMT0_NSt3__16vectorIT_NS7_9allocatorIS9_EEEEXadL_ZNS5_5certsEEEEEbPNS_11EnvironmentEPS6_RKN2v85LocalINSH_6ObjectEEERKNSI_INSH_6StringEEE
Unexecuted instantiation: tlscontext.cc:_ZN4node4quic12_GLOBAL__N_19SetOptionINS0_5StoreENS0_10TLSContext7OptionsETnMT0_NSt3__16vectorIT_NS7_9allocatorIS9_EEEEXadL_ZNS5_2caEEEEEbPNS_11EnvironmentEPS6_RKN2v85LocalINSH_6ObjectEEERKNSI_INSH_6StringEEE
Unexecuted instantiation: tlscontext.cc:_ZN4node4quic12_GLOBAL__N_19SetOptionINS0_5StoreENS0_10TLSContext7OptionsETnMT0_NSt3__16vectorIT_NS7_9allocatorIS9_EEEEXadL_ZNS5_3crlEEEEEbPNS_11EnvironmentEPS6_RKN2v85LocalINSH_6ObjectEEERKNSI_INSH_6StringEEE
194
}  // namespace
195
196
0
OSSLContext::OSSLContext() {
197
0
  CHECK_EQ(ngtcp2_crypto_ossl_ctx_new(&ctx_, nullptr), 0);
198
0
}
199
200
0
OSSLContext::~OSSLContext() {
201
0
  reset();
202
0
}
203
204
0
void OSSLContext::reset() {
205
0
  if (ctx_) {
206
0
    SSL_set_app_data(*this, nullptr);
207
0
    ngtcp2_conn_set_tls_native_handle(connection_, nullptr);
208
0
    ngtcp2_crypto_ossl_ctx_del(ctx_);
209
0
    ctx_ = nullptr;
210
0
    connection_ = nullptr;
211
0
  }
212
0
}
213
214
0
OSSLContext::operator SSL*() const {
215
0
  return ngtcp2_crypto_ossl_ctx_get_ssl(ctx_);
216
0
}
217
218
0
OSSLContext::operator ngtcp2_crypto_ossl_ctx*() const {
219
0
  return ctx_;
220
0
}
221
222
void OSSLContext::Initialize(SSL* ssl,
223
                             ngtcp2_crypto_conn_ref* ref,
224
                             ngtcp2_conn* connection,
225
0
                             SSL_CTX* ssl_ctx) {
226
0
  CHECK(ssl);
227
0
  ngtcp2_crypto_ossl_ctx_set_ssl(ctx_, ssl);
228
0
  SSL_set_app_data(*this, ref);
229
  // TODO(@jasnell): Later when BoringSSL is also supported, the native
230
  // handle will be different. The ngtcp2_crypto_ossl.h impl requires
231
  // that the native handle be set to the ngtcp2_crypto_ossl_ctx. So
232
  // this will need to be updated to support both cases.
233
0
  ngtcp2_conn_set_tls_native_handle(connection, ctx_);
234
0
  connection_ = connection;
235
0
}
236
237
0
std::string OSSLContext::get_cipher_name() const {
238
0
  return SSL_get_cipher_name(*this);
239
0
}
240
241
0
std::string OSSLContext::get_selected_alpn() const {
242
0
  const unsigned char* alpn = nullptr;
243
0
  unsigned int len;
244
0
  SSL_get0_alpn_selected(*this, &alpn, &len);
245
0
  return std::string(alpn, alpn + len);
246
0
}
247
248
0
std::string_view OSSLContext::get_negotiated_group() const {
249
0
  auto name = SSL_get0_group_name(*this);
250
0
  if (name == nullptr) return "";
251
0
  return name;
252
0
}
253
254
0
bool OSSLContext::set_alpn_protocols(std::string_view protocols) const {
255
0
  return SSL_set_alpn_protos(
256
0
             *this,
257
0
             reinterpret_cast<const unsigned char*>(protocols.data()),
258
0
             protocols.size()) == 0;
259
0
}
260
261
0
bool OSSLContext::set_hostname(std::string_view hostname) const {
262
0
  if (!hostname.empty()) {
263
0
    SSL_set_tlsext_host_name(*this, hostname.data());
264
0
  } else {
265
0
    SSL_set_tlsext_host_name(*this, "localhost");
266
0
  }
267
0
  return true;
268
0
}
269
270
0
bool OSSLContext::set_early_data_enabled() const {
271
0
  return SSL_set_quic_tls_early_data_enabled(*this, 1) == 1;
272
0
}
273
274
0
bool OSSLContext::set_transport_params(const ngtcp2_vec& tp) const {
275
0
  return SSL_set_quic_tls_transport_params(*this, tp.base, tp.len) == 1;
276
0
}
277
278
0
bool OSSLContext::get_early_data_accepted() const {
279
0
  return SSL_get_early_data_status(*this) == SSL_EARLY_DATA_ACCEPTED;
280
0
}
281
282
0
bool OSSLContext::ConfigureServer() const {
283
0
  if (ngtcp2_crypto_ossl_configure_server_session(*this) != 0) return false;
284
0
  SSL_set_accept_state(*this);
285
0
  return set_early_data_enabled();
286
0
}
287
288
0
bool OSSLContext::ConfigureClient() const {
289
0
  if (ngtcp2_crypto_ossl_configure_client_session(*this) != 0) return false;
290
0
  SSL_set_connect_state(*this);
291
0
  return true;
292
0
}
293
294
// ============================================================================
295
296
0
std::shared_ptr<TLSContext> TLSContext::CreateClient(const Options& options) {
297
0
  return std::make_shared<TLSContext>(Side::CLIENT, options);
298
0
}
299
300
0
std::shared_ptr<TLSContext> TLSContext::CreateServer(const Options& options) {
301
0
  return std::make_shared<TLSContext>(Side::SERVER, options);
302
0
}
303
304
TLSContext::TLSContext(Side side, const Options& options)
305
0
    : side_(side), options_(options), ctx_(Initialize()) {}
306
307
0
TLSContext::operator SSL_CTX*() const {
308
0
  DCHECK(ctx_);
309
0
  return ctx_.get();
310
0
}
311
312
int TLSContext::OnSelectAlpn(SSL* ssl,
313
                             const unsigned char** out,
314
                             unsigned char* outlen,
315
                             const unsigned char* in,
316
                             unsigned int inlen,
317
0
                             void* arg) {
318
0
  static constexpr size_t kMaxAlpnLen = 255;
319
0
  auto& session = TLSSession::From(ssl);
320
321
0
  const auto& requested = session.context().options().protocol;
322
0
  if (requested.length() > kMaxAlpnLen) return SSL_TLSEXT_ERR_NOACK;
323
324
  // The Session supports exactly one ALPN identifier. If that does not match
325
  // any of the ALPN identifiers provided in the client request, then we fail
326
  // here. Note that this will not fail the TLS handshake, so we have to check
327
  // later if the ALPN matches the expected identifier or not.
328
  //
329
  // TODO(@jasnell): We might eventually want to support the ability to
330
  // negotiate multiple possible ALPN's on a single endpoint/session but for
331
  // now, we only support one.
332
0
  if (SSL_select_next_proto(
333
0
          const_cast<unsigned char**>(out),
334
0
          outlen,
335
0
          reinterpret_cast<const unsigned char*>(requested.data()),
336
0
          requested.length(),
337
0
          in,
338
0
          inlen) == OPENSSL_NPN_NO_OVERLAP) {
339
0
    Debug(&session.session(), "ALPN negotiation failed");
340
0
    return SSL_TLSEXT_ERR_NOACK;
341
0
  }
342
343
0
  Debug(&session.session(), "ALPN negotiation succeeded");
344
0
  return SSL_TLSEXT_ERR_OK;
345
0
}
346
347
0
int TLSContext::OnNewSession(SSL* ssl, SSL_SESSION* sess) {
348
0
  auto& session = TLSSession::From(ssl).session();
349
350
  // If there is nothing listening for the session ticket, do not bother.
351
0
  if (session.wants_session_ticket()) {
352
0
    Debug(&session, "Preparing TLS session resumption ticket");
353
354
    // Pre-fight to see how much space we need to allocate for the session
355
    // ticket.
356
0
    size_t size = i2d_SSL_SESSION(sess, nullptr);
357
358
    // If size is 0, the size is greater than our max, or there is not
359
    // enough memory to allocate the backing store, then we ignore it
360
    // and continue without emitting the sessionticket event.
361
0
    if (size > 0 && size <= crypto::SecureContext::kMaxSessionSize) {
362
0
      JS_TRY_ALLOCATE_BACKING_OR_RETURN(session.env(), ticket, size, 0);
363
0
      auto data = reinterpret_cast<unsigned char*>(ticket->Data());
364
0
      if (i2d_SSL_SESSION(sess, &data) > 0) {
365
0
        session.EmitSessionTicket(Store(std::move(ticket), size));
366
0
      }
367
0
    }
368
0
  }
369
370
0
  return 0;
371
0
}
372
373
0
void TLSContext::OnKeylog(const SSL* ssl, const char* line) {
374
0
  TLSSession::From(ssl).session().EmitKeylog(line);
375
0
}
376
377
int TLSContext::OnVerifyClientCertificate(int preverify_ok,
378
0
                                          X509_STORE_CTX* ctx) {
379
  // TODO(@jasnell): Implement the logic to verify the client certificate
380
0
  return 1;
381
0
}
382
383
std::unique_ptr<TLSSession> TLSContext::NewSession(
384
0
    Session* session, const std::optional<SessionTicket>& maybeSessionTicket) {
385
  // Passing a session ticket only makes sense with a client session.
386
0
  CHECK_IMPLIES(session->is_server(), !maybeSessionTicket.has_value());
387
0
  return std::make_unique<TLSSession>(
388
0
      session, shared_from_this(), maybeSessionTicket);
389
0
}
390
391
0
SSLCtxPointer TLSContext::Initialize() {
392
0
  SSLCtxPointer ctx;
393
0
  switch (side_) {
394
0
    case Side::SERVER: {
395
0
      static constexpr unsigned char kSidCtx[] = "Node.js QUIC Server";
396
0
      ctx = SSLCtxPointer::NewServer();
397
0
      if (!ctx) [[unlikely]] {
398
0
        validation_error_ = "Failed to create SSL_CTX for server";
399
0
        return {};
400
0
      }
401
402
0
      if (SSL_CTX_set_max_early_data(ctx.get(), UINT32_MAX) != 1) {
403
0
        validation_error_ = "Failed to set max early data";
404
0
        return {};
405
0
      }
406
0
      SSL_CTX_set_options(ctx.get(),
407
0
                          (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
408
0
                              SSL_OP_SINGLE_ECDH_USE |
409
0
                              SSL_OP_CIPHER_SERVER_PREFERENCE |
410
0
                              SSL_OP_NO_ANTI_REPLAY);
411
412
0
      SSL_CTX_set_mode(ctx.get(), SSL_MODE_RELEASE_BUFFERS);
413
0
      SSL_CTX_set_alpn_select_cb(ctx.get(), OnSelectAlpn, this);
414
415
0
      if (SSL_CTX_set_session_id_context(
416
0
              ctx.get(), kSidCtx, sizeof(kSidCtx) - 1) != 1) {
417
0
        validation_error_ = "Failed to set session ID context";
418
0
        return {};
419
0
      }
420
421
0
      if (options_.verify_client) [[likely]] {
422
0
        SSL_CTX_set_verify(ctx.get(),
423
0
                           SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
424
0
                               SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
425
0
                           OnVerifyClientCertificate);
426
0
      }
427
428
      // TODO(@jasnell): There's a bug int the GenerateCallback flow somewhere.
429
      // Need to update in order to support session tickets.
430
      // CHECK_EQ(SSL_CTX_set_session_ticket_cb(ctx.get(),
431
      //                                        SessionTicket::GenerateCallback,
432
      //                                        SessionTicket::DecryptedCallback,
433
      //                                        nullptr),
434
      //          1);
435
0
      break;
436
0
    }
437
0
    case Side::CLIENT: {
438
0
      ctx = SSLCtxPointer::NewClient();
439
440
0
      SSL_CTX_set_session_cache_mode(
441
0
          ctx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
442
0
      SSL_CTX_sess_set_new_cb(ctx.get(), OnNewSession);
443
0
      break;
444
0
    }
445
0
  }
446
447
0
  SSL_CTX_set_default_verify_paths(ctx.get());
448
0
  SSL_CTX_set_keylog_callback(ctx.get(), OnKeylog);
449
450
0
  if (SSL_CTX_set_ciphersuites(ctx.get(), options_.ciphers.c_str()) != 1) {
451
0
    validation_error_ = "Invalid cipher suite";
452
0
    return SSLCtxPointer();
453
0
  }
454
455
0
  if (SSL_CTX_set1_groups_list(ctx.get(), options_.groups.c_str()) != 1) {
456
0
    validation_error_ = "Invalid cipher groups";
457
0
    return SSLCtxPointer();
458
0
  }
459
460
0
  {
461
0
    ClearErrorOnReturn clear_error_on_return;
462
0
    if (options_.ca.empty()) {
463
0
      auto store = crypto::GetOrCreateRootCertStore();
464
0
      X509_STORE_up_ref(store);
465
0
      SSL_CTX_set_cert_store(ctx.get(), store);
466
0
    } else {
467
0
      for (const auto& ca : options_.ca) {
468
0
        uv_buf_t buf = ca;
469
0
        if (buf.len == 0) {
470
0
          auto store = crypto::GetOrCreateRootCertStore();
471
0
          X509_STORE_up_ref(store);
472
0
          SSL_CTX_set_cert_store(ctx.get(), store);
473
0
        } else {
474
0
          BIOPointer bio = crypto::NodeBIO::NewFixed(buf.base, buf.len);
475
0
          CHECK(bio);
476
0
          X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx.get());
477
0
          while (
478
0
              auto x509 = X509Pointer(PEM_read_bio_X509_AUX(
479
0
                  bio.get(), nullptr, crypto::NoPasswordCallback, nullptr))) {
480
0
            if (cert_store == crypto::GetOrCreateRootCertStore()) {
481
0
              cert_store = crypto::NewRootCertStore();
482
0
              SSL_CTX_set_cert_store(ctx.get(), cert_store);
483
0
            }
484
0
            CHECK_EQ(1, X509_STORE_add_cert(cert_store, x509.get()));
485
0
            CHECK_EQ(1, SSL_CTX_add_client_CA(ctx.get(), x509.get()));
486
0
          }
487
0
        }
488
0
      }
489
0
    }
490
0
  }
491
492
0
  {
493
0
    ClearErrorOnReturn clear_error_on_return;
494
0
    for (const auto& cert : options_.certs) {
495
0
      uv_buf_t buf = cert;
496
0
      if (buf.len > 0) {
497
0
        BIOPointer bio = crypto::NodeBIO::NewFixed(buf.base, buf.len);
498
0
        CHECK(bio);
499
0
        cert_.reset();
500
0
        issuer_.reset();
501
0
        if (crypto::SSL_CTX_use_certificate_chain(
502
0
                ctx.get(), std::move(bio), &cert_, &issuer_) == 0) {
503
0
          validation_error_ = "Invalid certificate";
504
0
          return SSLCtxPointer();
505
0
        }
506
0
      }
507
0
    }
508
0
  }
509
510
0
  {
511
0
    ClearErrorOnReturn clear_error_on_return;
512
0
    for (const auto& key : options_.keys) {
513
0
      if (key.GetKeyType() != crypto::KeyType::kKeyTypePrivate) {
514
0
        validation_error_ = "Invalid key";
515
0
        return SSLCtxPointer();
516
0
      }
517
0
      if (!SSL_CTX_use_PrivateKey(ctx.get(), key.GetAsymmetricKey().get())) {
518
0
        validation_error_ = "Invalid key";
519
0
        return SSLCtxPointer();
520
0
      }
521
0
    }
522
0
  }
523
524
0
  {
525
0
    ClearErrorOnReturn clear_error_on_return;
526
0
    for (const auto& crl : options_.crl) {
527
0
      uv_buf_t buf = crl;
528
0
      BIOPointer bio = crypto::NodeBIO::NewFixed(buf.base, buf.len);
529
0
      DeleteFnPtr<X509_CRL, X509_CRL_free> crlptr(PEM_read_bio_X509_CRL(
530
0
          bio.get(), nullptr, crypto::NoPasswordCallback, nullptr));
531
532
0
      if (!crlptr) {
533
0
        validation_error_ = "Invalid CRL";
534
0
        return SSLCtxPointer();
535
0
      }
536
537
0
      X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx.get());
538
0
      if (cert_store == crypto::GetOrCreateRootCertStore()) {
539
0
        cert_store = crypto::NewRootCertStore();
540
0
        SSL_CTX_set_cert_store(ctx.get(), cert_store);
541
0
      }
542
543
0
      CHECK_EQ(1, X509_STORE_add_crl(cert_store, crlptr.get()));
544
0
      CHECK_EQ(
545
0
          1,
546
0
          X509_STORE_set_flags(
547
0
              cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL));
548
0
    }
549
0
  }
550
551
0
  {
552
0
    ClearErrorOnReturn clear_error_on_return;
553
0
    if (options_.verify_private_key &&
554
0
        SSL_CTX_check_private_key(ctx.get()) != 1) {
555
0
      validation_error_ = "Invalid private key";
556
0
      return SSLCtxPointer();
557
0
    }
558
0
  }
559
560
0
  return ctx;
561
0
}
562
563
0
void TLSContext::MemoryInfo(MemoryTracker* tracker) const {
564
0
  tracker->TrackField("options", options_);
565
0
}
566
567
Maybe<TLSContext::Options> TLSContext::Options::From(Environment* env,
568
0
                                                     Local<Value> value) {
569
0
  if (value.IsEmpty()) {
570
0
    return Nothing<Options>();
571
0
  }
572
573
0
  Options options;
574
0
  auto& state = BindingData::Get(env);
575
576
0
  if (value->IsUndefined()) {
577
0
    return Just(kDefault);
578
0
  }
579
580
0
  if (!value->IsObject()) {
581
0
    THROW_ERR_INVALID_ARG_TYPE(env, "tls options must be an object");
582
0
    return Nothing<Options>();
583
0
  }
584
585
0
  auto params = value.As<Object>();
586
587
0
#define SET_VECTOR(Type, name)                                                 \
588
0
  SetOption<Type, TLSContext::Options, &TLSContext::Options::name>(            \
589
0
      env, &options, params, state.name##_string())
590
591
0
#define SET(name)                                                              \
592
0
  SetOption<TLSContext::Options, &TLSContext::Options::name>(                  \
593
0
      env, &options, params, state.name##_string())
594
595
0
  if (!SET(verify_client) || !SET(enable_tls_trace) || !SET(protocol) ||
596
0
      !SET(servername) || !SET(ciphers) || !SET(groups) ||
597
0
      !SET(verify_private_key) || !SET(keylog) ||
598
0
      !SET_VECTOR(crypto::KeyObjectData, keys) || !SET_VECTOR(Store, certs) ||
599
0
      !SET_VECTOR(Store, ca) || !SET_VECTOR(Store, crl)) {
600
0
    return Nothing<Options>();
601
0
  }
602
603
0
  return Just<Options>(options);
604
0
}
605
606
0
std::string TLSContext::Options::ToString() const {
607
0
  DebugIndentScope indent;
608
0
  auto prefix = indent.Prefix();
609
0
  std::string res("{");
610
0
  res += prefix + "protocol: " + protocol;
611
0
  res += prefix + "servername: " + servername;
612
0
  res +=
613
0
      prefix + "keylog: " + (keylog ? std::string("yes") : std::string("no"));
614
0
  res += prefix + "verify client: " +
615
0
         (verify_client ? std::string("yes") : std::string("no"));
616
0
  res += prefix + "enable_tls_trace: " +
617
0
         (enable_tls_trace ? std::string("yes") : std::string("no"));
618
0
  res += prefix + "verify private key: " +
619
0
         (verify_private_key ? std::string("yes") : std::string("no"));
620
0
  res += prefix + "ciphers: " + ciphers;
621
0
  res += prefix + "groups: " + groups;
622
0
  res += prefix + "keys: " + std::to_string(keys.size());
623
0
  res += prefix + "certs: " + std::to_string(certs.size());
624
0
  res += prefix + "ca: " + std::to_string(ca.size());
625
0
  res += prefix + "crl: " + std::to_string(crl.size());
626
0
  res += indent.Close();
627
0
  return res;
628
0
}
629
630
0
void TLSContext::Options::MemoryInfo(MemoryTracker* tracker) const {
631
0
  tracker->TrackField("keys", keys);
632
0
  tracker->TrackField("certs", certs);
633
0
  tracker->TrackField("ca", ca);
634
0
  tracker->TrackField("crl", crl);
635
0
}
636
637
const TLSContext::Options TLSContext::Options::kDefault = {};
638
639
// ============================================================================
640
641
0
const TLSSession& TLSSession::From(const SSL* ssl) {
642
0
  auto ref = static_cast<ngtcp2_crypto_conn_ref*>(SSL_get_app_data(ssl));
643
0
  CHECK_NOT_NULL(ref);
644
0
  return *static_cast<TLSSession*>(ref->user_data);
645
0
}
646
647
TLSSession::TLSSession(Session* session,
648
                       std::shared_ptr<TLSContext> context,
649
                       const std::optional<SessionTicket>& maybeSessionTicket)
650
0
    : ref_({connection, this}),
651
0
      context_(std::move(context)),
652
0
      session_(session) {
653
0
  Debug(session_, "Created new TLS session for %s", session->config().dcid);
654
0
  Initialize(maybeSessionTicket);
655
0
  if (!ossl_context_) [[unlikely]] {
656
0
    Debug(session_,
657
0
          "Failed to initialize TLS session: %s",
658
0
          validation_error_.empty() ? "unknown error" : validation_error_);
659
0
  }
660
0
}
661
662
0
TLSSession::operator SSL*() const {
663
0
  return ossl_context_;
664
0
}
665
666
0
bool TLSSession::early_data_was_accepted() const {
667
0
  CHECK_NE(ngtcp2_conn_get_handshake_completed(*session_), 0);
668
0
  return ossl_context_.get_early_data_accepted();
669
0
}
670
671
void TLSSession::Initialize(
672
0
    const std::optional<SessionTicket>& maybeSessionTicket) {
673
0
  auto& ctx = context();
674
0
  auto& options = ctx.options();
675
0
  auto ssl = SSLPointer::New(ctx);
676
0
  if (!ssl) [[unlikely]] {
677
0
    validation_error_ = "Failed to create SSL session";
678
0
    ossl_context_.reset();
679
0
    return;
680
0
  }
681
682
  // Enable tracing if the `--trace-tls` command line flag is used.
683
0
  if (session_->env()->options()->trace_tls || options.enable_tls_trace)
684
0
      [[unlikely]] {
685
0
    EnableTrace(session_->env(), &bio_trace_, ssl);
686
0
  }
687
688
0
  ossl_context_.Initialize(ssl.release(), &ref_, session(), ctx);
689
690
0
  switch (ctx.side()) {
691
0
    case Side::SERVER: {
692
0
      if (!ossl_context_.ConfigureServer()) [[unlikely]] {
693
0
        validation_error_ = "Failed to configure server session";
694
0
        ossl_context_.reset();
695
0
        return;
696
0
      }
697
0
      break;
698
0
    }
699
0
    case Side::CLIENT: {
700
0
      if (!ossl_context_.ConfigureClient()) [[unlikely]] {
701
0
        validation_error_ = "Failed to configure client session";
702
0
        ossl_context_.reset();
703
0
        return;
704
0
      };
705
706
0
      if (!ossl_context_.set_alpn_protocols(options.protocol)) {
707
0
        validation_error_ = "Failed to set ALPN protocols";
708
0
        ossl_context_.reset();
709
0
        return;
710
0
      }
711
712
0
      if (!ossl_context_.set_hostname(options.servername)) {
713
0
        validation_error_ = "Failed to set server name";
714
0
        ossl_context_.reset();
715
0
        return;
716
0
      }
717
718
0
      if (maybeSessionTicket.has_value()) {
719
0
        auto sessionTicket = maybeSessionTicket.value();
720
0
        uv_buf_t buf = sessionTicket.ticket();
721
0
        SSLSessionPointer ticket = crypto::GetTLSSession(
722
0
            reinterpret_cast<unsigned char*>(buf.base), buf.len);
723
724
        // The early data will just be ignored if it's invalid.
725
0
        if (ssl.setSession(ticket) &&
726
0
            SSL_SESSION_get_max_early_data(ticket.get()) != 0) {
727
0
          ngtcp2_vec rtp = sessionTicket.transport_params();
728
0
          if (ngtcp2_conn_decode_and_set_0rtt_transport_params(
729
0
                  *session_, rtp.base, rtp.len) == 0) {
730
0
            if (!ossl_context_.set_early_data_enabled()) {
731
0
              validation_error_ = "Failed to enable early data";
732
0
              ossl_context_.reset();
733
0
              return;
734
0
            }
735
0
            session_->SetStreamOpenAllowed();
736
0
          }
737
0
        }
738
0
      }
739
740
0
      break;
741
0
    }
742
0
  }
743
744
0
  TransportParams tp(ngtcp2_conn_get_local_transport_params(*session_));
745
0
  Store store = tp.Encode(session_->env());
746
0
  if (store && store.length() > 0) {
747
0
    if (!ossl_context_.set_transport_params(store)) {
748
0
      validation_error_ = "Failed to set transport parameters";
749
0
      ossl_context_.reset();
750
0
      return;
751
0
    }
752
0
  }
753
0
}
754
755
std::optional<TLSSession::PeerIdentityValidationError>
756
0
TLSSession::VerifyPeerIdentity(Environment* env) {
757
  // We are just temporarily wrapping the ssl, not taking ownership.
758
0
  SSLPointerRef ssl(ossl_context_);
759
0
  int err = ssl->verifyPeerCertificate().value_or(X509_V_ERR_UNSPECIFIED);
760
0
  if (err == X509_V_OK) return std::nullopt;
761
0
  Local<Value> reason;
762
0
  Local<Value> code;
763
0
  if (!crypto::GetValidationErrorReason(env, err).ToLocal(&reason) ||
764
0
      !crypto::GetValidationErrorCode(env, err).ToLocal(&code)) {
765
    // Getting the validation error details failed. We'll return a value but
766
    // the fields will be empty.
767
0
    return PeerIdentityValidationError{};
768
0
  }
769
0
  return PeerIdentityValidationError{reason, code};
770
0
}
771
772
0
MaybeLocal<Object> TLSSession::cert(Environment* env) const {
773
0
  SSLPointerRef ssl(ossl_context_);
774
0
  return crypto::X509Certificate::GetCert(env, ssl);
775
0
}
776
777
0
MaybeLocal<Object> TLSSession::peer_cert(Environment* env) const {
778
  // We are just temporarily wrapping the ssl, not taking ownership.
779
0
  SSLPointerRef ssl(ossl_context_);
780
0
  crypto::X509Certificate::GetPeerCertificateFlag flag =
781
0
      context_->side() == Side::SERVER
782
0
          ? crypto::X509Certificate::GetPeerCertificateFlag::SERVER
783
0
          : crypto::X509Certificate::GetPeerCertificateFlag::NONE;
784
0
  return crypto::X509Certificate::GetPeerCert(env, ssl, flag);
785
0
}
786
787
0
MaybeLocal<Object> TLSSession::ephemeral_key(Environment* env) const {
788
  // We are just temporarily wrapping the ssl, not taking ownership.
789
0
  SSLPointerRef ssl(ossl_context_);
790
0
  return crypto::GetEphemeralKey(env, ssl);
791
0
}
792
793
0
MaybeLocal<Value> TLSSession::cipher_name(Environment* env) const {
794
0
  CHECK(ossl_context_);
795
0
  auto name = ossl_context_.get_cipher_name();
796
0
  return OneByteString(env->isolate(), name);
797
0
}
798
799
0
MaybeLocal<Value> TLSSession::cipher_version(Environment* env) const {
800
0
  SSLPointerRef ssl(ossl_context_);
801
0
  auto version = ssl->getCipherVersion();
802
0
  if (!version.has_value()) return Undefined(env->isolate());
803
0
  return OneByteString(env->isolate(), version.value());
804
0
}
805
806
0
const std::string_view TLSSession::servername() const {
807
0
  SSLPointerRef ssl(ossl_context_);
808
0
  return ssl->getServerName().value_or(std::string_view());
809
0
}
810
811
0
const std::string TLSSession::protocol() const {
812
0
  CHECK(ossl_context_);
813
0
  return ossl_context_.get_selected_alpn();
814
0
}
815
816
0
bool TLSSession::InitiateKeyUpdate() {
817
0
  if (in_key_update_) return false;
818
0
  auto leave = OnScopeLeave([this] { in_key_update_ = false; });
819
0
  in_key_update_ = true;
820
821
0
  Debug(session_, "Initiating key update");
822
0
  return ngtcp2_conn_initiate_key_update(*session_, uv_hrtime()) == 0;
823
0
}
824
825
0
ngtcp2_conn* TLSSession::connection(ngtcp2_crypto_conn_ref* ref) {
826
0
  CHECK_NOT_NULL(ref->user_data);
827
0
  return static_cast<TLSSession*>(ref->user_data)->session();
828
0
}
829
830
0
void TLSSession::MemoryInfo(MemoryTracker* tracker) const {
831
0
  tracker->TrackField("context", context_);
832
0
}
833
834
}  // namespace quic
835
}  // namespace node
836
#endif  // OPENSSL_NO_QUIC
837
#endif  // HAVE_OPENSSL