/src/node/src/quic/sessionticket.cc
Line | Count | Source |
1 | | #if HAVE_OPENSSL |
2 | | #include "guard.h" |
3 | | #ifndef OPENSSL_NO_QUIC |
4 | | #include "sessionticket.h" |
5 | | #include <env-inl.h> |
6 | | #include <memory_tracker-inl.h> |
7 | | #include <ngtcp2/ngtcp2_crypto.h> |
8 | | #include <node_buffer.h> |
9 | | #include <node_errors.h> |
10 | | |
11 | | namespace node { |
12 | | |
13 | | using v8::ArrayBufferView; |
14 | | using v8::Just; |
15 | | using v8::Local; |
16 | | using v8::Maybe; |
17 | | using v8::MaybeLocal; |
18 | | using v8::Nothing; |
19 | | using v8::Object; |
20 | | using v8::Value; |
21 | | using v8::ValueDeserializer; |
22 | | using v8::ValueSerializer; |
23 | | |
24 | | namespace quic { |
25 | | |
26 | | namespace { |
27 | 0 | SessionTicket::AppData::Source* GetAppDataSource(SSL* ssl) { |
28 | 0 | ngtcp2_crypto_conn_ref* ref = |
29 | 0 | static_cast<ngtcp2_crypto_conn_ref*>(SSL_get_app_data(ssl)); |
30 | 0 | if (ref != nullptr && ref->user_data != nullptr) { |
31 | 0 | return static_cast<SessionTicket::AppData::Source*>(ref->user_data); |
32 | 0 | } |
33 | 0 | return nullptr; |
34 | 0 | } |
35 | | } // namespace |
36 | | |
37 | | SessionTicket::SessionTicket(Store&& ticket, Store&& transport_params) |
38 | 0 | : ticket_(std::move(ticket)), |
39 | 0 | transport_params_(std::move(transport_params)) {} |
40 | | |
41 | | Maybe<SessionTicket> SessionTicket::FromV8Value(Environment* env, |
42 | 0 | Local<Value> value) { |
43 | 0 | if (!value->IsArrayBufferView()) { |
44 | 0 | THROW_ERR_INVALID_ARG_TYPE(env, "The ticket must be an ArrayBufferView."); |
45 | 0 | return Nothing<SessionTicket>(); |
46 | 0 | } |
47 | | |
48 | 0 | Store content; |
49 | 0 | if (!Store::From(value.As<ArrayBufferView>()).To(&content)) { |
50 | 0 | return Nothing<SessionTicket>(); |
51 | 0 | } |
52 | 0 | ngtcp2_vec vec = content; |
53 | |
|
54 | 0 | ValueDeserializer des(env->isolate(), vec.base, vec.len); |
55 | |
|
56 | 0 | if (des.ReadHeader(env->context()).IsNothing()) { |
57 | 0 | THROW_ERR_INVALID_ARG_VALUE(env, "The ticket format is invalid."); |
58 | 0 | return Nothing<SessionTicket>(); |
59 | 0 | } |
60 | | |
61 | 0 | Local<Value> ticket; |
62 | 0 | Local<Value> transport_params; |
63 | |
|
64 | 0 | if (!des.ReadValue(env->context()).ToLocal(&ticket) || |
65 | 0 | !des.ReadValue(env->context()).ToLocal(&transport_params)) { |
66 | 0 | return Nothing<SessionTicket>(); |
67 | 0 | } |
68 | 0 | if (!ticket->IsArrayBufferView()) { |
69 | 0 | THROW_ERR_INVALID_ARG_TYPE(env, "The ticket must be an ArrayBufferView"); |
70 | 0 | return Nothing<SessionTicket>(); |
71 | 0 | } |
72 | 0 | if (!transport_params->IsArrayBufferView()) { |
73 | 0 | THROW_ERR_INVALID_ARG_TYPE( |
74 | 0 | env, "The transport parameters must be an ArrayBufferView"); |
75 | 0 | return Nothing<SessionTicket>(); |
76 | 0 | } |
77 | | |
78 | 0 | Store ticket_store; |
79 | 0 | Store transport_params_store; |
80 | 0 | if (!Store::From(ticket.As<ArrayBufferView>()).To(&ticket_store)) { |
81 | 0 | return Nothing<SessionTicket>(); |
82 | 0 | } |
83 | 0 | if (!Store::From(transport_params.As<ArrayBufferView>()) |
84 | 0 | .To(&transport_params_store)) { |
85 | 0 | return Nothing<SessionTicket>(); |
86 | 0 | } |
87 | | |
88 | 0 | return Just(SessionTicket(std::move(ticket_store), |
89 | 0 | std::move(transport_params_store))); |
90 | 0 | } |
91 | | |
92 | 0 | MaybeLocal<Object> SessionTicket::encode(Environment* env) const { |
93 | 0 | auto context = env->context(); |
94 | 0 | ValueSerializer ser(env->isolate()); |
95 | 0 | ser.WriteHeader(); |
96 | |
|
97 | 0 | if (ser.WriteValue(context, ticket_.ToUint8Array(env)).IsNothing() || |
98 | 0 | ser.WriteValue(context, transport_params_.ToUint8Array(env)) |
99 | 0 | .IsNothing()) { |
100 | 0 | return MaybeLocal<Object>(); |
101 | 0 | } |
102 | | |
103 | 0 | auto result = ser.Release(); |
104 | |
|
105 | 0 | return Buffer::New(env, reinterpret_cast<char*>(result.first), result.second); |
106 | 0 | } |
107 | | |
108 | 0 | const uv_buf_t SessionTicket::ticket() const { |
109 | 0 | return ticket_; |
110 | 0 | } |
111 | | |
112 | 0 | const ngtcp2_vec SessionTicket::transport_params() const { |
113 | 0 | return transport_params_; |
114 | 0 | } |
115 | | |
116 | 0 | void SessionTicket::MemoryInfo(MemoryTracker* tracker) const { |
117 | 0 | tracker->TrackField("ticket", ticket_); |
118 | 0 | tracker->TrackField("transport_params", transport_params_); |
119 | 0 | } |
120 | | |
121 | 0 | int SessionTicket::GenerateCallback(SSL* ssl, void* arg) { |
122 | 0 | AppData::Collect(ssl); |
123 | 0 | return 1; |
124 | 0 | } |
125 | | |
126 | | SSL_TICKET_RETURN SessionTicket::DecryptedCallback(SSL* ssl, |
127 | | SSL_SESSION* session, |
128 | | const unsigned char* keyname, |
129 | | size_t keyname_len, |
130 | | SSL_TICKET_STATUS status, |
131 | 0 | void* arg) { |
132 | 0 | switch (status) { |
133 | 0 | default: |
134 | 0 | return SSL_TICKET_RETURN_IGNORE; |
135 | 0 | case SSL_TICKET_EMPTY: |
136 | 0 | [[fallthrough]]; |
137 | 0 | case SSL_TICKET_NO_DECRYPT: |
138 | 0 | return SSL_TICKET_RETURN_IGNORE_RENEW; |
139 | 0 | case SSL_TICKET_SUCCESS_RENEW: |
140 | 0 | [[fallthrough]]; |
141 | 0 | case SSL_TICKET_SUCCESS: |
142 | 0 | return static_cast<SSL_TICKET_RETURN>(AppData::Extract(ssl)); |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | 0 | SessionTicket::AppData::AppData(SSL* ssl) : ssl_(ssl) {} |
147 | | |
148 | 0 | bool SessionTicket::AppData::Set(const uv_buf_t& data) { |
149 | 0 | if (set_ || data.base == nullptr || data.len == 0) return false; |
150 | 0 | set_ = true; |
151 | 0 | SSL_SESSION_set1_ticket_appdata(SSL_get0_session(ssl_), data.base, data.len); |
152 | 0 | return set_; |
153 | 0 | } |
154 | | |
155 | 0 | std::optional<const uv_buf_t> SessionTicket::AppData::Get() const { |
156 | 0 | uv_buf_t buf; |
157 | 0 | int ret = |
158 | 0 | SSL_SESSION_get0_ticket_appdata(SSL_get0_session(ssl_), |
159 | 0 | reinterpret_cast<void**>(&buf.base), |
160 | 0 | reinterpret_cast<size_t*>(&buf.len)); |
161 | 0 | if (ret != 1) return std::nullopt; |
162 | 0 | return buf; |
163 | 0 | } |
164 | | |
165 | 0 | void SessionTicket::AppData::Collect(SSL* ssl) { |
166 | 0 | AppData app_data(ssl); |
167 | 0 | if (auto source = GetAppDataSource(ssl)) { |
168 | 0 | source->CollectSessionTicketAppData(&app_data); |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | SessionTicket::AppData::Status SessionTicket::AppData::Extract(SSL* ssl) { |
173 | 0 | auto source = GetAppDataSource(ssl); |
174 | 0 | if (source != nullptr) { |
175 | 0 | AppData app_data(ssl); |
176 | 0 | return source->ExtractSessionTicketAppData(app_data); |
177 | 0 | } |
178 | 0 | return Status::TICKET_IGNORE; |
179 | 0 | } |
180 | | |
181 | | } // namespace quic |
182 | | } // namespace node |
183 | | |
184 | | #endif // OPENSSL_NO_QUIC |
185 | | #endif // HAVE_OPENSSL |