182 lines
7.4 KiB
Java
182 lines
7.4 KiB
Java
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);
|
||
}
|
||
}
|