java-topology/defects/mbedtls/unit/MbedTlsCipherSelectTest.java

182 lines
7.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.HashMap;
/**
* mbedtls-0002 unit test
*
* Models the O(S×C×D) cipher suite selection in ssl_tls12_server.c
* and the O(S+C) fixed version using a hash set of client-offered IDs.
*
* Compile: javac -d . MbedTlsCipherSelectTest.java
* Run: java unit.MbedTlsCipherSelectTest
*/
public class MbedTlsCipherSelectTest {
// Simulated ciphersuite_definitions table (D entries)
static final int[] CIPHER_DEFINITIONS;
static {
// Representative TLS 1.2 cipher suite IDs (a subset of the ~70 in mbedTLS)
CIPHER_DEFINITIONS = new int[]{
0xC02B, // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
0xC02C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
0xC02F, // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
0xC030, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
0xCCA9, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
0xCCA8, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
0xC009, // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
0xC013, // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
0x002F, // TLS_RSA_WITH_AES_128_CBC_SHA
0x0035, // TLS_RSA_WITH_AES_256_CBC_SHA
0x003C, // TLS_RSA_WITH_AES_128_CBC_SHA256
0x003D, // TLS_RSA_WITH_AES_256_CBC_SHA256
0x009C, // TLS_RSA_WITH_AES_128_GCM_SHA256
0x009D, // TLS_RSA_WITH_AES_256_GCM_SHA384
0xC023, // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
0xC024, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
};
}
// O(D) linear lookup — mirrors mbedtls_ssl_ciphersuite_from_id
static boolean cipherDefinitionExists(int id) {
for (int def : CIPHER_DEFINITIONS) {
if (def == id) return true;
}
return false;
}
// ------------------------------------------------------------------ //
// DEFECTIVE: O(S × C × D) //
// server-preference mode (most common): for each server suite, //
// scan all client suites; on match, call ciphersuite_match (O(D)) //
// ------------------------------------------------------------------ //
static int defectiveSelect(int[] serverSuites, int[] clientSuites) {
for (int sid : serverSuites) {
for (int cid : clientSuites) {
if (cid == sid) {
// ssl_ciphersuite_match calls mbedtls_ssl_ciphersuite_from_id (O(D))
if (cipherDefinitionExists(sid))
return sid;
}
}
}
return -1;
}
// ------------------------------------------------------------------ //
// FIXED: O(S + C) — hash set of client IDs, O(1) membership test //
// ------------------------------------------------------------------ //
static int fixedSelect(int[] serverSuites, int[] clientSuites) {
// Build hash set of client IDs — O(C)
HashMap<Integer, Boolean> clientSet = new HashMap<>();
for (int cid : clientSuites) clientSet.put(cid, Boolean.TRUE);
// Iterate server list — O(S), each lookup O(1)
for (int sid : serverSuites) {
if (clientSet.containsKey(sid)) {
// ciphersuite_match still needed, but only on actual matches
if (cipherDefinitionExists(sid))
return sid;
}
}
return -1;
}
// ------------------------------------------------------------------ //
// Counting versions //
// ------------------------------------------------------------------ //
static long[] ops = new long[2]; // [defective, fixed]
static int defectiveCounting(int[] server, int[] client) {
for (int sid : server) {
for (int cid : client) {
ops[0]++; // inner comparison
if (cid == sid) {
for (int def : CIPHER_DEFINITIONS) {
ops[0]++; // O(D) lookup
if (def == sid) return sid;
}
}
}
}
return -1;
}
static int fixedCounting(int[] server, int[] client) {
HashMap<Integer, Boolean> cs = new HashMap<>();
for (int cid : client) { ops[1]++; cs.put(cid, Boolean.TRUE); }
for (int sid : server) {
ops[1]++;
if (cs.containsKey(sid)) {
for (int def : CIPHER_DEFINITIONS) {
ops[1]++;
if (def == sid) return sid;
}
}
}
return -1;
}
static int pass = 0, fail = 0;
static void check(String label, boolean cond) {
if (cond) { System.out.println(" PASS " + label); pass++; }
else { System.out.println(" FAIL " + label); fail++; }
}
public static void main(String[] args) {
System.out.println("=== mbedtls-0002: TLS 1.2 cipher selection O(S×C×D) defect ===\n");
// Server prefers ECDHE-ECDSA-AES128-GCM, then ECDHE-RSA-AES128-GCM
int[] serverList = {0xC02B, 0xC02F, 0x002F};
// Test 1: first server suite matches
int[] client1 = {0xC02F, 0xC02B, 0x0000};
check("defective: picks server-preferred 0xC02B",
defectiveSelect(serverList, client1) == 0xC02B);
check("fixed: picks server-preferred 0xC02B",
fixedSelect(serverList, client1) == 0xC02B);
// Test 2: only second server suite matches
int[] client2 = {0xC02F, 0x0035};
check("defective: picks 0xC02F (second server entry)",
defectiveSelect(serverList, client2) == 0xC02F);
check("fixed: picks 0xC02F (second server entry)",
fixedSelect(serverList, client2) == 0xC02F);
// Test 3: no common suite
int[] client3 = {0x0004, 0x0005, 0x000A};
check("defective: no match → -1", defectiveSelect(serverList, client3) == -1);
check("fixed: no match → -1", fixedSelect(serverList, client3) == -1);
// Test 4: complexity — adversarial large client list with no match
// (no-match is worst case: defective must scan all S*C combinations)
int S = 20;
int C = 300;
int D = CIPHER_DEFINITIONS.length;
int[] bigServer = new int[S];
for (int i = 0; i < S; i++)
bigServer[i] = 0x1000 + i; // server suites NOT in CIPHER_DEFINITIONS
// client sends C unknown IDs → no match, full O(S*C) scan
int[] bigClient = new int[C];
for (int i = 0; i < C; i++) bigClient[i] = 0x9000 + i;
ops[0] = 0; ops[1] = 0;
int rd = defectiveCounting(bigServer, bigClient);
int rf = fixedCounting(bigServer, bigClient);
System.out.println("\n Complexity comparison (S=" + S + ", C=" + C + ", D=" + D + ", no-match):");
System.out.println(" Defective ops: " + ops[0] + " (expected ~" + ((long)S * C) + ")");
System.out.println(" Fixed ops: " + ops[1] + " (expected ~" + (S + C) + ")");
System.out.printf(" Speedup: %.1fx%n", (double) ops[0] / ops[1]);
check("defective and fixed agree on result (both -1)", rd == rf && rd == -1);
check("defective ops > 5x fixed ops (quadratic vs linear)",
ops[0] > 5 * ops[1]);
System.out.println("\n--- " + (pass + fail) + " tests: " + pass + " passed, " + fail + " failed ---");
if (fail > 0) System.exit(1);
}
}