201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
|
import * as plugins from '../../plugins.js';
|
||
|
|
||
|
/**
|
||
|
* TLS record types as defined in various RFCs
|
||
|
*/
|
||
|
export enum TlsRecordType {
|
||
|
CHANGE_CIPHER_SPEC = 20,
|
||
|
ALERT = 21,
|
||
|
HANDSHAKE = 22,
|
||
|
APPLICATION_DATA = 23,
|
||
|
HEARTBEAT = 24, // RFC 6520
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* TLS handshake message types
|
||
|
*/
|
||
|
export enum TlsHandshakeType {
|
||
|
HELLO_REQUEST = 0,
|
||
|
CLIENT_HELLO = 1,
|
||
|
SERVER_HELLO = 2,
|
||
|
NEW_SESSION_TICKET = 4,
|
||
|
ENCRYPTED_EXTENSIONS = 8, // TLS 1.3
|
||
|
CERTIFICATE = 11,
|
||
|
SERVER_KEY_EXCHANGE = 12,
|
||
|
CERTIFICATE_REQUEST = 13,
|
||
|
SERVER_HELLO_DONE = 14,
|
||
|
CERTIFICATE_VERIFY = 15,
|
||
|
CLIENT_KEY_EXCHANGE = 16,
|
||
|
FINISHED = 20,
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* TLS extension types
|
||
|
*/
|
||
|
export enum TlsExtensionType {
|
||
|
SERVER_NAME = 0, // SNI
|
||
|
MAX_FRAGMENT_LENGTH = 1,
|
||
|
CLIENT_CERTIFICATE_URL = 2,
|
||
|
TRUSTED_CA_KEYS = 3,
|
||
|
TRUNCATED_HMAC = 4,
|
||
|
STATUS_REQUEST = 5, // OCSP
|
||
|
SUPPORTED_GROUPS = 10, // Previously named "elliptic_curves"
|
||
|
EC_POINT_FORMATS = 11,
|
||
|
SIGNATURE_ALGORITHMS = 13,
|
||
|
APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, // ALPN
|
||
|
SIGNED_CERTIFICATE_TIMESTAMP = 18, // Certificate Transparency
|
||
|
PADDING = 21,
|
||
|
SESSION_TICKET = 35,
|
||
|
PRE_SHARED_KEY = 41, // TLS 1.3
|
||
|
EARLY_DATA = 42, // TLS 1.3 0-RTT
|
||
|
SUPPORTED_VERSIONS = 43, // TLS 1.3
|
||
|
COOKIE = 44, // TLS 1.3
|
||
|
PSK_KEY_EXCHANGE_MODES = 45, // TLS 1.3
|
||
|
CERTIFICATE_AUTHORITIES = 47, // TLS 1.3
|
||
|
POST_HANDSHAKE_AUTH = 49, // TLS 1.3
|
||
|
SIGNATURE_ALGORITHMS_CERT = 50, // TLS 1.3
|
||
|
KEY_SHARE = 51, // TLS 1.3
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* TLS alert levels
|
||
|
*/
|
||
|
export enum TlsAlertLevel {
|
||
|
WARNING = 1,
|
||
|
FATAL = 2,
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* TLS alert description codes
|
||
|
*/
|
||
|
export enum TlsAlertDescription {
|
||
|
CLOSE_NOTIFY = 0,
|
||
|
UNEXPECTED_MESSAGE = 10,
|
||
|
BAD_RECORD_MAC = 20,
|
||
|
DECRYPTION_FAILED = 21, // TLS 1.0 only
|
||
|
RECORD_OVERFLOW = 22,
|
||
|
DECOMPRESSION_FAILURE = 30, // TLS 1.2 and below
|
||
|
HANDSHAKE_FAILURE = 40,
|
||
|
NO_CERTIFICATE = 41, // SSLv3 only
|
||
|
BAD_CERTIFICATE = 42,
|
||
|
UNSUPPORTED_CERTIFICATE = 43,
|
||
|
CERTIFICATE_REVOKED = 44,
|
||
|
CERTIFICATE_EXPIRED = 45,
|
||
|
CERTIFICATE_UNKNOWN = 46,
|
||
|
ILLEGAL_PARAMETER = 47,
|
||
|
UNKNOWN_CA = 48,
|
||
|
ACCESS_DENIED = 49,
|
||
|
DECODE_ERROR = 50,
|
||
|
DECRYPT_ERROR = 51,
|
||
|
EXPORT_RESTRICTION = 60, // TLS 1.0 only
|
||
|
PROTOCOL_VERSION = 70,
|
||
|
INSUFFICIENT_SECURITY = 71,
|
||
|
INTERNAL_ERROR = 80,
|
||
|
INAPPROPRIATE_FALLBACK = 86,
|
||
|
USER_CANCELED = 90,
|
||
|
NO_RENEGOTIATION = 100, // TLS 1.2 and below
|
||
|
MISSING_EXTENSION = 109, // TLS 1.3
|
||
|
UNSUPPORTED_EXTENSION = 110, // TLS 1.3
|
||
|
CERTIFICATE_REQUIRED = 111, // TLS 1.3
|
||
|
UNRECOGNIZED_NAME = 112,
|
||
|
BAD_CERTIFICATE_STATUS_RESPONSE = 113,
|
||
|
BAD_CERTIFICATE_HASH_VALUE = 114, // TLS 1.2 and below
|
||
|
UNKNOWN_PSK_IDENTITY = 115,
|
||
|
CERTIFICATE_REQUIRED_1_3 = 116, // TLS 1.3
|
||
|
NO_APPLICATION_PROTOCOL = 120,
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* TLS version codes (major.minor)
|
||
|
*/
|
||
|
export const TlsVersion = {
|
||
|
SSL3: [0x03, 0x00],
|
||
|
TLS1_0: [0x03, 0x01],
|
||
|
TLS1_1: [0x03, 0x02],
|
||
|
TLS1_2: [0x03, 0x03],
|
||
|
TLS1_3: [0x03, 0x04],
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Utility functions for TLS protocol operations
|
||
|
*/
|
||
|
export class TlsUtils {
|
||
|
/**
|
||
|
* Checks if a buffer contains a TLS handshake record
|
||
|
* @param buffer The buffer to check
|
||
|
* @returns true if the buffer starts with a TLS handshake record
|
||
|
*/
|
||
|
public static isTlsHandshake(buffer: Buffer): boolean {
|
||
|
return buffer.length > 0 && buffer[0] === TlsRecordType.HANDSHAKE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a buffer contains TLS application data
|
||
|
* @param buffer The buffer to check
|
||
|
* @returns true if the buffer starts with a TLS application data record
|
||
|
*/
|
||
|
public static isTlsApplicationData(buffer: Buffer): boolean {
|
||
|
return buffer.length > 0 && buffer[0] === TlsRecordType.APPLICATION_DATA;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a buffer contains a TLS alert record
|
||
|
* @param buffer The buffer to check
|
||
|
* @returns true if the buffer starts with a TLS alert record
|
||
|
*/
|
||
|
public static isTlsAlert(buffer: Buffer): boolean {
|
||
|
return buffer.length > 0 && buffer[0] === TlsRecordType.ALERT;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a buffer contains a TLS ClientHello message
|
||
|
* @param buffer The buffer to check
|
||
|
* @returns true if the buffer appears to be a ClientHello message
|
||
|
*/
|
||
|
public static isClientHello(buffer: Buffer): boolean {
|
||
|
// Minimum ClientHello size (TLS record header + handshake header)
|
||
|
if (buffer.length < 9) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Check record type (must be TLS_HANDSHAKE_RECORD_TYPE)
|
||
|
if (buffer[0] !== TlsRecordType.HANDSHAKE) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Skip version and length in TLS record header (5 bytes total)
|
||
|
// Check handshake type at byte 5 (must be CLIENT_HELLO)
|
||
|
return buffer[5] === TlsHandshakeType.CLIENT_HELLO;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the record length from a TLS record header
|
||
|
* @param buffer Buffer containing a TLS record
|
||
|
* @returns The record length if the buffer is valid, -1 otherwise
|
||
|
*/
|
||
|
public static getTlsRecordLength(buffer: Buffer): number {
|
||
|
if (buffer.length < 5) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Bytes 3-4 contain the record length (big-endian)
|
||
|
return (buffer[3] << 8) + buffer[4];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a connection ID based on source/destination information
|
||
|
* Used to track fragmented ClientHello messages across multiple packets
|
||
|
*
|
||
|
* @param connectionInfo Object containing connection identifiers
|
||
|
* @returns A string ID for the connection
|
||
|
*/
|
||
|
public static createConnectionId(connectionInfo: {
|
||
|
sourceIp?: string;
|
||
|
sourcePort?: number;
|
||
|
destIp?: string;
|
||
|
destPort?: number;
|
||
|
}): string {
|
||
|
const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
|
||
|
return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
|
||
|
}
|
||
|
}
|