import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from './plugins.js'; import { createTestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../helpers/smtp.client.js'; tap.test('CSEC-07: should handle cipher suites correctly', async (tools) => { const testId = 'CSEC-07-cipher-suites'; console.log(`\n${testId}: Testing cipher suite handling...`); let scenarioCount = 0; // Scenario 1: Strong cipher suite negotiation await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing strong cipher suite negotiation`); const testServer = await createTestServer({ secure: true, tlsOptions: { // Configure strong ciphers only ciphers: 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384', honorCipherOrder: true, minVersion: 'TLSv1.2' }, onConnection: async (socket) => { console.log(' [Server] Client connected with strong ciphers'); // Log cipher info if available const tlsSocket = socket as any; if (tlsSocket.getCipher) { const cipher = tlsSocket.getCipher(); console.log(` [Server] Negotiated cipher: ${cipher.name} (${cipher.version})`); } socket.write('220 secure.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-secure.example.com\r\n'); socket.write('250-SIZE 10485760\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK: Message accepted with strong encryption\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, // Prefer strong ciphers ciphers: 'HIGH:!aNULL:!MD5:!3DES', minVersion: 'TLSv1.2' } }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Strong cipher test', text: 'Testing with strong cipher suites' }); const result = await smtpClient.sendMail(email); console.log(' Successfully negotiated strong cipher'); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); await testServer.server.close(); })(); // Scenario 2: Weak cipher rejection await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing weak cipher rejection`); const testServer = await createTestServer({ secure: true, tlsOptions: { // Only allow strong ciphers ciphers: 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256', honorCipherOrder: true, minVersion: 'TLSv1.2' }, onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 secure.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-secure.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Try to connect with weak ciphers only (should fail) const weakClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, // Try to use weak ciphers ciphers: 'DES-CBC3-SHA:RC4-SHA', maxVersion: 'TLSv1.0' } }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Weak cipher test', text: 'Testing weak cipher rejection' }); try { await weakClient.sendMail(email); console.log(' Unexpected: Weak cipher was accepted'); } catch (error) { console.log(` Expected error: ${error.message}`); expect(error.message).toMatch(/handshake|cipher|ssl/i); } // Connect with acceptable ciphers const strongClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, ciphers: 'HIGH:!aNULL', minVersion: 'TLSv1.2' } }); const result = await strongClient.sendMail(email); console.log(' Successfully connected with strong ciphers'); expect(result).toBeDefined(); await testServer.server.close(); })(); // Scenario 3: Cipher suite priority testing await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing cipher suite priority`); const preferredCiphers = [ 'TLS_AES_256_GCM_SHA384', 'TLS_AES_128_GCM_SHA256', 'ECDHE-RSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES128-GCM-SHA256' ]; const testServer = await createTestServer({ secure: true, tlsOptions: { ciphers: preferredCiphers.join(':'), honorCipherOrder: true, // Server chooses cipher minVersion: 'TLSv1.2' }, onConnection: async (socket) => { console.log(' [Server] Client connected'); const tlsSocket = socket as any; if (tlsSocket.getCipher) { const cipher = tlsSocket.getCipher(); console.log(` [Server] Selected cipher: ${cipher.name}`); // Check if preferred cipher was selected const cipherIndex = preferredCiphers.findIndex(c => cipher.name.includes(c) || c.includes(cipher.name) ); console.log(` [Server] Cipher priority: ${cipherIndex + 1}/${preferredCiphers.length}`); } socket.write('220 priority.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-priority.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, // Client offers ciphers in different order ciphers: preferredCiphers.slice().reverse().join(':'), honorCipherOrder: false // Let server choose } }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Cipher priority test', text: 'Testing cipher suite selection priority' }); const result = await smtpClient.sendMail(email); console.log(' Cipher negotiation completed'); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); await testServer.server.close(); })(); // Scenario 4: Perfect Forward Secrecy (PFS) ciphers await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing Perfect Forward Secrecy ciphers`); const testServer = await createTestServer({ secure: true, tlsOptions: { // Only PFS ciphers (ECDHE/DHE) ciphers: 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384', honorCipherOrder: true, ecdhCurve: 'auto' }, onConnection: async (socket) => { console.log(' [Server] Client connected with PFS'); const tlsSocket = socket as any; if (tlsSocket.getCipher) { const cipher = tlsSocket.getCipher(); const hasPFS = cipher.name.includes('ECDHE') || cipher.name.includes('DHE'); console.log(` [Server] Cipher: ${cipher.name} (PFS: ${hasPFS ? 'Yes' : 'No'})`); } socket.write('220 pfs.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-pfs.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK: Message sent with PFS\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, // Prefer PFS ciphers ciphers: 'ECDHE:DHE:!aNULL:!MD5', ecdhCurve: 'auto' } }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'PFS cipher test', text: 'Testing Perfect Forward Secrecy' }); const result = await smtpClient.sendMail(email); console.log(' Successfully used PFS cipher'); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); await testServer.server.close(); })(); // Scenario 5: Cipher renegotiation await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing cipher renegotiation handling`); const testServer = await createTestServer({ secure: true, tlsOptions: { ciphers: 'HIGH:MEDIUM:!aNULL:!MD5', // Disable renegotiation for security secureOptions: plugins.crypto.constants.SSL_OP_NO_RENEGOTIATION }, onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 norenegotiation.example.com ESMTP\r\n'); let messageCount = 0; socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-norenegotiation.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { messageCount++; console.log(` [Server] Processing message ${messageCount}`); socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'RSET') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, // Also disable renegotiation on client secureOptions: plugins.crypto.constants.SSL_OP_NO_RENEGOTIATION } }); // Send multiple emails on same connection for (let i = 0; i < 3; i++) { const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: `Renegotiation test ${i + 1}`, text: `Testing without cipher renegotiation - email ${i + 1}` }); const result = await smtpClient.sendMail(email); console.log(` Email ${i + 1} sent without renegotiation`); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); } await testServer.server.close(); })(); // Scenario 6: Cipher compatibility testing await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing cipher compatibility`); const cipherSets = [ { name: 'TLS 1.3 only', ciphers: 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256', minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3' }, { name: 'TLS 1.2 compatible', ciphers: 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256', minVersion: 'TLSv1.2', maxVersion: 'TLSv1.2' }, { name: 'Broad compatibility', ciphers: 'HIGH:MEDIUM:!aNULL:!MD5:!3DES', minVersion: 'TLSv1.2', maxVersion: undefined } ]; for (const cipherSet of cipherSets) { console.log(`\n Testing ${cipherSet.name}...`); const testServer = await createTestServer({ secure: true, tlsOptions: { ciphers: cipherSet.ciphers, minVersion: cipherSet.minVersion as any, maxVersion: cipherSet.maxVersion as any }, onConnection: async (socket) => { const tlsSocket = socket as any; if (tlsSocket.getCipher && tlsSocket.getProtocol) { const cipher = tlsSocket.getCipher(); const protocol = tlsSocket.getProtocol(); console.log(` [Server] Protocol: ${protocol}, Cipher: ${cipher.name}`); } socket.write('220 compat.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); if (command.startsWith('EHLO')) { socket.write('250-compat.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); try { const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: true, tls: { rejectUnauthorized: false, ciphers: cipherSet.ciphers, minVersion: cipherSet.minVersion as any, maxVersion: cipherSet.maxVersion as any } }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: `${cipherSet.name} test`, text: `Testing ${cipherSet.name} cipher configuration` }); const result = await smtpClient.sendMail(email); console.log(` Success with ${cipherSet.name}`); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); } catch (error) { console.log(` ${cipherSet.name} not supported in this environment`); } await testServer.server.close(); } })(); console.log(`\n${testId}: All ${scenarioCount} cipher suite scenarios tested ✓`); });