package unit; /** * OpenSslCipherSetTest — CWE-407 unit test for openssl-0001 / openssl-0002. * * Models the defective and fixed patterns from OpenSSL: * openssl-0001: SSL_get_shared_ciphers — O(n*m) linear find vs O(n+m) hash-set * openssl-0002: ciphersuite_cb dedup — O(n²) linear scan vs O(n) bitmask * * No JUnit. Standalone: javac OpenSslCipherSetTest.java && java unit.OpenSslCipherSetTest */ public class OpenSslCipherSetTest { // ----------------------------------------------------------------------- // Defect model: openssl-0001 // // slow(): for each client cipher, scan entire server array linearly. // Returns (comparison count). // fast(): build hash-set of server IDs first, then O(1) lookup per client. // Returns (comparison count). // ----------------------------------------------------------------------- static long sharedCiphersSlow(int[] clientIds, int[] serverIds) { long ops = 0; for (int cid : clientIds) { for (int sid : serverIds) { // O(m) linear scan per client cipher ops++; if (sid == cid) break; } } return ops; } static long sharedCiphersFast(int[] clientIds, int[] serverIds) { // Build hash-set: open addressing, power-of-2 table int tableSize = Integer.highestOneBit(serverIds.length * 4); // load ~25% int[] table = new int[tableSize]; // 0 = empty slot long ops = 0; for (int sid : serverIds) { int slot = (sid * 0x9e3779b9) & (tableSize - 1); while (table[slot] != 0 && table[slot] != sid) slot = (slot + 1) & (tableSize - 1); table[slot] = sid; ops++; // one insert op each } for (int cid : clientIds) { int slot = (cid * 0x9e3779b9) & (tableSize - 1); while (table[slot] != 0 && table[slot] != cid) slot = (slot + 1) & (tableSize - 1); ops++; // one probe op each } return ops; } // ----------------------------------------------------------------------- // Defect model: openssl-0002 // // slow(): for each new cipher in the input list, scan all already-added // ciphers to suppress duplicates. Returns comparison count. // fast(): bitmask dedup — O(1) per element. Returns op count. // ----------------------------------------------------------------------- static long ciphersuiteDeduplicateSlow(int[] inputIds) { int[] added = new int[inputIds.length]; int addedCount = 0; long ops = 0; for (int id : inputIds) { boolean dup = false; for (int i = 0; i < addedCount; i++) { // O(k) scan ops++; if (added[i] == id) { dup = true; break; } } if (!dup) added[addedCount++] = id; } return ops; } static long ciphersuiteDeduplicateFast(int[] inputIds) { long bits = 0L; // bitmask for up to 64 IDs (sufficient for TLS 1.3 suites) long ops = 0; for (int id : inputIds) { ops++; // one bitmask check+set per element int bit = id & 63; bits |= (1L << bit); } return ops; } // ----------------------------------------------------------------------- // Test runner // ----------------------------------------------------------------------- static void assertGt(long slow, long fast, int nx, String label) { if (slow <= fast * nx) { System.out.println("FAIL " + label + ": slow=" + slow + " fast=" + fast + " required slow > fast*" + nx); System.exit(1); } System.out.println("PASS " + label + ": slow=" + slow + " fast=" + fast + " ratio=" + String.format("%.1f", (double) slow / fast) + "x"); } public static void main(String[] args) { int passed = 0; int total = 0; // Test 1: openssl-0001 small — 20 client, 20 server ciphers, no overlap { int n = 20; int[] client = new int[n]; int[] server = new int[n]; for (int i = 0; i < n; i++) client[i] = i + 1; for (int i = 0; i < n; i++) server[i] = i + 1001; long s = sharedCiphersSlow(client, server); long f = sharedCiphersFast(client, server); total++; assertGt(s, f, 3, "openssl-0001/small(n=20)"); passed++; } // Test 2: openssl-0001 large — 100 client, 100 server (realistic TLS 1.2 worst case) { int n = 100; int[] client = new int[n]; int[] server = new int[n]; for (int i = 0; i < n; i++) client[i] = i + 1; for (int i = 0; i < n; i++) server[i] = i + 10001; long s = sharedCiphersSlow(client, server); long f = sharedCiphersFast(client, server); total++; assertGt(s, f, 10, "openssl-0001/large(n=100)"); passed++; } // Test 3: openssl-0002 dedup — 30 tokens, 5 unique IDs (heavy duplicate input) { int[] input = new int[30]; for (int i = 0; i < 30; i++) input[i] = (i % 5) + 1; // IDs 1-5, repeated long s = ciphersuiteDeduplicateSlow(input); long f = ciphersuiteDeduplicateFast(input); total++; assertGt(s, f, 2, "openssl-0002/dedup(n=30,unique=5)"); passed++; } // Test 4: openssl-0002 dedup — n=50 unique IDs (worst case: no duplicates, max scan) { int[] input = new int[50]; for (int i = 0; i < 50; i++) input[i] = i + 1; long s = ciphersuiteDeduplicateSlow(input); long f = ciphersuiteDeduplicateFast(input); total++; assertGt(s, f, 5, "openssl-0002/dedup(n=50,no-dup)"); passed++; } System.out.println(passed + "/" + total + " PASS"); } }