feat(PortProxy/TLS): Add allowSessionTicket option to control TLS session ticket handling
This commit is contained in:
@ -279,6 +279,118 @@ export class SniHandler {
|
||||
return buffer[5] === this.TLS_CLIENT_HELLO_HANDSHAKE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a ClientHello message contains session resumption indicators
|
||||
* such as session tickets or PSK (Pre-Shared Key) extensions.
|
||||
*
|
||||
* @param buffer - The buffer containing a ClientHello message
|
||||
* @param enableLogging - Whether to enable logging
|
||||
* @returns true if the ClientHello contains session resumption mechanisms
|
||||
*/
|
||||
public static hasSessionResumption(
|
||||
buffer: Buffer,
|
||||
enableLogging: boolean = false
|
||||
): boolean {
|
||||
const log = (message: string) => {
|
||||
if (enableLogging) {
|
||||
console.log(`[Session Resumption] ${message}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (!this.isClientHello(buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check for session ID presence first
|
||||
let pos = 5 + 1 + 3 + 2; // Position after handshake type, length and client version
|
||||
pos += 32; // Skip client random
|
||||
|
||||
if (pos + 1 > buffer.length) return false;
|
||||
|
||||
const sessionIdLength = buffer[pos];
|
||||
let hasNonEmptySessionId = sessionIdLength > 0;
|
||||
|
||||
if (hasNonEmptySessionId) {
|
||||
log(`Detected non-empty session ID (length: ${sessionIdLength})`);
|
||||
}
|
||||
|
||||
// Continue to check for extensions
|
||||
pos += 1 + sessionIdLength;
|
||||
|
||||
// Skip cipher suites
|
||||
if (pos + 2 > buffer.length) return false;
|
||||
const cipherSuitesLength = (buffer[pos] << 8) + buffer[pos + 1];
|
||||
pos += 2 + cipherSuitesLength;
|
||||
|
||||
// Skip compression methods
|
||||
if (pos + 1 > buffer.length) return false;
|
||||
const compressionMethodsLength = buffer[pos];
|
||||
pos += 1 + compressionMethodsLength;
|
||||
|
||||
// Check for extensions
|
||||
if (pos + 2 > buffer.length) return false;
|
||||
|
||||
// Look for session resumption extensions
|
||||
const extensionsLength = (buffer[pos] << 8) + buffer[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
// Extensions end position
|
||||
const extensionsEnd = pos + extensionsLength;
|
||||
if (extensionsEnd > buffer.length) return false;
|
||||
|
||||
// Track resumption indicators
|
||||
let hasSessionTicket = false;
|
||||
let hasPSK = false;
|
||||
let hasEarlyData = false;
|
||||
|
||||
// Iterate through extensions
|
||||
while (pos + 4 <= extensionsEnd) {
|
||||
const extensionType = (buffer[pos] << 8) + buffer[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
const extensionLength = (buffer[pos] << 8) + buffer[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
if (extensionType === this.TLS_SESSION_TICKET_EXTENSION_TYPE) {
|
||||
log('Found session ticket extension');
|
||||
hasSessionTicket = true;
|
||||
|
||||
// Check if session ticket has non-zero length (active ticket)
|
||||
if (extensionLength > 0) {
|
||||
log(`Session ticket has length ${extensionLength} - active ticket present`);
|
||||
}
|
||||
} else if (extensionType === this.TLS_PSK_EXTENSION_TYPE) {
|
||||
log('Found PSK extension (TLS 1.3 resumption mechanism)');
|
||||
hasPSK = true;
|
||||
} else if (extensionType === this.TLS_EARLY_DATA_EXTENSION_TYPE) {
|
||||
log('Found Early Data extension (TLS 1.3 0-RTT)');
|
||||
hasEarlyData = true;
|
||||
}
|
||||
|
||||
// Skip extension data
|
||||
pos += extensionLength;
|
||||
}
|
||||
|
||||
// Consider it a resumption if any resumption mechanism is present
|
||||
const isResumption = hasSessionTicket || hasPSK || hasEarlyData ||
|
||||
(hasNonEmptySessionId && !hasPSK); // Legacy resumption
|
||||
|
||||
if (isResumption) {
|
||||
log('Session resumption detected: ' +
|
||||
(hasSessionTicket ? 'session ticket, ' : '') +
|
||||
(hasPSK ? 'PSK, ' : '') +
|
||||
(hasEarlyData ? 'early data, ' : '') +
|
||||
(hasNonEmptySessionId ? 'session ID' : ''));
|
||||
}
|
||||
|
||||
return isResumption;
|
||||
} catch (error) {
|
||||
log(`Error checking for session resumption: ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects characteristics of a tab reactivation TLS handshake
|
||||
* These often have specific patterns in Chrome and other browsers
|
||||
|
Reference in New Issue
Block a user