Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

Commit

Permalink
quic: complete implementation of OCSPRequest/OCSPResponse
Browse files Browse the repository at this point in the history
PR-URL: #31
  • Loading branch information
jasnell committed Aug 9, 2019
1 parent 3c879aa commit 9d64668
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 97 deletions.
132 changes: 94 additions & 38 deletions lib/internal/quic/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const {
assertCrypto();

const { Error } = primordials;
const { isArrayBufferView } = require('internal/util/types');
const {
getAllowUnauthorized,
getSocketType,
Expand Down Expand Up @@ -222,20 +223,72 @@ function onSessionClose(code, family) {
this[owner_symbol].destroy();
}

// This callback is invoked at the start of the TLS handshake to provide
// some basic information about the ALPN, SNI, and Ciphers that are
// being requested. It is only called if the 'clientHello' event is
// listened for.
function onSessionClientHello(alpn, servername, ciphers, callback) {
callback = callback.bind(this);
function cb(...args) {
callback(...args);
}
this[owner_symbol][kClientHello](alpn, servername, ciphers, cb);
this[owner_symbol][kClientHello](
alpn,
servername,
ciphers,
(err, ...args) => {
if (err) {
this[owner_symbol].destroy(err);
return;
}
try {
callback(...args);
} catch (err) {
this[owner_symbol].destroy(err);
}
});
}

function onSessionCert(servername, ocsp, callback) {
// This callback is only ever invoked for QuicServerSession instances,
// and is used to trigger OCSP request processing when needed. The
// user callback must invoke the callback function in order for the
// TLS handshake to continue.
function onSessionCert(servername, callback) {
callback = callback.bind(this);
function cb(...args) {
callback(...args);
}
this[owner_symbol][kCert](servername, ocsp, cb);
this[owner_symbol][kCert](servername, (err, context, ocspResponse) => {
if (err) {
this[owner_symbol].destroy(err);
return;
}
if (context != null && !context.context) {
this[owner_symbol].destroy(
new ERR_INVALID_ARG_TYPE(
'context',
'SecureContext',
context));
}
if (ocspResponse != null) {
if (typeof ocspResponse === 'string')
ocspResponse = Buffer.from(ocspResponse);
if (!isArrayBufferView(ocspResponse)) {
this[owner_symbol].destroy(
new ERR_INVALID_ARG_TYPE(
'ocspResponse',
['string', 'Buffer', 'TypedArray', 'DataView'],
ocspResponse));
}
}
try {
callback(context, ocspResponse);
} catch (err) {
this[owner_symbol].destroy(err);
}
});
}

// This callback is only ever invoked for QuicClientSession instances,
// and is used to deliver the OCSP response as provided by the server.
// If the requestOCSP configuration option is false, this will never
// be called.
function onSessionStatus(response) {
this[owner_symbol][kCert](response);
}

function onSessionHandshake(
Expand Down Expand Up @@ -334,6 +387,7 @@ setCallbacks({
onSessionExtend,
onSessionHandshake,
onSessionKeylog,
onSessionStatus,
onSessionTicket,
onStreamReady,
onStreamClose,
Expand Down Expand Up @@ -454,7 +508,7 @@ function onNewListener(event) {
case 'clientHello':
this[kHandle].state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 1;
break;
case 'cert':
case 'OCSPRequest':
this[kHandle].state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 1;
break;
}
Expand All @@ -471,7 +525,7 @@ function onRemoveListener(event) {
case 'clientHello':
this[kHandle].state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 0;
break;
case 'cert':
case 'OCSPRequest':
this[kHandle].state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 0;
break;
}
Expand Down Expand Up @@ -1222,34 +1276,28 @@ class QuicServerSession extends QuicSession {
}

[kClientHello](alpn, servername, ciphers, callback) {
const { serverSecureContext } = this.socket;
if (!serverSecureContext)
callback(null, null);
const { context } = serverSecureContext;

this.emit(
'clientHello',
alpn,
servername,
ciphers,
context.getCertificate(),
context.getIssuer(),
callback.bind(this[kHandle]));
}

[kReady]() {
process.nextTick(emit.bind(this, 'ready'));
}

[kCert](servername, ocsp, callback) {
if (!ocsp) {
callback(null);
return;
}
[kCert](servername, callback) {
const { serverSecureContext } = this.socket;
if (!serverSecureContext)
callback(null, null);
const { context } = serverSecureContext;
this.emit(
'cert',
'OCSPRequest',
servername,
ocsp,
context.getCertificate(),
context.getIssuer(),
callback.bind(this[kHandle]));
}

Expand Down Expand Up @@ -1284,16 +1332,17 @@ function setSocketAfterBind(socket, callback) {

class QuicClientSession extends QuicSession {
#alpn = undefined;
#dcid = undefined;
#handleReady = false;
#ipv6Only = undefined;
#minDHSize = undefined;
#port = undefined;
#remoteTransportParams = undefined;
#requestOCSP = undefined;
#secureContext = undefined;
#sessionTicket = undefined;
#socketReady = false;
#transportParams = undefined;
#sessionTicket = undefined;
#remoteTransportParams = undefined;
#dcid = undefined;
#preferredAddressPolicy;

constructor(socket, options) {
Expand All @@ -1311,26 +1360,28 @@ class QuicClientSession extends QuicSession {
port,
preferredAddressPolicy,
remoteTransportParams,
requestOCSP,
servername,
sessionTicket,
} = validateQuicClientSessionOptions(options);

super(socket, servername);
this.#alpn = alpn;
this.#transportParams =
validateTransportParams(
options,
maxCidLen,
minCidLen);
this.#dcid = dcid;
this.#ipv6Only = ipv6Only;
this.#minDHSize = minDHSize;
this.#port = port || 0;
this.#preferredAddressPolicy = preferredAddressPolicy;
this.#remoteTransportParams = remoteTransportParams;
this.#requestOCSP = requestOCSP;
this.#secureContext = createSecureContext(sc_options,
initSecureContextClient);
initSecureContextClient);
this.#sessionTicket = sessionTicket;
this.#remoteTransportParams = remoteTransportParams;
this.#dcid = dcid;
this.#preferredAddressPolicy = preferredAddressPolicy;
this.#transportParams =
validateTransportParams(
options,
maxCidLen,
minCidLen);
}

[kHandshakePost]() {
Expand Down Expand Up @@ -1371,7 +1422,8 @@ class QuicClientSession extends QuicSession {
this.#sessionTicket,
this.#dcid,
this.#preferredAddressPolicy,
this.#alpn);
this.#alpn,
this.#requestOCSP);
// We no longer need these, unset them so
// memory can be garbage collected.
this.#remoteTransportParams = undefined;
Expand Down Expand Up @@ -1407,6 +1459,10 @@ class QuicClientSession extends QuicSession {
this[kMaybeReady]();
}

[kCert](response) {
this.emit('OCSPResponse', response);
}

[kMaybeReady]() {
if (this.#socketReady && this.#handleReady)
process.nextTick(emit.bind(this, 'ready'));
Expand Down
9 changes: 9 additions & 0 deletions lib/internal/quic/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ function validateQuicClientSessionOptions(options) {
port = 0,
preferredAddressPolicy = 'ignore',
remoteTransportParams,
requestOCSP = false,
servername = address,
sessionTicket,
} = { ...options };
Expand Down Expand Up @@ -301,6 +302,13 @@ function validateQuicClientSessionOptions(options) {
preferredAddressPolicy);
}

if (typeof requestOCSP !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE(
'options.requestOCSP',
'boolean',
requestOCSP);
}

return {
address,
alpn,
Expand All @@ -315,6 +323,7 @@ function validateQuicClientSessionOptions(options) {
QUIC_PREFERRED_ADDRESS_ACCEPT :
QUIC_PREFERRED_ADDRESS_IGNORE,
remoteTransportParams,
requestOCSP,
servername,
sessionTicket,
};
Expand Down
1 change: 1 addition & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ constexpr size_t kFsStatsBufferLength =
V(quic_on_session_extend_function, v8::Function) \
V(quic_on_session_handshake_function, v8::Function) \
V(quic_on_session_keylog_function, v8::Function) \
V(quic_on_session_status_function, v8::Function) \
V(quic_on_session_ticket_function, v8::Function) \
V(quic_on_session_path_validation_function, v8::Function) \
V(quic_on_stream_ready_function, v8::Function) \
Expand Down
9 changes: 0 additions & 9 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -458,15 +458,6 @@ bool EntropySource(unsigned char* buffer, size_t length) {
return RAND_bytes(buffer, length) != -1;
}


template <typename T>
static T* MallocOpenSSL(size_t count) {
void* mem = OPENSSL_malloc(MultiplyWithOverflowCheck(count, sizeof(T)));
CHECK_IMPLIES(mem == nullptr, count == 0);
return static_cast<T*>(mem);
}


void SecureContext::Initialize(Environment* env, Local<Object> target) {
Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
Expand Down
8 changes: 8 additions & 0 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,14 @@ v8::Local<v8::Object> GetLastIssuedCert(
SSL* ssl,
v8::Local<v8::Object> issuer_chain,
Environment* const env);

template <typename T>
inline T* MallocOpenSSL(size_t count) {
void* mem = OPENSSL_malloc(MultiplyWithOverflowCheck(count, sizeof(T)));
CHECK_IMPLIES(mem == nullptr, count == 0);
return static_cast<T*>(mem);
}

} // namespace crypto
} // namespace node

Expand Down
10 changes: 10 additions & 0 deletions src/node_quic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void QuicSetCallbacks(const FunctionCallbackInfo<Value>& args) {
SETFUNCTION("onSessionHandshake", session_handshake);
SETFUNCTION("onSessionKeylog", session_keylog);
SETFUNCTION("onSessionPathValidation", session_path_validation);
SETFUNCTION("onSessionStatus", session_status);
SETFUNCTION("onSessionTicket", session_ticket);
SETFUNCTION("onStreamReady", stream_ready);
SETFUNCTION("onStreamClose", stream_close);
Expand Down Expand Up @@ -159,6 +160,11 @@ int Client_Transport_Params_Add_CB(
return 1;
}

int TLS_Status_Callback(SSL* ssl, void* arg) {
QuicSession* session = static_cast<QuicSession*>(SSL_get_app_data(ssl));
return session->OnTLSStatus();
}

int Server_Transport_Params_Add_CB(
SSL* ssl,
unsigned int ext_type,
Expand Down Expand Up @@ -290,6 +296,8 @@ void QuicInitSecureContext(const FunctionCallbackInfo<Value>& args) {
SSL_CTX_set_max_early_data(**sc, std::numeric_limits<uint32_t>::max());
SSL_CTX_set_alpn_select_cb(**sc, ALPN_Select_Proto_CB, nullptr);
SSL_CTX_set_client_hello_cb(**sc, Client_Hello_CB, nullptr);
SSL_CTX_set_tlsext_status_cb(**sc, TLS_Status_Callback);
SSL_CTX_set_tlsext_status_arg(**sc, nullptr);
CHECK_EQ(
SSL_CTX_add_custom_ext(
**sc,
Expand Down Expand Up @@ -320,6 +328,8 @@ void QuicInitSecureContextClient(const FunctionCallbackInfo<Value>& args) {
SSL_CTX_set_mode(**sc, SSL_MODE_QUIC_HACK);
SSL_CTX_clear_options(**sc, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
SSL_CTX_set_default_verify_paths(**sc);
SSL_CTX_set_tlsext_status_cb(**sc, TLS_Status_Callback);
SSL_CTX_set_tlsext_status_arg(**sc, nullptr);

CHECK_EQ(SSL_CTX_add_custom_ext(
**sc,
Expand Down
Loading

0 comments on commit 9d64668

Please sign in to comment.