Skip to content

Commit

Permalink
tls: expose Finished messages in TLSSocket
Browse files Browse the repository at this point in the history
Exposes SSL_get_finished and SSL_get_peer_finished routines in OpenSSL
as tlsSocket.getFinished and tlsSocket.getPeerFinished, respectively.

PR-URL: nodejs#19102
Fixes: nodejs#19055
Refs: XRPLF/rippled#2413
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
codedot authored and MayaLekova committed May 8, 2018
1 parent b011308 commit 43c8082
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 0 deletions.
35 changes: 35 additions & 0 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,23 @@ if called on a server socket. The supported types are `'DH'` and `'ECDH'`. The

For Example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }`

### tlsSocket.getFinished()
<!-- YAML
added: REPLACEME
-->

* Returns: {Buffer|undefined} The latest `Finished` message that has been
sent to the socket as part of a SSL/TLS handshake, or `undefined` if
no `Finished` message has been sent yet.

As the `Finished` messages are message digests of the complete handshake
(with a total of 192 bits for TLS 1.0 and more for SSL 3.0), they can
be used for external authentication procedures when the authentication
provided by SSL/TLS is not desired or is not enough.

Corresponds to the `SSL_get_finished` routine in OpenSSL and may be used
to implement the `tls-unique` channel binding from [RFC 5929][].

### tlsSocket.getPeerCertificate([detailed])
<!-- YAML
added: v0.11.4
Expand Down Expand Up @@ -628,6 +645,23 @@ For example:

If the peer does not provide a certificate, an empty object will be returned.

### tlsSocket.getPeerFinished()
<!-- YAML
added: REPLACEME
-->

* Returns: {Buffer|undefined} The latest `Finished` message that is expected
or has actually been received from the socket as part of a SSL/TLS handshake,
or `undefined` if there is no `Finished` message so far.

As the `Finished` messages are message digests of the complete handshake
(with a total of 192 bits for TLS 1.0 and more for SSL 3.0), they can
be used for external authentication procedures when the authentication
provided by SSL/TLS is not desired or is not enough.

Corresponds to the `SSL_get_peer_finished` routine in OpenSSL and may be used
to implement the `tls-unique` channel binding from [RFC 5929][].

### tlsSocket.getProtocol()
<!-- YAML
added: v5.7.0
Expand Down Expand Up @@ -1368,3 +1402,4 @@ where `secure_socket` has the same API as `pair.cleartext`.
[specific attacks affecting larger AES key sizes]: https://www.schneier.com/blog/archives/2009/07/another_new_aes.html
[tls.Server]: #tls_class_tls_server
[`dns.lookup()`]: dns.html#dns_dns_lookup_hostname_options_callback
[RFC 5929]: https://tools.ietf.org/html/rfc5929
10 changes: 10 additions & 0 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,16 @@ TLSSocket.prototype.getPeerCertificate = function(detailed) {
return null;
};

TLSSocket.prototype.getFinished = function() {
if (this._handle)
return this._handle.getFinished();
};

TLSSocket.prototype.getPeerFinished = function() {
if (this._handle)
return this._handle.getPeerFinished();
};

TLSSocket.prototype.getSession = function() {
if (this._handle) {
return this._handle.getSession();
Expand Down
48 changes: 48 additions & 0 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,8 @@ void SSLWrap<Base>::AddMethods(Environment* env, Local<FunctionTemplate> t) {
HandleScope scope(env->isolate());

env->SetProtoMethod(t, "getPeerCertificate", GetPeerCertificate);
env->SetProtoMethod(t, "getFinished", GetFinished);
env->SetProtoMethod(t, "getPeerFinished", GetPeerFinished);
env->SetProtoMethod(t, "getSession", GetSession);
env->SetProtoMethod(t, "setSession", SetSession);
env->SetProtoMethod(t, "loadSession", LoadSession);
Expand Down Expand Up @@ -2120,6 +2122,52 @@ void SSLWrap<Base>::GetPeerCertificate(
}


template <class Base>
void SSLWrap<Base>::GetFinished(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Base* w;
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());

// We cannot just pass nullptr to SSL_get_finished()
// because it would further be propagated to memcpy(),
// where the standard requirements as described in ISO/IEC 9899:2011
// sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated.
// Thus, we use a dummy byte.
char dummy[1];
size_t len = SSL_get_finished(w->ssl_, dummy, sizeof dummy);
if (len == 0)
return;

char* buf = Malloc(len);
CHECK_EQ(len, SSL_get_finished(w->ssl_, buf, len));
args.GetReturnValue().Set(Buffer::New(env, buf, len).ToLocalChecked());
}


template <class Base>
void SSLWrap<Base>::GetPeerFinished(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Base* w;
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());

// We cannot just pass nullptr to SSL_get_peer_finished()
// because it would further be propagated to memcpy(),
// where the standard requirements as described in ISO/IEC 9899:2011
// sections 7.21.2.1, 7.21.1.2, and 7.1.4, would be violated.
// Thus, we use a dummy byte.
char dummy[1];
size_t len = SSL_get_peer_finished(w->ssl_, dummy, sizeof dummy);
if (len == 0)
return;

char* buf = Malloc(len);
CHECK_EQ(len, SSL_get_peer_finished(w->ssl_, buf, len));
args.GetReturnValue().Set(Buffer::New(env, buf, len).ToLocalChecked());
}


template <class Base>
void SSLWrap<Base>::GetSession(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Expand Down
2 changes: 2 additions & 0 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ class SSLWrap {

static void GetPeerCertificate(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetPeerFinished(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetSession(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetSession(const v8::FunctionCallbackInfo<v8::Value>& args);
static void LoadSession(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down
66 changes: 66 additions & 0 deletions test/parallel/test-tls-finished.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

const common = require('../common');
const fixtures = require('../common/fixtures');

if (!common.hasCrypto)
common.skip('missing crypto');

// This test ensures that tlsSocket.getFinished() and
// tlsSocket.getPeerFinished() return undefined before
// secure connection is established, and return non-empty
// Buffer objects with Finished messages afterwards, also
// verifying alice.getFinished() == bob.getPeerFinished()
// and alice.getPeerFinished() == bob.getFinished().

const assert = require('assert');
const tls = require('tls');

const msg = {};
const pem = (n) => fixtures.readKey(`${n}.pem`);
const server = tls.createServer({
key: pem('agent1-key'),
cert: pem('agent1-cert')
}, common.mustCall((alice) => {
msg.server = {
alice: alice.getFinished(),
bob: alice.getPeerFinished()
};
server.close();
}));

server.listen(0, common.mustCall(() => {
const bob = tls.connect({
port: server.address().port,
rejectUnauthorized: false
}, common.mustCall(() => {
msg.client = {
alice: bob.getPeerFinished(),
bob: bob.getFinished()
};
bob.end();
}));

msg.before = {
alice: bob.getPeerFinished(),
bob: bob.getFinished()
};
}));

process.on('exit', () => {
assert.strictEqual(undefined, msg.before.alice);
assert.strictEqual(undefined, msg.before.bob);

assert(Buffer.isBuffer(msg.server.alice));
assert(Buffer.isBuffer(msg.server.bob));
assert(Buffer.isBuffer(msg.client.alice));
assert(Buffer.isBuffer(msg.client.bob));

assert(msg.server.alice.length > 0);
assert(msg.server.bob.length > 0);
assert(msg.client.alice.length > 0);
assert(msg.client.bob.length > 0);

assert(msg.server.alice.equals(msg.client.alice));
assert(msg.server.bob.equals(msg.client.bob));
});

0 comments on commit 43c8082

Please sign in to comment.