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