11cb0ef41Sopenharmony_ci#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci#include "sessionticket.h"
41cb0ef41Sopenharmony_ci#include <env-inl.h>
51cb0ef41Sopenharmony_ci#include <memory_tracker-inl.h>
61cb0ef41Sopenharmony_ci#include <ngtcp2/ngtcp2_crypto.h>
71cb0ef41Sopenharmony_ci#include <node_buffer.h>
81cb0ef41Sopenharmony_ci#include <node_errors.h>
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_cinamespace node {
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ciusing v8::ArrayBufferView;
131cb0ef41Sopenharmony_ciusing v8::Just;
141cb0ef41Sopenharmony_ciusing v8::Local;
151cb0ef41Sopenharmony_ciusing v8::Maybe;
161cb0ef41Sopenharmony_ciusing v8::MaybeLocal;
171cb0ef41Sopenharmony_ciusing v8::Nothing;
181cb0ef41Sopenharmony_ciusing v8::Object;
191cb0ef41Sopenharmony_ciusing v8::Value;
201cb0ef41Sopenharmony_ciusing v8::ValueDeserializer;
211cb0ef41Sopenharmony_ciusing v8::ValueSerializer;
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_cinamespace quic {
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_cinamespace {
261cb0ef41Sopenharmony_ciSessionTicket::AppData::Source* GetAppDataSource(SSL* ssl) {
271cb0ef41Sopenharmony_ci  ngtcp2_crypto_conn_ref* ref =
281cb0ef41Sopenharmony_ci      static_cast<ngtcp2_crypto_conn_ref*>(SSL_get_app_data(ssl));
291cb0ef41Sopenharmony_ci  if (ref != nullptr && ref->user_data != nullptr) {
301cb0ef41Sopenharmony_ci    return static_cast<SessionTicket::AppData::Source*>(ref->user_data);
311cb0ef41Sopenharmony_ci  }
321cb0ef41Sopenharmony_ci  return nullptr;
331cb0ef41Sopenharmony_ci}
341cb0ef41Sopenharmony_ci}  // namespace
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ciSessionTicket::SessionTicket(Store&& ticket, Store&& transport_params)
371cb0ef41Sopenharmony_ci    : ticket_(std::move(ticket)),
381cb0ef41Sopenharmony_ci      transport_params_(std::move(transport_params)) {}
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ciMaybe<SessionTicket> SessionTicket::FromV8Value(Environment* env,
411cb0ef41Sopenharmony_ci                                                v8::Local<v8::Value> value) {
421cb0ef41Sopenharmony_ci  if (!value->IsArrayBufferView()) {
431cb0ef41Sopenharmony_ci    THROW_ERR_INVALID_ARG_TYPE(env, "The ticket must be an ArrayBufferView.");
441cb0ef41Sopenharmony_ci    return Nothing<SessionTicket>();
451cb0ef41Sopenharmony_ci  }
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  Store content(value.As<ArrayBufferView>());
481cb0ef41Sopenharmony_ci  ngtcp2_vec vec = content;
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci  ValueDeserializer des(env->isolate(), vec.base, vec.len);
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  if (des.ReadHeader(env->context()).IsNothing()) {
531cb0ef41Sopenharmony_ci    THROW_ERR_INVALID_ARG_VALUE(env, "The ticket format is invalid.");
541cb0ef41Sopenharmony_ci    return Nothing<SessionTicket>();
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  Local<Value> ticket;
581cb0ef41Sopenharmony_ci  Local<Value> transport_params;
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci  errors::TryCatchScope tryCatch(env);
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci  if (!des.ReadValue(env->context()).ToLocal(&ticket) ||
631cb0ef41Sopenharmony_ci      !des.ReadValue(env->context()).ToLocal(&transport_params) ||
641cb0ef41Sopenharmony_ci      !ticket->IsArrayBufferView() || !transport_params->IsArrayBufferView()) {
651cb0ef41Sopenharmony_ci    if (tryCatch.HasCaught()) {
661cb0ef41Sopenharmony_ci      // Any errors thrown we want to catch and suppress. The only
671cb0ef41Sopenharmony_ci      // error we want to expose to the user is that the ticket format
681cb0ef41Sopenharmony_ci      // is invalid.
691cb0ef41Sopenharmony_ci      if (!tryCatch.HasTerminated()) {
701cb0ef41Sopenharmony_ci        THROW_ERR_INVALID_ARG_VALUE(env, "The ticket format is invalid.");
711cb0ef41Sopenharmony_ci        tryCatch.ReThrow();
721cb0ef41Sopenharmony_ci      }
731cb0ef41Sopenharmony_ci      return Nothing<SessionTicket>();
741cb0ef41Sopenharmony_ci    }
751cb0ef41Sopenharmony_ci    THROW_ERR_INVALID_ARG_VALUE(env, "The ticket format is invalid.");
761cb0ef41Sopenharmony_ci    return Nothing<SessionTicket>();
771cb0ef41Sopenharmony_ci  }
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  return Just(SessionTicket(Store(ticket.As<ArrayBufferView>()),
801cb0ef41Sopenharmony_ci                            Store(transport_params.As<ArrayBufferView>())));
811cb0ef41Sopenharmony_ci}
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ciMaybeLocal<Object> SessionTicket::encode(Environment* env) const {
841cb0ef41Sopenharmony_ci  auto context = env->context();
851cb0ef41Sopenharmony_ci  ValueSerializer ser(env->isolate());
861cb0ef41Sopenharmony_ci  ser.WriteHeader();
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  if (ser.WriteValue(context, ticket_.ToUint8Array(env)).IsNothing() ||
891cb0ef41Sopenharmony_ci      ser.WriteValue(context, transport_params_.ToUint8Array(env))
901cb0ef41Sopenharmony_ci          .IsNothing()) {
911cb0ef41Sopenharmony_ci    return MaybeLocal<Object>();
921cb0ef41Sopenharmony_ci  }
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci  auto result = ser.Release();
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  return Buffer::New(env, reinterpret_cast<char*>(result.first), result.second);
971cb0ef41Sopenharmony_ci}
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ciconst uv_buf_t SessionTicket::ticket() const {
1001cb0ef41Sopenharmony_ci  return ticket_;
1011cb0ef41Sopenharmony_ci}
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ciconst ngtcp2_vec SessionTicket::transport_params() const {
1041cb0ef41Sopenharmony_ci  return transport_params_;
1051cb0ef41Sopenharmony_ci}
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_civoid SessionTicket::MemoryInfo(MemoryTracker* tracker) const {
1081cb0ef41Sopenharmony_ci  tracker->TrackField("ticket", ticket_);
1091cb0ef41Sopenharmony_ci  tracker->TrackField("transport_params", transport_params_);
1101cb0ef41Sopenharmony_ci}
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ciint SessionTicket::GenerateCallback(SSL* ssl, void* arg) {
1131cb0ef41Sopenharmony_ci  SessionTicket::AppData::Collect(ssl);
1141cb0ef41Sopenharmony_ci  return 1;
1151cb0ef41Sopenharmony_ci}
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ciSSL_TICKET_RETURN SessionTicket::DecryptedCallback(SSL* ssl,
1181cb0ef41Sopenharmony_ci                                                   SSL_SESSION* session,
1191cb0ef41Sopenharmony_ci                                                   const unsigned char* keyname,
1201cb0ef41Sopenharmony_ci                                                   size_t keyname_len,
1211cb0ef41Sopenharmony_ci                                                   SSL_TICKET_STATUS status,
1221cb0ef41Sopenharmony_ci                                                   void* arg) {
1231cb0ef41Sopenharmony_ci  switch (status) {
1241cb0ef41Sopenharmony_ci    default:
1251cb0ef41Sopenharmony_ci      return SSL_TICKET_RETURN_IGNORE;
1261cb0ef41Sopenharmony_ci    case SSL_TICKET_EMPTY:
1271cb0ef41Sopenharmony_ci      [[fallthrough]];
1281cb0ef41Sopenharmony_ci    case SSL_TICKET_NO_DECRYPT:
1291cb0ef41Sopenharmony_ci      return SSL_TICKET_RETURN_IGNORE_RENEW;
1301cb0ef41Sopenharmony_ci    case SSL_TICKET_SUCCESS_RENEW:
1311cb0ef41Sopenharmony_ci      [[fallthrough]];
1321cb0ef41Sopenharmony_ci    case SSL_TICKET_SUCCESS:
1331cb0ef41Sopenharmony_ci      return static_cast<SSL_TICKET_RETURN>(
1341cb0ef41Sopenharmony_ci          SessionTicket::AppData::Extract(ssl));
1351cb0ef41Sopenharmony_ci  }
1361cb0ef41Sopenharmony_ci}
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ciSessionTicket::AppData::AppData(SSL* ssl) : ssl_(ssl) {}
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_cibool SessionTicket::AppData::Set(const uv_buf_t& data) {
1411cb0ef41Sopenharmony_ci  if (set_ || data.base == nullptr || data.len == 0) return false;
1421cb0ef41Sopenharmony_ci  set_ = true;
1431cb0ef41Sopenharmony_ci  SSL_SESSION_set1_ticket_appdata(SSL_get0_session(ssl_), data.base, data.len);
1441cb0ef41Sopenharmony_ci  return set_;
1451cb0ef41Sopenharmony_ci}
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_cistd::optional<const uv_buf_t> SessionTicket::AppData::Get() const {
1481cb0ef41Sopenharmony_ci  uv_buf_t buf;
1491cb0ef41Sopenharmony_ci  int ret =
1501cb0ef41Sopenharmony_ci      SSL_SESSION_get0_ticket_appdata(SSL_get0_session(ssl_),
1511cb0ef41Sopenharmony_ci                                      reinterpret_cast<void**>(&buf.base),
1521cb0ef41Sopenharmony_ci                                      reinterpret_cast<size_t*>(&buf.len));
1531cb0ef41Sopenharmony_ci  if (ret != 1) return std::nullopt;
1541cb0ef41Sopenharmony_ci  return buf;
1551cb0ef41Sopenharmony_ci}
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_civoid SessionTicket::AppData::Collect(SSL* ssl) {
1581cb0ef41Sopenharmony_ci  auto source = GetAppDataSource(ssl);
1591cb0ef41Sopenharmony_ci  if (source != nullptr) {
1601cb0ef41Sopenharmony_ci    SessionTicket::AppData app_data(ssl);
1611cb0ef41Sopenharmony_ci    source->CollectSessionTicketAppData(&app_data);
1621cb0ef41Sopenharmony_ci  }
1631cb0ef41Sopenharmony_ci}
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ciSessionTicket::AppData::Status SessionTicket::AppData::Extract(SSL* ssl) {
1661cb0ef41Sopenharmony_ci  auto source = GetAppDataSource(ssl);
1671cb0ef41Sopenharmony_ci  if (source != nullptr) {
1681cb0ef41Sopenharmony_ci    SessionTicket::AppData app_data(ssl);
1691cb0ef41Sopenharmony_ci    return source->ExtractSessionTicketAppData(app_data);
1701cb0ef41Sopenharmony_ci  }
1711cb0ef41Sopenharmony_ci  return Status::TICKET_IGNORE;
1721cb0ef41Sopenharmony_ci}
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci}  // namespace quic
1751cb0ef41Sopenharmony_ci}  // namespace node
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci#endif  // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
178