java-topology/defects/openssl/unit/OpenSslSrtpProfileTest.java

190 lines
7.8 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;
/**
* openssl-0003 unit test
*
* Models the O(C×S) SRTP profile matching in tls_parse_ctos_use_srtp()
* and the O(C+S) fixed version using a hash set.
*
* Compile: javac -d . OpenSslSrtpProfileTest.java
* Run: java unit.OpenSslSrtpProfileTest
*/
public class OpenSslSrtpProfileTest {
// ------------------------------------------------------------------ //
// DEFECTIVE: O(C x S) — linear scan for every client profile id //
// ------------------------------------------------------------------ //
static int defectiveMatch(int[] serverProfiles, int[] clientProfiles) {
int srtp_pref = serverProfiles.length;
int bestIdx = -1;
for (int clientId : clientProfiles) {
// inner: linear scan over server list up to srtp_pref
for (int i = 0; i < srtp_pref; i++) {
if (serverProfiles[i] == clientId) {
bestIdx = i;
srtp_pref = i; // shrink next scan bound (still O(C*S) worst)
break;
}
}
}
return bestIdx;
}
// ------------------------------------------------------------------ //
// FIXED: O(C + S) — hash set for O(1) membership, then rank lookup //
// ------------------------------------------------------------------ //
static int fixedMatch(int[] serverProfiles, int[] clientProfiles) {
final int HS = 32;
int[] set = new int[HS]; // 0 = empty, otherwise server profile id + 1
int[] setIdx = new int[HS]; // server index for each slot
// Build hash set of server ids — O(S)
for (int i = 0; i < serverProfiles.length; i++) {
int sid = serverProfiles[i];
int slot = (sid * 0x9E3779B9) >>> (32 - 5); // Knuth mult hash mod 32
slot &= (HS - 1);
while (set[slot] != 0 && set[slot] != sid + 1)
slot = (slot + 1) & (HS - 1);
set[slot] = sid + 1; // +1 so 0 stays as "empty"
setIdx[slot] = i;
}
int bestServerIdx = serverProfiles.length; // sentinel: no match yet
int bestClientId = -1;
// Iterate client list once — O(C) with O(1) lookup
for (int clientId : clientProfiles) {
int slot = (clientId * 0x9E3779B9) >>> (32 - 5);
slot &= (HS - 1);
while (set[slot] != 0 && set[slot] != clientId + 1)
slot = (slot + 1) & (HS - 1);
if (set[slot] == clientId + 1) {
int serverIdx = setIdx[slot];
if (serverIdx < bestServerIdx) {
bestServerIdx = serverIdx;
bestClientId = clientId;
if (bestServerIdx == 0) break; // can't do better
}
}
}
return bestServerIdx < serverProfiles.length ? bestServerIdx : -1;
}
// ------------------------------------------------------------------ //
// Overhead counter to verify algorithmic complexity //
// ------------------------------------------------------------------ //
static long[] ops = new long[2];
static int defectiveMatchCounting(int[] server, int[] client) {
int srtp_pref = server.length;
int bestIdx = -1;
for (int cid : client) {
for (int i = 0; i < srtp_pref; i++) {
ops[0]++;
if (server[i] == cid) {
bestIdx = i;
srtp_pref = i;
break;
}
}
}
return bestIdx;
}
static int fixedMatchCounting(int[] server, int[] client) {
final int HS = 32;
int[] set = new int[HS];
int[] setIdx = new int[HS];
for (int i = 0; i < server.length; i++) {
ops[1]++;
int sid = server[i];
int slot = (sid * 0x9E3779B9) >>> (32 - 5); slot &= (HS - 1);
while (set[slot] != 0 && set[slot] != sid + 1) { slot = (slot + 1) & (HS - 1); ops[1]++; }
set[slot] = sid + 1; setIdx[slot] = i;
}
int best = server.length;
for (int cid : client) {
ops[1]++;
int slot = (cid * 0x9E3779B9) >>> (32 - 5); slot &= (HS - 1);
while (set[slot] != 0 && set[slot] != cid + 1) { slot = (slot + 1) & (HS - 1); ops[1]++; }
if (set[slot] == cid + 1) { int idx = setIdx[slot]; if (idx < best) { best = idx; if (best == 0) break; } }
}
return best < server.length ? best : -1;
}
// ------------------------------------------------------------------ //
// Tests //
// ------------------------------------------------------------------ //
static int pass = 0, fail = 0;
static void check(String name, boolean cond) {
if (cond) {
System.out.println(" PASS " + name);
pass++;
} else {
System.out.println(" FAIL " + name);
fail++;
}
}
public static void main(String[] args) {
System.out.println("=== openssl-0003: SRTP profile O(C×S) defect ===\n");
// Standard IANA SRTP profile IDs
int[] server = {0x0001, 0x0007, 0x0005}; // SRTP_AES128_CM_HMAC_SHA1_80, _32, NULL_HMAC
int[] clientMatch = {0x0002, 0x0007, 0x0001}; // 0x0007 matches at server index 1
int[] clientNoMatch = {0x0002, 0x0003, 0x0004};
int[] clientPrefer1 = {0x0005, 0x0001}; // prefer index 2, but server has index 0 too
// clientMatch = {0x0002, 0x0007, 0x0001}: 0x0007 at server[1], 0x0001 at server[0].
// Algorithm finds the highest-priority (smallest index) server match → index 0.
check("defective: finds best server match (idx=0)",
defectiveMatch(server, clientMatch) == 0);
// Test 2: fixed agrees
check("fixed: finds best server match (idx=0)",
fixedMatch(server, clientMatch) == 0);
// Test 3: no match
check("defective: no match returns -1",
defectiveMatch(server, clientNoMatch) == -1);
check("fixed: no match returns -1",
fixedMatch(server, clientNoMatch) == -1);
// Test 4: prefer higher-priority server entry
// clientPrefer1 = [0x0005(server idx 2), 0x0001(server idx 0)]
// server-preference: should pick server idx 0 (0x0001)
check("fixed: picks higher server-priority profile",
fixedMatch(server, clientPrefer1) == 0);
// Test 5: complexity — generate adversarial input
int S = 6;
int C = 200;
int[] bigServer = new int[S];
for (int i = 0; i < S; i++) bigServer[i] = 0x1000 + i;
// client sends non-matching IDs followed by a hit at the end
int[] bigClient = new int[C];
for (int i = 0; i < C - 1; i++) bigClient[i] = 0x2000 + i; // no match
bigClient[C - 1] = bigServer[S - 1]; // match at worst position
ops[0] = 0; ops[1] = 0;
int rd = defectiveMatchCounting(bigServer, bigClient);
int rf = fixedMatchCounting(bigServer, bigClient);
System.out.println("\n Complexity comparison (S=" + S + ", C=" + C + "):");
System.out.println(" Defective ops: " + ops[0] + " (expected ~" + ((long)(C-1)*S + 1) + ")");
System.out.println(" Fixed ops: " + ops[1] + " (expected ~" + (S + C) + ")");
System.out.printf(" Speedup: %.1fx%n", (double) ops[0] / ops[1]);
check("defective found same result as fixed",
rd == rf);
check("defective ops > fixed ops (quadratic vs linear)",
ops[0] > ops[1]);
check("fixed ops is O(C+S) not O(C*S)",
ops[1] < ops[0] / 3);
System.out.println("\n--- " + (pass + fail) + " tests: " + pass + " passed, " + fail + " failed ---");
if (fail > 0) System.exit(1);
}
}