forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtlscontext.h
More file actions
343 lines (275 loc) · 13 KB
/
Copy pathtlscontext.h
File metadata and controls
343 lines (275 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#pragma once
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include <base_object.h>
#include <crypto/crypto_context.h>
#include <crypto/crypto_keys.h>
#include <memory_tracker.h>
#include <ncrypto.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <ngtcp2/ngtcp2_crypto_ossl.h>
#include <unordered_map>
#include "bindingdata.h"
#include "data.h"
#include "defs.h"
#include "sessionticket.h"
namespace node::quic {
class Session;
class TLSContext;
#if OPENSSL_IS_BORING
static_assert(false,
"This implementation of tlscontext relies on OpenSSL APIS "
"that are not available in BoringSSL. An alternative impl "
"using the equivalent BoringSSL APIs is possible but not "
"yet implemented.");
#endif
// The OSSLContext is a wrapper around the ngtcp2_crypto_ossl_ctx that
// holds the state for the OpenSSL TLS session. The ngtcp2_crypto_ossl_ctx
// is required by ngtcp2's adapter for OpenSSL.
class OSSLContext final {
public:
OSSLContext();
~OSSLContext();
DISALLOW_COPY_AND_MOVE(OSSLContext)
operator SSL*() const;
operator ngtcp2_crypto_ossl_ctx*() const;
void Initialize(SSL* ssl,
ngtcp2_crypto_conn_ref* ref,
ngtcp2_conn* connection,
SSL_CTX* ssl_ctx);
std::string_view get_cipher_name() const;
std::string get_selected_alpn() const;
std::string_view get_negotiated_group() const;
bool set_alpn_protocols(std::string_view protocols) const;
bool set_hostname(std::string_view hostname) const;
bool set_verify_hostname(std::string_view hostname) const;
bool set_early_data_enabled() const;
bool set_transport_params(const ngtcp2_vec& tp) const;
bool get_early_data_accepted() const;
bool get_early_data_rejected() const;
bool get_early_data_attempted() const;
// Sets the session ticket for 0-RTT resumption. Returns true if the
// ticket was set successfully and the ticket supports early data.
bool set_session_ticket(const ncrypto::SSLSessionPointer& ticket);
bool ConfigureServer() const;
bool ConfigureClient() const;
void reset();
inline operator bool() const { return ctx_ != nullptr; }
private:
ngtcp2_crypto_ossl_ctx* ctx_;
ngtcp2_conn* connection_ = nullptr;
};
// Every QUIC Session has exactly one TLSSession that maintains the state
// of the TLS handshake and negotiated keys after the handshake has been
// completed. It is separated out from the main Session class only as a
// convenience to help make the code more maintainable and understandable.
// A TLSSession is created from a TLSContext and maintains a reference to
// the context.
class TLSSession final : public MemoryRetainer {
public:
// TODO(@jasnell): In order to support using QUIC with host embedders
// like Electron, we need to abstract the TLSContext and TLSSession
// more so that they can use either the OpenSSL 3.5 or BoringSSL APIs.
// This can be done by creatig an internal TLSSession::Impl and
// TLSContext::Impl abstractions that can be implemented using either
// of the two APIs, keeping the TLSSession and TLSContext APIs the same
// for each.
// Gets the TLSSession from the SSL pointer app data.
static const TLSSession& From(const SSL* ssl);
// The constructor is public in order to satisfy the call to std::make_unique
// in TLSContext::NewSession. It should not be called directly.
TLSSession(Session* session,
std::shared_ptr<TLSContext> context,
const std::optional<SessionTicket>& maybeSessionTicket);
DISALLOW_COPY_AND_MOVE(TLSSession)
inline bool is_valid() const { return ossl_context_; }
inline operator bool() const { return is_valid(); }
inline Session& session() const { return *session_; }
inline TLSContext& context() const { return *context_; }
// Returns true if the handshake has been completed and early data was
// accepted by the TLS session. This will assert if the handshake has
// not been completed.
bool early_data_was_accepted() const;
bool early_data_was_rejected() const;
bool early_data_was_attempted() const;
v8::MaybeLocal<v8::Object> cert(Environment* env) const;
v8::MaybeLocal<v8::Object> peer_cert(Environment* env) const;
v8::MaybeLocal<v8::Object> ephemeral_key(Environment* env) const;
v8::MaybeLocal<v8::Value> cipher_name(Environment* env) const;
v8::MaybeLocal<v8::Value> cipher_version(Environment* env) const;
// The SNI (server name) negotiated for the session
const std::string_view servername() const;
// The ALPN (protocol name) negotiated for the session
const std::string protocol() const;
// Triggers key update to begin. This will fail and return false if either a
// previous key update is in progress or if the initial handshake has not yet
// been confirmed.
bool InitiateKeyUpdate();
struct PeerIdentityValidationError {
v8::MaybeLocal<v8::Value> reason;
v8::MaybeLocal<v8::Value> code;
};
// Checks the peer identity against the configured CA and CRL. If the peer
// certificate is valid, std::nullopt is returned. Otherwise a
// PeerIdentityValidationError is returned with the reason and code for the
// failure. If there was an error setting the reason or code, the fields
// will be empty but the PeerIdentityValidationError will still be returned.
// This is an indication that an exception has been scheduled by v8.
std::optional<PeerIdentityValidationError> VerifyPeerIdentity(
Environment* env);
inline std::string_view validation_error() const { return validation_error_; }
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(TLSSession)
SET_SELF_SIZE(TLSSession)
private:
operator SSL*() const;
void Initialize(const std::optional<SessionTicket>& maybeSessionTicket);
static ngtcp2_conn* connection(ngtcp2_crypto_conn_ref* ref);
ngtcp2_crypto_conn_ref ref_;
std::shared_ptr<TLSContext> context_;
OSSLContext ossl_context_;
Session* session_;
ncrypto::BIOPointer bio_trace_;
std::string validation_error_ = "";
};
// The TLSContext is used to create a TLSSession. For the client, there is
// typically only a single TLSContext for each TLSSession. For the server,
// there is a single TLSContext for the server and a TLSSession for every
// QUIC session created by that server.
class TLSContext final : public MemoryRetainer,
public std::enable_shared_from_this<TLSContext> {
public:
static constexpr auto DEFAULT_CIPHERS = "TLS_AES_128_GCM_SHA256:"
"TLS_AES_256_GCM_SHA384:"
"TLS_CHACHA20_POLY1305_"
"SHA256:TLS_AES_128_CCM_SHA256";
static constexpr auto DEFAULT_GROUPS = "X25519:P-256:P-384:P-521";
struct Options final : public MemoryRetainer {
// The SNI servername to use for this session. This option is only used by
// the client.
std::string servername = "localhost";
// The ALPN protocol identifier(s) in wire format (length-prefixed,
// concatenated). For clients this is a single entry. For servers
// this may contain multiple entries in preference order.
std::string alpn = NGHTTP3_ALPN_H3;
// The list of TLS ciphers to use for this session.
std::string ciphers = DEFAULT_CIPHERS;
// The list of TLS groups to use for this session.
std::string groups = DEFAULT_GROUPS;
// When true, enables keylog output for the session.
bool keylog = false;
// When true, the peer certificate is verified against the list of supplied
// CA. If verification fails, the connection will be refused. When set,
// instructs the server session to request a client auth certificate. This
// option is only used by the server side.
bool verify_client = false;
// When true (the default), client certificates that fail chain
// validation are rejected during the handshake. When false, the
// handshake completes and the validation result is passed to JS
// via the handshake callback for the application to decide.
// This option is only used by the server side.
bool reject_unauthorized = true;
// When true, the client will set SSL_VERIFY_PEER so that OpenSSL
// aborts the handshake if the server's certificate fails validation.
// This is the "strict" verify_peer mode. When false (the default),
// the handshake completes regardless and VerifyPeerIdentity is
// called after to surface errors to JS. This option is only used
// by the client side.
bool verify_peer_strict = false;
// When true, OpenSSL verifies that the server's certificate matches
// the servername (hostname verification via SSL_set1_host). Should
// be true for 'strict' and 'auto' verifyPeer modes, false for
// 'manual'. Without this, a valid certificate for any domain would
// be accepted. This option is only used by the client side.
bool verify_hostname = false;
// When true (the default), the server accepts 0-RTT early data
// from clients with valid session tickets. When false, early data
// is disabled and clients must complete a full handshake before
// sending application data. Disabling early data prevents replay
// attacks at the cost of an additional round trip.
// This option is only used by the server side.
bool enable_early_data = true;
// When true, enables TLS tracing for the session. This should only be used
// for debugging.
// JavaScript option name "tlsTrace".
bool enable_tls_trace = false;
// When true, causes the private key passed in for the session to be
// verified.
// JavaScript option name "verifyPrivateKey"
bool verify_private_key = false;
// The TLS private key(s) to use for this session.
// JavaScript option name "keys"
std::vector<crypto::KeyObjectData> keys;
// Collection of certificates to use for this session.
// JavaScript option name "certs"
std::vector<Store> certs;
// Optional certificate authority overrides to use.
// JavaScript option name "ca"
std::vector<Store> ca;
// Optional certificate revocation lists to use.
// JavaScript option name "crl"
std::vector<Store> crl;
// The port to advertise in ORIGIN frames for this hostname.
// Defaults to 443 (the standard HTTPS port). Only relevant for
// server-side SNI entries used with HTTP/3.
uint16_t port = 443;
// Whether this hostname should be included in ORIGIN frames.
// Only relevant for server-side SNI entries.
bool authoritative = true;
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(TLSContext::Options)
SET_SELF_SIZE(Options)
// The default TLS configuration.
static const Options kDefault;
static v8::Maybe<Options> From(Environment* env,
v8::Local<v8::Value> value);
std::string ToString() const;
};
static std::shared_ptr<TLSContext> CreateClient(Environment* env,
const Options& options);
static std::shared_ptr<TLSContext> CreateServer(Environment* env,
const Options& options);
TLSContext(Environment* env, Side side, const Options& options);
DISALLOW_COPY_AND_MOVE(TLSContext)
// Each QUIC Session has exactly one TLSSession. Each TLSSession maintains
// a reference to the TLSContext used to create it.
std::unique_ptr<TLSSession> NewSession(
Session* session, const std::optional<SessionTicket>& maybeSessionTicket);
bool AddSNIContext(Environment* env,
const std::string& hostname,
const Options& options);
bool SetSNIContexts(Environment* env,
const std::unordered_map<std::string, Options>& entries);
inline Side side() const { return side_; }
inline const Options& options() const { return options_; }
inline operator bool() const { return ctx_ != nullptr; }
inline operator const ncrypto::SSLCtxPointer&() const { return ctx_; }
inline const std::string_view validation_error() const {
return validation_error_;
}
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(TLSContext)
SET_SELF_SIZE(TLSContext)
private:
ncrypto::SSLCtxPointer Initialize(Environment* env);
operator SSL_CTX*() const;
static void OnKeylog(const SSL* ssl, const char* line);
static int OnNewSession(SSL* ssl, SSL_SESSION* session);
static int OnSelectAlpn(SSL* ssl,
const unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen,
void* arg);
static int OnVerifyClientCertificate(int preverify_ok, X509_STORE_CTX* ctx);
static int OnSNI(SSL* ssl, int* ad, void* arg);
Side side_;
Options options_;
ncrypto::X509Pointer cert_;
ncrypto::X509Pointer issuer_;
std::string validation_error_ = "";
ncrypto::SSLCtxPointer ctx_;
std::unordered_map<std::string, std::shared_ptr<TLSContext>> sni_contexts_;
friend class TLSSession;
};
} // namespace node::quic
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS