Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit acb3aff

Browse filesBrowse files
simllllcodebytere
authored andcommitted
tls: expose SSL_export_keying_material
Fixes: #31802 PR-URL: #31814 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
1 parent 2046652 commit acb3aff
Copy full SHA for acb3aff

File tree

Expand file treeCollapse file tree

7 files changed

+208
-2
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+208
-2
lines changed
Open diff view settings
Collapse file

‎doc/api/errors.md‎

Copy file name to clipboardExpand all lines: doc/api/errors.md
+9Lines changed: 9 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,15 @@ added: v13.3.0
18581858

18591859
The context must be a `SecureContext`.
18601860

1861+
<a id="ERR_TLS_INVALID_STATE"></a>
1862+
### `ERR_TLS_INVALID_STATE`
1863+
<!-- YAML
1864+
added: REPLACEME
1865+
-->
1866+
1867+
The TLS socket must be connected and securily established. Ensure the 'secure'
1868+
event is emitted, before you continue.
1869+
18611870
<a id="ERR_TLS_INVALID_PROTOCOL_METHOD"></a>
18621871
### `ERR_TLS_INVALID_PROTOCOL_METHOD`
18631872

Collapse file

‎doc/api/tls.md‎

Copy file name to clipboardExpand all lines: doc/api/tls.md
+34Lines changed: 34 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,39 @@ See
10941094
[SSL_get_shared_sigalgs](https://www.openssl.org/docs/man1.1.1/man3/SSL_get_shared_sigalgs.html)
10951095
for more information.
10961096

1097+
### `tlsSocket.exportKeyingMaterial(length, label[, context])`
1098+
<!-- YAML
1099+
added: REPLACEME
1100+
-->
1101+
1102+
* `length` {number} number of bytes to retrieve from keying material
1103+
* `label` {string} an application specific label, typically this will be a
1104+
value from the
1105+
[IANA Exporter Label Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#exporter-labels).
1106+
* `context` {Buffer} Optionally provide a context.
1107+
1108+
* Returns: {Buffer} requested bytes of the keying material
1109+
1110+
Keying material is used for validations to prevent different kind of attacks in
1111+
network protocols, for example in the specifications of IEEE 802.1X.
1112+
1113+
Example
1114+
1115+
```js
1116+
const keyingMaterial = tlsSocket.exportKeyingMaterial(
1117+
128,
1118+
'client finished');
1119+
1120+
/**
1121+
Example return value of keyingMaterial:
1122+
<Buffer 76 26 af 99 c5 56 8e 42 09 91 ef 9f 93 cb ad 6c 7b 65 f8 53 f1 d8 d9
1123+
12 5a 33 b8 b5 25 df 7b 37 9f e0 e2 4f b8 67 83 a3 2f cd 5d 41 42 4c 91
1124+
74 ef 2c ... 78 more bytes>
1125+
*/
1126+
```
1127+
See the OpenSSL [`SSL_export_keying_material`][] documentation for more
1128+
information.
1129+
10971130
### `tlsSocket.getTLSTicket()`
10981131
<!-- YAML
10991132
added: v0.11.4
@@ -1899,6 +1932,7 @@ where `secureSocket` has the same API as `pair.cleartext`.
18991932
[`'session'`]: #tls_event_session
19001933
[`--tls-cipher-list`]: cli.html#cli_tls_cipher_list_list
19011934
[`NODE_OPTIONS`]: cli.html#cli_node_options_options
1935+
[`SSL_export_keying_material`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_export_keying_material.html
19021936
[`SSL_get_version`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_version.html
19031937
[`crypto.getCurves()`]: crypto.html#crypto_crypto_getcurves
19041938
[`net.createServer()`]: net.html#net_net_createserver_options_connectionlistener
Collapse file

‎lib/_tls_wrap.js‎

Copy file name to clipboardExpand all lines: lib/_tls_wrap.js
+19-2Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,16 @@ const {
6666
ERR_TLS_RENEGOTIATION_DISABLED,
6767
ERR_TLS_REQUIRED_SERVER_NAME,
6868
ERR_TLS_SESSION_ATTACK,
69-
ERR_TLS_SNI_FROM_SERVER
69+
ERR_TLS_SNI_FROM_SERVER,
70+
ERR_TLS_INVALID_STATE
7071
} = codes;
7172
const { onpskexchange: kOnPskExchange } = internalBinding('symbols');
7273
const { getOptionValue } = require('internal/options');
73-
const { validateString, validateBuffer } = require('internal/validators');
74+
const {
75+
validateString,
76+
validateBuffer,
77+
validateUint32
78+
} = require('internal/validators');
7479
const traceTls = getOptionValue('--trace-tls');
7580
const tlsKeylog = getOptionValue('--tls-keylog');
7681
const { appendFile } = require('fs');
@@ -860,6 +865,18 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
860865
return true;
861866
};
862867

868+
TLSSocket.prototype.exportKeyingMaterial = function(length, label, context) {
869+
validateUint32(length, 'length', true);
870+
validateString(label, 'label');
871+
if (context !== undefined)
872+
validateBuffer(context, 'context');
873+
874+
if (!this._secureEstablished)
875+
throw new ERR_TLS_INVALID_STATE();
876+
877+
return this._handle.exportKeyingMaterial(length, label, context);
878+
};
879+
863880
TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
864881
return this._handle.setMaxSendFragment(size) === 1;
865882
};
Collapse file

‎lib/internal/errors.js‎

Copy file name to clipboardExpand all lines: lib/internal/errors.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,8 @@ E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) {
13141314
E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error);
13151315
E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error);
13161316
E('ERR_TLS_INVALID_CONTEXT', '%s must be a SecureContext', TypeError),
1317+
E('ERR_TLS_INVALID_STATE', 'TLS socket connection must be securely established',
1318+
Error),
13171319
E('ERR_TLS_INVALID_PROTOCOL_VERSION',
13181320
'%j is not a valid %s TLS protocol version', TypeError);
13191321
E('ERR_TLS_PROTOCOL_VERSION_CONFLICT',
Collapse file

‎src/node_crypto.cc‎

Copy file name to clipboardExpand all lines: src/node_crypto.cc
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,8 @@ void SSLWrap<Base>::AddMethods(Environment* env, Local<FunctionTemplate> t) {
17431743
env->SetProtoMethodNoSideEffect(t, "verifyError", VerifyError);
17441744
env->SetProtoMethodNoSideEffect(t, "getCipher", GetCipher);
17451745
env->SetProtoMethodNoSideEffect(t, "getSharedSigalgs", GetSharedSigalgs);
1746+
env->SetProtoMethodNoSideEffect(
1747+
t, "exportKeyingMaterial", ExportKeyingMaterial);
17461748
env->SetProtoMethod(t, "endParser", EndParser);
17471749
env->SetProtoMethod(t, "certCbDone", CertCbDone);
17481750
env->SetProtoMethod(t, "renegotiate", Renegotiate);
@@ -2772,6 +2774,44 @@ void SSLWrap<Base>::GetSharedSigalgs(const FunctionCallbackInfo<Value>& args) {
27722774
Array::New(env->isolate(), ret_arr.out(), ret_arr.length()));
27732775
}
27742776

2777+
template <class Base>
2778+
void SSLWrap<Base>::ExportKeyingMaterial(
2779+
const FunctionCallbackInfo<Value>& args) {
2780+
CHECK(args[0]->IsInt32());
2781+
CHECK(args[1]->IsString());
2782+
2783+
Base* w;
2784+
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
2785+
Environment* env = w->ssl_env();
2786+
2787+
uint32_t olen = args[0].As<Uint32>()->Value();
2788+
node::Utf8Value label(env->isolate(), args[1]);
2789+
2790+
AllocatedBuffer out = env->AllocateManaged(olen);
2791+
2792+
ByteSource key;
2793+
2794+
int useContext = 0;
2795+
if (!args[2]->IsNull() && Buffer::HasInstance(args[2])) {
2796+
key = ByteSource::FromBuffer(args[2]);
2797+
2798+
useContext = 1;
2799+
}
2800+
2801+
if (SSL_export_keying_material(w->ssl_.get(),
2802+
reinterpret_cast<unsigned char*>(out.data()),
2803+
olen,
2804+
*label,
2805+
label.length(),
2806+
reinterpret_cast<const unsigned char*>(
2807+
key.get()),
2808+
key.size(),
2809+
useContext) != 1) {
2810+
return ThrowCryptoError(env, ERR_get_error(), "SSL_export_keying_material");
2811+
}
2812+
2813+
args.GetReturnValue().Set(out.ToBuffer().ToLocalChecked());
2814+
}
27752815

27762816
template <class Base>
27772817
void SSLWrap<Base>::GetProtocol(const FunctionCallbackInfo<Value>& args) {
Collapse file

‎src/node_crypto.h‎

Copy file name to clipboardExpand all lines: src/node_crypto.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ class SSLWrap {
263263
static void VerifyError(const v8::FunctionCallbackInfo<v8::Value>& args);
264264
static void GetCipher(const v8::FunctionCallbackInfo<v8::Value>& args);
265265
static void GetSharedSigalgs(const v8::FunctionCallbackInfo<v8::Value>& args);
266+
static void ExportKeyingMaterial(
267+
const v8::FunctionCallbackInfo<v8::Value>& args);
266268
static void EndParser(const v8::FunctionCallbackInfo<v8::Value>& args);
267269
static void CertCbDone(const v8::FunctionCallbackInfo<v8::Value>& args);
268270
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
Collapse file
+102Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict';
2+
3+
// Test return value of tlsSocket.exportKeyingMaterial
4+
5+
const common = require('../common');
6+
7+
if (!common.hasCrypto)
8+
common.skip('missing crypto');
9+
10+
const assert = require('assert');
11+
const net = require('net');
12+
const tls = require('tls');
13+
const fixtures = require('../common/fixtures');
14+
15+
const key = fixtures.readKey('agent1-key.pem');
16+
const cert = fixtures.readKey('agent1-cert.pem');
17+
18+
const server = net.createServer(common.mustCall((s) => {
19+
const tlsSocket = new tls.TLSSocket(s, {
20+
isServer: true,
21+
server: server,
22+
secureContext: tls.createSecureContext({ key, cert })
23+
});
24+
25+
assert.throws(() => {
26+
tlsSocket.exportKeyingMaterial(128, 'label');
27+
}, {
28+
name: 'Error',
29+
message: 'TLS socket connection must be securely established',
30+
code: 'ERR_TLS_INVALID_STATE'
31+
});
32+
33+
tlsSocket.on('secure', common.mustCall(() => {
34+
const label = 'client finished';
35+
36+
const validKeyingMaterial = tlsSocket.exportKeyingMaterial(128, label);
37+
assert.strictEqual(validKeyingMaterial.length, 128);
38+
39+
const validKeyingMaterialWithContext = tlsSocket
40+
.exportKeyingMaterial(128, label, Buffer.from([0, 1, 2, 3]));
41+
assert.strictEqual(validKeyingMaterialWithContext.length, 128);
42+
43+
// Ensure providing a context results in a different key than without
44+
assert.notStrictEqual(validKeyingMaterial, validKeyingMaterialWithContext);
45+
46+
const validKeyingMaterialWithEmptyContext = tlsSocket
47+
.exportKeyingMaterial(128, label, Buffer.from([]));
48+
assert.strictEqual(validKeyingMaterialWithEmptyContext.length, 128);
49+
50+
assert.throws(() => {
51+
tlsSocket.exportKeyingMaterial(128, label, 'stringAsContextNotSupported');
52+
}, {
53+
name: 'TypeError',
54+
code: 'ERR_INVALID_ARG_TYPE'
55+
});
56+
57+
assert.throws(() => {
58+
tlsSocket.exportKeyingMaterial(128, label, 1234);
59+
}, {
60+
name: 'TypeError',
61+
code: 'ERR_INVALID_ARG_TYPE'
62+
});
63+
64+
assert.throws(() => {
65+
tlsSocket.exportKeyingMaterial(10, null);
66+
}, {
67+
name: 'TypeError',
68+
code: 'ERR_INVALID_ARG_TYPE'
69+
});
70+
71+
assert.throws(() => {
72+
tlsSocket.exportKeyingMaterial('length', 1234);
73+
}, {
74+
name: 'TypeError',
75+
code: 'ERR_INVALID_ARG_TYPE'
76+
});
77+
78+
assert.throws(() => {
79+
tlsSocket.exportKeyingMaterial(-3, 'a');
80+
}, {
81+
name: 'RangeError',
82+
code: 'ERR_OUT_OF_RANGE'
83+
});
84+
85+
assert.throws(() => {
86+
tlsSocket.exportKeyingMaterial(0, 'a');
87+
}, {
88+
name: 'RangeError',
89+
code: 'ERR_OUT_OF_RANGE'
90+
});
91+
92+
tlsSocket.end();
93+
server.close();
94+
}));
95+
})).listen(0, () => {
96+
const opts = {
97+
port: server.address().port,
98+
rejectUnauthorized: false
99+
};
100+
101+
tls.connect(opts, common.mustCall(function() { this.end(); }));
102+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.