253 lines
8.0 KiB
TypeScript
253 lines
8.0 KiB
TypeScript
/**
|
|
* Comprehensive TLS Connection Test Suite
|
|
* Tests all aspects of raw TLS S2S connection establishment
|
|
*/
|
|
|
|
import * as tls from 'tls';
|
|
import * as crypto from 'crypto';
|
|
import * as fs from 'fs';
|
|
import { TLSClient, TLSConnection } from '@/transport/tls-client/tls-client';
|
|
import { receiverConfig } from '@/config/receiver-config';
|
|
|
|
describe('TLS Connection Tests', () => {
|
|
const RECEIVER_IP = '172.67.157.88';
|
|
const RECEIVER_PORT = 443;
|
|
const RECEIVER_PORT_ALT = 8443;
|
|
const RECEIVER_SNI = 'devmindgroup.com';
|
|
const EXPECTED_SHA256_FINGERPRINT = 'b19f2a94eab4cd3b92f1e3e0dce9d5e41c8b7aa3fdbe6e2f4ac3c91a5fbb2f44';
|
|
|
|
describe('Connection Parameters', () => {
|
|
it('should have correct receiver IP configured', () => {
|
|
expect(receiverConfig.ip).toBe(RECEIVER_IP);
|
|
});
|
|
|
|
it('should have correct receiver port configured', () => {
|
|
expect(receiverConfig.port).toBe(RECEIVER_PORT);
|
|
});
|
|
|
|
it('should have correct SNI configured', () => {
|
|
expect(receiverConfig.sni).toBe(RECEIVER_SNI);
|
|
});
|
|
|
|
it('should have TLS version configured', () => {
|
|
expect(receiverConfig.tlsVersion).toBeDefined();
|
|
expect(['TLSv1.2', 'TLSv1.3']).toContain(receiverConfig.tlsVersion);
|
|
});
|
|
|
|
it('should have length-prefix framing configured', () => {
|
|
expect(receiverConfig.framing).toBe('length-prefix-4be');
|
|
});
|
|
});
|
|
|
|
describe('Raw TLS Socket Connection', () => {
|
|
let tlsClient: TLSClient;
|
|
let connection: TLSConnection | null = null;
|
|
|
|
beforeEach(() => {
|
|
tlsClient = new TLSClient();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
if (connection) {
|
|
await tlsClient.close();
|
|
connection = null;
|
|
}
|
|
});
|
|
|
|
it('should establish TLS connection to receiver IP', async () => {
|
|
connection = await tlsClient.connect();
|
|
|
|
expect(connection).toBeDefined();
|
|
expect(connection.connected).toBe(true);
|
|
expect(connection.socket).toBeDefined();
|
|
expect(connection.sessionId).toBeDefined();
|
|
}, 60000); // 60 second timeout for network operations
|
|
|
|
it('should use correct SNI in TLS handshake', async () => {
|
|
const tlsOptions: tls.ConnectionOptions = {
|
|
host: RECEIVER_IP,
|
|
port: RECEIVER_PORT,
|
|
servername: RECEIVER_SNI,
|
|
rejectUnauthorized: false, // For testing only
|
|
};
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
const socket = tls.connect(tlsOptions, () => {
|
|
const servername = (socket as any).servername;
|
|
expect(servername).toBe(RECEIVER_SNI);
|
|
socket.end();
|
|
resolve();
|
|
});
|
|
|
|
socket.on('error', reject);
|
|
socket.setTimeout(30000);
|
|
socket.on('timeout', () => {
|
|
socket.destroy();
|
|
reject(new Error('Connection timeout'));
|
|
});
|
|
});
|
|
}, 60000);
|
|
|
|
it('should verify server certificate SHA256 fingerprint', async () => {
|
|
const tlsOptions: tls.ConnectionOptions = {
|
|
host: RECEIVER_IP,
|
|
port: RECEIVER_PORT,
|
|
servername: RECEIVER_SNI,
|
|
rejectUnauthorized: false, // We'll verify manually
|
|
};
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
const socket = tls.connect(tlsOptions, () => {
|
|
try {
|
|
const cert = socket.getPeerCertificate(true);
|
|
if (cert && cert.raw) {
|
|
const fingerprint = crypto
|
|
.createHash('sha256')
|
|
.update(cert.raw)
|
|
.digest('hex')
|
|
.toLowerCase();
|
|
|
|
expect(fingerprint).toBe(EXPECTED_SHA256_FINGERPRINT.toLowerCase());
|
|
socket.end();
|
|
resolve();
|
|
} else {
|
|
reject(new Error('Certificate not available'));
|
|
}
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
|
|
socket.on('error', reject);
|
|
socket.setTimeout(30000);
|
|
socket.on('timeout', () => {
|
|
socket.destroy();
|
|
reject(new Error('Connection timeout'));
|
|
});
|
|
});
|
|
}, 60000);
|
|
|
|
it('should use TLSv1.2 or higher', async () => {
|
|
connection = await tlsClient.connect();
|
|
|
|
const protocol = connection.socket.getProtocol();
|
|
expect(protocol).toBeDefined();
|
|
expect(['TLSv1.2', 'TLSv1.3']).toContain(protocol);
|
|
}, 60000);
|
|
|
|
it('should handle connection to alternate port 8443', async () => {
|
|
const tlsOptions: tls.ConnectionOptions = {
|
|
host: RECEIVER_IP,
|
|
port: RECEIVER_PORT_ALT,
|
|
servername: RECEIVER_SNI,
|
|
rejectUnauthorized: false,
|
|
minVersion: 'TLSv1.2',
|
|
};
|
|
|
|
await new Promise<void>((resolve) => {
|
|
const socket = tls.connect(tlsOptions, () => {
|
|
expect(socket.authorized || true).toBeDefined(); // May or may not be authorized
|
|
socket.end();
|
|
resolve();
|
|
});
|
|
|
|
socket.on('error', (error) => {
|
|
// Port might not be available, that's okay for testing
|
|
console.warn(`Port ${RECEIVER_PORT_ALT} connection test:`, error.message);
|
|
resolve(); // Don't fail if port is not available
|
|
});
|
|
|
|
socket.setTimeout(30000);
|
|
socket.on('timeout', () => {
|
|
socket.destroy();
|
|
resolve(); // Don't fail on timeout for alternate port
|
|
});
|
|
});
|
|
}, 60000);
|
|
|
|
it('should record TLS session with fingerprint', async () => {
|
|
connection = await tlsClient.connect();
|
|
|
|
expect(connection.fingerprint).toBeDefined();
|
|
expect(connection.fingerprint.length).toBeGreaterThan(0);
|
|
expect(connection.sessionId).toBeDefined();
|
|
expect(connection.sessionId.length).toBeGreaterThan(0);
|
|
}, 60000);
|
|
|
|
it('should handle connection errors gracefully', async () => {
|
|
const invalidTlsClient = new TLSClient();
|
|
// Temporarily override config to use invalid IP
|
|
const originalIp = receiverConfig.ip;
|
|
(receiverConfig as any).ip = '192.0.2.1'; // Invalid test IP
|
|
|
|
try {
|
|
await expect(invalidTlsClient.connect()).rejects.toThrow();
|
|
} finally {
|
|
(receiverConfig as any).ip = originalIp;
|
|
await invalidTlsClient.close();
|
|
}
|
|
}, 30000);
|
|
|
|
it('should timeout after configured timeout period', async () => {
|
|
const timeoutClient = new TLSClient();
|
|
const originalIp = receiverConfig.ip;
|
|
(receiverConfig as any).ip = '10.255.255.1'; // Unreachable IP
|
|
|
|
try {
|
|
await expect(timeoutClient.connect()).rejects.toThrow();
|
|
} finally {
|
|
(receiverConfig as any).ip = originalIp;
|
|
await timeoutClient.close();
|
|
}
|
|
}, 35000);
|
|
});
|
|
|
|
describe('Mutual TLS (mTLS)', () => {
|
|
it('should support client certificate if configured', () => {
|
|
// Check if mTLS paths are configured
|
|
if (receiverConfig.clientCertPath && receiverConfig.clientKeyPath) {
|
|
expect(fs.existsSync(receiverConfig.clientCertPath)).toBe(true);
|
|
expect(fs.existsSync(receiverConfig.clientKeyPath)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('should support CA certificate bundle if configured', () => {
|
|
if (receiverConfig.caCertPath) {
|
|
expect(fs.existsSync(receiverConfig.caCertPath)).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Connection Reuse', () => {
|
|
let tlsClient: TLSClient;
|
|
|
|
beforeEach(() => {
|
|
tlsClient = new TLSClient();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await tlsClient.close();
|
|
});
|
|
|
|
it('should reuse existing connection if available', async () => {
|
|
const connection1 = await tlsClient.connect();
|
|
const connection2 = await tlsClient.connect();
|
|
|
|
expect(connection1.sessionId).toBe(connection2.sessionId);
|
|
expect(connection1.socket).toBe(connection2.socket);
|
|
}, 60000);
|
|
|
|
it('should create new connection if previous one closed', async () => {
|
|
const connection1 = await tlsClient.connect();
|
|
const sessionId1 = connection1.sessionId;
|
|
|
|
await tlsClient.close();
|
|
|
|
const connection2 = await tlsClient.connect();
|
|
const sessionId2 = connection2.sessionId;
|
|
|
|
expect(sessionId1).not.toBe(sessionId2);
|
|
}, 60000);
|
|
});
|
|
});
|