lemon-tls 0.4.1 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -2
- package/index.js +10 -0
- package/package.json +5 -3
- package/src/compat.js +30 -17
- package/src/crypto.js +92 -2
- package/src/dtls_session.js +487 -56
- package/src/dtls_socket.js +22 -0
- package/src/record.js +133 -5
- package/src/session/ecdh.js +16 -1
- package/src/tls_session.js +224 -32
- package/src/tls_socket.js +44 -1
- package/src/utils.js +28 -1
package/src/tls_socket.js
CHANGED
|
@@ -331,6 +331,18 @@ function TLSSocket(duplex, options){
|
|
|
331
331
|
? context.isTls13
|
|
332
332
|
: session.getVersion() === 0x0304; // early records (pre-secureConnect)
|
|
333
333
|
|
|
334
|
+
// AEAD decryption can throw on any forged/corrupted record (GCM/ChaCha
|
|
335
|
+
// tag mismatch surfaces as an exception from decipher.final()). Left
|
|
336
|
+
// unhandled, a single injected bad segment would throw out of the
|
|
337
|
+
// transport 'data' handler — an unhandled exception that tears down the
|
|
338
|
+
// connection or the process: a trivial remote DoS.
|
|
339
|
+
//
|
|
340
|
+
// RFC 8446 §5.2: in TLS (unlike DTLS, which silently drops), a record
|
|
341
|
+
// that fails to decrypt MUST be treated as a fatal error. So we catch,
|
|
342
|
+
// send a fatal bad_record_mac alert, destroy the connection, and stop.
|
|
343
|
+
// The read sequence number is intentionally NOT advanced on failure —
|
|
344
|
+
// the rejected record never counts.
|
|
345
|
+
try {
|
|
334
346
|
if (!isTls13 && context.remote_ccs_seen === true) {
|
|
335
347
|
// TLS 1.2: decrypt with key_block derived keys
|
|
336
348
|
if (context.app_read_key === null) {
|
|
@@ -394,7 +406,14 @@ function TLSSocket(duplex, options){
|
|
|
394
406
|
}
|
|
395
407
|
context.handshake_read_seq++;
|
|
396
408
|
}
|
|
397
|
-
|
|
409
|
+
} catch (e) {
|
|
410
|
+
// Record failed authentication → fatal bad_record_mac (RFC 8446 §5.2).
|
|
411
|
+
try { session.sendAlert ? session.sendAlert(2, TLS_ALERT.BAD_RECORD_MAC) : null; } catch (_) {}
|
|
412
|
+
self.emit('error', new Error('TLS record decryption failed (bad_record_mac)'));
|
|
413
|
+
self.destroy();
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
398
417
|
|
|
399
418
|
if (out !== null) {
|
|
400
419
|
// Reuse cached isTls13 computed at the top of this function
|
|
@@ -1427,6 +1446,30 @@ function TLSSocket(duplex, options){
|
|
|
1427
1446
|
/** Full negotiation result — all selected parameters in one object. */
|
|
1428
1447
|
self.getNegotiationResult = function(){ return session.getNegotiationResult ? session.getNegotiationResult() : null; };
|
|
1429
1448
|
|
|
1449
|
+
/**
|
|
1450
|
+
* Export keying material (RFC 5705 for TLS 1.2, RFC 8446 §7.5 for
|
|
1451
|
+
* TLS 1.3). Mirrors Node's tls.TLSSocket#exportKeyingMaterial —
|
|
1452
|
+
* same argument order (length, label, context). One deliberate
|
|
1453
|
+
* difference: Node THROWS before the handshake completes; we return
|
|
1454
|
+
* null (matching this library's null-until-ready convention used by
|
|
1455
|
+
* getTrafficSecrets etc.).
|
|
1456
|
+
*/
|
|
1457
|
+
self.exportKeyingMaterial = function(length, label, context_value){
|
|
1458
|
+
return session.exportKeyingMaterial
|
|
1459
|
+
? session.exportKeyingMaterial(length, label, context_value)
|
|
1460
|
+
: null;
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
/** Peer hello extensions: [{ type, name, data, value }]. */
|
|
1464
|
+
self.getRemoteExtensions = function(){
|
|
1465
|
+
return session.getRemoteExtensions ? session.getRemoteExtensions() : [];
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
/** One peer extension by numeric type, or null. */
|
|
1469
|
+
self.getRemoteExtension = function(type){
|
|
1470
|
+
return session.getRemoteExtension ? session.getRemoteExtension(type) : null;
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1430
1473
|
/** Request Key Update — refresh outgoing encryption keys (TLS 1.3 only). */
|
|
1431
1474
|
self.rekeySend = function(){ if (session.requestKeyUpdate) session.requestKeyUpdate(false); };
|
|
1432
1475
|
|
package/src/utils.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
|
|
2
3
|
function concatUint8Arrays(arrays) {
|
|
3
4
|
let totalLength = 0;
|
|
@@ -93,9 +94,35 @@ function uint8Equal(a, b) {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Constant-time equality for two byte arrays.
|
|
99
|
+
*
|
|
100
|
+
* Unlike uint8Equal (which short-circuits on the first differing byte and
|
|
101
|
+
* therefore leaks — via timing — how many leading bytes matched), this always
|
|
102
|
+
* compares the full length. Use it wherever a mismatch would otherwise be
|
|
103
|
+
* exploitable as an oracle: Finished verify_data, PSK binders, ticket key_name,
|
|
104
|
+
* and any MAC/tag comparison.
|
|
105
|
+
*
|
|
106
|
+
* Returns false immediately (and safely) on length mismatch — that's public
|
|
107
|
+
* information, not a secret, so short-circuiting there is fine.
|
|
108
|
+
*
|
|
109
|
+
* Note: crypto.timingSafeEqual requires two equal-length Buffers. We wrap the
|
|
110
|
+
* inputs as Buffer views over the same memory (no copy) via byteOffset/length.
|
|
111
|
+
*/
|
|
112
|
+
function timingSafeEqualU8(a, b) {
|
|
113
|
+
if (a == null || b == null) return false;
|
|
114
|
+
if (a.length !== b.length) return false;
|
|
115
|
+
if (a.length === 0) return true;
|
|
116
|
+
let ba = Buffer.isBuffer(a) ? a : Buffer.from(a.buffer, a.byteOffset, a.length);
|
|
117
|
+
let bb = Buffer.isBuffer(b) ? b : Buffer.from(b.buffer, b.byteOffset, b.length);
|
|
118
|
+
return crypto.timingSafeEqual(ba, bb);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
96
122
|
export {
|
|
97
123
|
concatUint8Arrays,
|
|
98
124
|
arraybufferEqual,
|
|
99
125
|
arraysEqual,
|
|
100
|
-
uint8Equal
|
|
126
|
+
uint8Equal,
|
|
127
|
+
timingSafeEqualU8
|
|
101
128
|
};
|