Uh oh!
There was an error while loading. Please reload this page.
crypto: add ticketKeys option in createSecureContext - #20916
Conversation
There's a method to initialize a TLS Server using tls.createSever by specifying a ticketKeys option, but none in the underlying constructor, tls.createSecureContext. This PR adds the ticketKeys option to tls.createSecureContext. Fixes: nodejs#20908
ryzokuken
commented
May 23, 2018
Running tests locally. |
silverwind
commented
May 23, 2018
Change seems good to me. Couly you update the docs and add a test that verifies that both |
ryzokuken
commented
May 23, 2018
@silverwind exactly what I had in mind once the tests passed. I guess I'd also have to add a "Changes" entry to the docs. |
ryzokuken
commented
May 24, 2018
Tests pass. Let me add the docs for this. |
DiegoTUI
commented
May 24, 2018
Other than the docs, I think it also makes sense to add the methods |
ryzokuken
commented
May 24, 2018
@DiegoTUI that does sound like a logical thing to do, thanks. I will add those today. If I had any doubts regarding the change, this makes it certain that it'll indeed be a |
bnoordhuis
left a comment
There was a problem hiding this comment.
Needs documentation and tests. It's not useful to expose this without a documented way to:
- get the ticket keys
- configure the session timeout
See tls.Server#getTicketKeys() and the sessionTimeout option to tls.createServer().
| options.clientCertEngine); | ||
| } | ||
| // Set ticketKeys right inside createSecureContext |
There was a problem hiding this comment.
Will remove it, in that case.
| crl: this.crl, | ||
| sessionIdContext: this.sessionIdContext | ||
| sessionIdContext: this.sessionIdContext, | ||
| ticketKeys: this.ticketKeys |
There was a problem hiding this comment.
I certainly will! I didn't add one because there wasn't one to begin with 😅
Noticed that some documentation links to the |
Helpful checklist:
|
ryzokuken
commented
May 24, 2018
@bnoordhuis@silverwind No docs or tests still, but added the required functions and option, so PTAL |
| } | ||
| SecureContext.prototype.getTicketKeys = function getTicketKeys(keys) { | ||
| return this.context.getTicketKeys(keys); |
There was a problem hiding this comment.
I realize you copied this from elsewhere but this method doesn't take any arguments, it returns a new buffer.
There was a problem hiding this comment.
@bnoordhuis quite right, I actually took my cues from https://github.com/ryzokuken/node/blob/6269853a9a444a3fbed351ddeb12a6e97114543d/lib/_tls_wrap.js#L928
There was a problem hiding this comment.
I'd look into the original context's getTicketKeys() method and try working with that one.
There was a problem hiding this comment.
Umm, it can take arguments? Idk, the code was:
voidSecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
SecureContext* wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder());
Local<Object> buff = Buffer::New(wrap->env(), 48).ToLocalChecked();
memcpy(Buffer::Data(buff), wrap->ticket_key_name_, 16);
memcpy(Buffer::Data(buff) + 16, wrap->ticket_key_hmac_, 16);
memcpy(Buffer::Data(buff) + 32, wrap->ticket_key_aes_, 16);
args.GetReturnValue().Set(buff);
#endif// !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
}There was a problem hiding this comment.
Anyway, I think most if not all people call it without any args plus no arguments mentioned in the docs, so I'll make the change.
There was a problem hiding this comment.
You mean args? Any C++ API function takes that as its argument.
If you're referring to args.Holder(), that's the this object. Positional arguments are accessed using array notation, i.e., args[0].
There was a problem hiding this comment.
@bnoordhuis TIL, I guess 😅 Not super good at the C++ part of things.
There was a problem hiding this comment.
In that case, getTicketKeys indeed doesn't use any arguments while setTicketKeys takes one, perfect.
ryzokuken
commented
May 26, 2018
@bnoordhuis added a few tests, they're running locally. PTAL and let me know if I missed anything. |
I needed |
@bnoordhuis I have added the test-tls-session-timeout.js'use strict';constcommon=require('../common');if(!common.opensslCli)common.skip('node compiled without OpenSSL CLI.');if(!common.hasCrypto)common.skip('missing crypto');consttmpdir=require('../common/tmpdir');doTest();// This test consists of three TLS requests --// * The first one should result in a new connection because we don't have// a valid session ticket.// * The second one should result in connection resumption because we used// the session ticket we saved from the first connection.// * The third one should result in a new connection because the ticket// that we used has expired by now.functiondoTest(){constassert=require('assert');consttls=require('tls');constfs=require('fs');constjoin=require('path').join;constfixtures=require('../common/fixtures');constspawn=require('child_process').spawn;constSESSION_TIMEOUT=1;constkey=fixtures.path('agent.key');constcert=fixtures.path('agent.crt');constoptions={key: key,cert: cert,ca: [cert],sessionTimeout: SESSION_TIMEOUT};// We need to store a sample session ticket in the fixtures directory because// `s_client` behaves incorrectly if we do not pass in both the `-sess_in`// and the `-sess_out` flags, and the `-sess_in` argument must point to a// file containing a proper serialization of a session ticket.// To avoid a source control diff, we copy the ticket to a temporary file.constsessionFileName=(function(){constticketFileName='tls-session-ticket.txt';consttmpPath=join(tmpdir.path,ticketFileName);fs.writeFileSync(tmpPath,fixtures.readSync(ticketFileName));returntmpPath;}());// Expects a callback -- cb(connectionType : enum ['New'|'Reused'])functionClient(cb){constflags=['s_client','-connect',`localhost:${common.PORT}`,'-sess_in',sessionFileName,'-sess_out',sessionFileName];constclient=spawn(common.opensslCli,flags,{stdio: ['ignore','pipe','ignore']});letclientOutput='';client.stdout.on('data',function(data){clientOutput+=data.toString();});client.on('exit',function(code){letconnectionType;constgrepConnectionType=(line)=>{constmatches=line.match(/(New|Reused),/);if(matches){connectionType=matches[1];returntrue;}};constlines=clientOutput.split('\n');if(!lines.some(grepConnectionType)){thrownewError('unexpected output from openssl client');}cb(connectionType);});}constserver=tls.createServer(options,function(cleartext){cleartext.on('error',function(er){if(er.code!=='ECONNRESET')thrower;});cleartext.end();});server.listen(common.PORT,function(){Client(function(connectionType){assert.strictEqual(connectionType,'New');Client(function(connectionType){assert.strictEqual(connectionType,'Reused');setTimeout(function(){Client(function(connectionType){assert.strictEqual(connectionType,'New');server.close();});},(SESSION_TIMEOUT+1)*1000);});});});} |
bnoordhuis
commented
May 26, 2018
You could test |
ryzokuken
commented
May 26, 2018
@bnoordhuis I assigned the |
Built Node in debug mode and checked it out myself, Breakpoint and Backtrace~/Code/nodejs/node crypto-20908*
❯ lldb -- ./out/Debug/node test/parallel/test-tls-securecontext-ticketkeys.js(lldb) target create "./out/Debug/node"Current executable set to './out/Debug/node' (x86_64).(lldb) settings set -- target.run-args "test/parallel/test-tls-securecontext-ticketkeys.js"(lldb) breakpoint set --name SSL_CTX_set_timeoutBreakpoint 1: where = node`SSL_CTX_set_timeout + 12 at ssl_sess.c:916, address = 0x0000000101a910bc(lldb) rProcess 56423 launched: './out/Debug/node' (x86_64)Process 56423 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x0000000101a910bc node`SSL_CTX_set_timeout(s=0x00000001053064b0, t=1) at ssl_sess.c:916 913 long SSL_CTX_set_timeout(SSL_CTX *s, long t) 914 { 915 long l;-> 916 if (s == NULL) 917 return (0); 918 l = s->session_timeout; 919 s->session_timeout = t;Target 0: (node) stopped.(lldb) bt* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000101a910bc node`SSL_CTX_set_timeout(s=0x00000001053064b0, t=1) at ssl_sess.c:916 frame #1: 0x00000001002b5d44 node`node::crypto::SecureContext::SetSessionTimeout(args=0x00007ffeefbfcf70) at node_crypto.cc:1025 frame #2: 0x0000000100607cb3 node`v8::internal::FunctionCallbackArguments::Call(this=0x00007ffeefbfd0d0, handler=0x00003969690bdd41) at api-arguments.cc:29 frame #3: 0x00000001007283ee node`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(isolate=0x0000000105804c00, function=Handle<v8::internal::HeapObject> @ 0x00007ffeefbfd1e0, new_target=Handle<v8::internal::HeapObject> @ 0x00007ffeefbfd1d8, fun_data=Handle<v8::internal::FunctionTemplateInfo> @ 0x00007ffeefbfd1d0, receiver=Handle<v8::internal::Object> @ 0x00007ffeefbfd1c8, args=BuiltinArguments @ 0x00007ffeefbfd200) at builtins-api.cc:108 frame #4: 0x0000000100726d0a node`v8::internal::Builtin_Impl_HandleApiCall(args=BuiltinArguments @ 0x00007ffeefbfd330, isolate=0x0000000105804c00) at builtins-api.cc:138 frame #5: 0x000000010072683d node`v8::internal::Builtin_HandleApiCall(args_length=6, args_object=0x00007ffeefbfd410, isolate=0x0000000105804c00) at builtins-api.cc:126 frame #6: 0x00001f0f67a042c4 frame #7: 0x00001f0f67a1cf78 frame #8: 0x00001f0f67a108e3 frame #9: 0x00001f0f67a1cf78 frame #10: 0x00001f0f67a1cf78 frame #11: 0x00001f0f67a1cf78 frame #12: 0x00001f0f67a1cf78 frame #13: 0x00001f0f67a1cf78 frame #14: 0x00001f0f67a1cf78 frame #15: 0x00001f0f67a1cf78 frame #16: 0x00001f0f67a1cf78 frame #17: 0x00001f0f67a1cf78 frame #18: 0x00001f0f67a16e15 frame #19: 0x00001f0f67a0b581 frame #20: 0x0000000100fd0efd node`v8::internal::GeneratedCode<v8::internal::Object*, v8::internal::Object*, v8::internal::Object*, v8::internal::Object*, int, v8::internal::Object***>::Call(this=0x00007ffeefbfdd50, args=0x00003969a12822e1, args=0x000039699cc0e4d9, args=0x00003969a1282201, args=2, args=0x00007ffeefbfe3e0) at simulator.h:110 frame #21: 0x0000000100fcf182 node`v8::internal::(anonymous namespace)::Invoke(isolate=0x0000000105804c00, is_construct=false, target=Handle<v8::internal::Object> @ 0x00007ffeefbfde70, receiver=Handle<v8::internal::Object> @ 0x00007ffeefbfde68, argc=2, args=0x00007ffeefbfe3e0, new_target=Handle<v8::internal::Object> @ 0x00007ffeefbfde60, message_handling=kReport, execution_target=kCallable) at execution.cc:153 frame #22: 0x0000000100fcea03 node`v8::internal::(anonymous namespace)::CallInternal(isolate=0x0000000105804c00, callable=Handle<v8::internal::Object> @ 0x00007ffeefbfdf40, receiver=Handle<v8::internal::Object> @ 0x00007ffeefbfdf38, argc=2, argv=0x00007ffeefbfe3e0, message_handling=kReport, target=kCallable) at execution.cc:189 frame #23: 0x0000000100fce89d node`v8::internal::Execution::Call(isolate=0x0000000105804c00, callable=Handle<v8::internal::Object> @ 0x00007ffeefbfdfa0, receiver=Handle<v8::internal::Object> @ 0x00007ffeefbfdf98, argc=2, argv=0x00007ffeefbfe3e0) at execution.cc:200 frame #24: 0x00000001005b0f48 node`v8::Function::Call(this=0x000000010703ea68, context=(val_ = 0x00000001058216e0), recv=(val_ = 0x0000000105804c78), argc=2, argv=0x00007ffeefbfe3e0) at api.cc:5077 frame #25: 0x000000010008a872 node`node::ExecuteBootstrapper(env=0x00007ffeefbfe4f0, bootstrapper=(val_ = 0x000000010703ea68), argc=2, argv=0x00007ffeefbfe3e0, out=0x00007ffeefbfe258) at node.cc:3067 frame #26: 0x0000000100089a0c node`node::LoadEnvironment(env=0x00007ffeefbfe4f0) at node.cc:3167 frame #27: 0x000000010009460e node`node::Start(isolate=0x0000000105804c00, isolate_data=0x0000000107040400, argc=2, argv=0x0000000105200050, exec_argc=0, exec_argv=0x00000001052001f0) at node.cc:4256 frame #28: 0x000000010008f19b node`node::Start(event_loop=0x0000000102da7a40, argc=2, argv=0x0000000105200050, exec_argc=0, exec_argv=0x00000001052001f0) at node.cc:4363 frame #29: 0x000000010008e7c5 node`node::Start(argc=2, argv=0x0000000105200050) at node.cc:4414 frame #30: 0x0000000101cb912e node`main(argc=2, argv=0x00007ffeefbff170) at node_main.cc:124 frame #31: 0x0000000100001034 node`start + 52(lldb) cProcess 56423 resuming<Buffer 38 67 52 0b 46 dc 16 f1 df df cf 62 81 f8 a0 3b ed 45 27 75 d4 f9 9e 00 2d 5b fc 1d 8a 3e 60 20 eb ae ea 7f 31 45 0d f8 24 f6 4a 48 6d e1 ca 04> <Buffer 38 67 52 0b 46 dc 16 f1 df df cf 62 81 f8 a0 3b ed 45 27 75 d4 f9 9e 00 2d 5b fc 1d 8a 3e 60 20 eb ae ea 7f 31 45 0d f8 24 f6 4a 48 6d e1 ca 04>Process 56423 exited with status = 0 (0x00000000)(lldb) exit |
ryzokuken
commented
May 28, 2018
@nodejs/crypto @bnoordhuis PTAL. |
@nodejs/crypto anyone willing to give this a shot with me during the summit? P.S. giving this a shot == helping me debug this, I'm clueless. |
ryzokuken
commented
Jun 10, 2018
ping @nodejs/crypto please help? |
Sorry, didn't have time to look at this yet, and I am not that confident with the TLS APIs of OpenSSL. Are you sure that the test case is correct? Edit: Also, I believe the subsystem should be |
ryzokuken
commented
Jun 11, 2018
@tniessen that's what I'm afraid of. I'm so bad with the specifics of this that I don't know if the test case is correct. Let me try looking deeper too. |
jasnell
commented
Sep 10, 2018
Ping @ryzokuken |
Trott
commented
Nov 20, 2018
@ryzokuken Still working on this? |
BridgeAR
commented
Mar 10, 2019
Closing due to long inactivity. @ryzokuken please reopen / open a new PR in case you want to continue working on this. |
There's a method to initialize a TLS Server using tls.createSever by
specifying a ticketKeys option, but none in the underlying constructor,
tls.createSecureContext.
This PR adds the ticketKeys option to tls.createSecureContext.
Fixes: #20908
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes/cc @nodejs/crypto @bnoordhuis@silverwind@DiegoTUI
I guess this qualifies as a
semver-minor?