wave17: openssh-0001/2, strongswan-0001, gradle-0002, groovy-0001/2, make-0001, clojure-CLEAN + whitepaper 549/240

This commit is contained in:
russell@unturf.com 2026-03-27 19:55:52 -04:00
parent 0f275c44af
commit 4221966e66
22 changed files with 1776 additions and 4 deletions

View file

@ -0,0 +1,265 @@
package unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* strongswan-0001: CWE-407 O(P²×T×A²) nested linear scans in proposal_select / select_algo
*
* Models the strongSwan proposal negotiation:
* SLOW: nested ArrayList scans for per-transform algorithm matching (O(A₁×A₂) per type)
* inside an outer O(P_c × P_s) proposal loop.
* FAST: HashMap-indexed algorithm set per transform type → O(A₁+A₂)
*
* No JUnit — compile and run standalone.
*/
public class StrongSwanProposalSelectTest {
// ------------------------------------------------------------------
// Data model: a "proposal" has transform types, each with a list of algs
// Each alg is an int (algorithm id | key_size packed as long)
// ------------------------------------------------------------------
static class Proposal {
// transform_type -> list of (alg_id << 16 | key_size) longs
List<List<Long>> transforms; // indexed by transform type 0..T-1
Proposal(int numTypes) {
transforms = new ArrayList<>();
for (int i = 0; i < numTypes; i++) transforms.add(new ArrayList<>());
}
void addAlg(int type, long algKey) {
transforms.get(type).add(algKey);
}
}
// ------------------------------------------------------------------
// SLOW: select_algo O(A₁ × A₂) — mirrors the C nested enumerator loop
// Returns op count.
// ------------------------------------------------------------------
static long slowSelectAlgo(List<Long> localAlgs, List<Long> remoteAlgs) {
long ops = 0;
for (Long alg1 : localAlgs) {
for (Long alg2 : remoteAlgs) {
ops++;
if (alg1.equals(alg2)) {
return ops; // found — mirrors early break
}
}
}
return ops; // no match
}
// SLOW: select_algos — iterate all T transform types
static long slowSelectAlgos(Proposal local, Proposal remote) {
long ops = 0;
int T = local.transforms.size();
for (int type = 0; type < T; type++) {
ops += slowSelectAlgo(local.transforms.get(type), remote.transforms.get(type));
}
return ops;
}
// SLOW: proposal_select — O(P_c × P_s) outer loop
static long slowProposalSelect(List<Proposal> configured, List<Proposal> supplied) {
long ops = 0;
for (Proposal local : configured) {
for (Proposal remote : supplied) {
ops += slowSelectAlgos(local, remote);
}
}
return ops;
}
// ------------------------------------------------------------------
// FAST: build HashMap per transform type from remote proposal, then O(1) lookup
// ------------------------------------------------------------------
static long fastSelectAlgo(List<Long> localAlgs, List<Long> remoteAlgs) {
HashMap<Long, Boolean> remoteSet = new HashMap<>();
long ops = 0;
for (Long alg2 : remoteAlgs) {
ops++;
remoteSet.put(alg2, true);
}
for (Long alg1 : localAlgs) {
ops++;
if (remoteSet.containsKey(alg1)) {
return ops; // found
}
}
return ops;
}
static long fastSelectAlgos(Proposal local, Proposal remote) {
long ops = 0;
int T = local.transforms.size();
for (int type = 0; type < T; type++) {
ops += fastSelectAlgo(local.transforms.get(type), remote.transforms.get(type));
}
return ops;
}
static long fastProposalSelect(List<Proposal> configured, List<Proposal> supplied) {
long ops = 0;
for (Proposal local : configured) {
for (Proposal remote : supplied) {
ops += fastSelectAlgos(local, remote);
}
}
return ops;
}
// ------------------------------------------------------------------
// Helpers: build proposal lists
// ------------------------------------------------------------------
/**
* Build a list of P proposals, each with T transform types,
* each type having A algorithms. Algorithms are unique per proposal
* and offset so that no match occurs until the last comparison
* (worst-case for the slow path).
*/
static List<Proposal> makeProposals(int P, int T, int A, int baseOffset) {
List<Proposal> proposals = new ArrayList<>();
for (int p = 0; p < P; p++) {
Proposal prop = new Proposal(T);
for (int t = 0; t < T; t++) {
for (int a = 0; a < A; a++) {
// unique algorithm id per (proposal, type, alg)
long algKey = (long)(baseOffset + p * T * A + t * A + a) << 16;
prop.addAlg(t, algKey);
}
}
proposals.add(prop);
}
return proposals;
}
/**
* Build supplied proposals that match on the LAST algorithm of LAST
* proposal to force worst-case O(P²×T×A²).
*/
static List<Proposal> makeMatchingSupplied(List<Proposal> configured, int T, int A) {
List<Proposal> supplied = new ArrayList<>();
// clone configured proposals, put the matching alg last in each type
for (Proposal cfg : configured) {
Proposal sup = new Proposal(T);
for (int t = 0; t < T; t++) {
List<Long> cfgAlgs = cfg.transforms.get(t);
// add non-matching alg first, then the matching alg last
sup.addAlg(t, Long.MAX_VALUE - t); // no-match filler
sup.addAlg(t, cfgAlgs.get(cfgAlgs.size() - 1)); // last cfg alg
}
supplied.add(sup);
}
return supplied;
}
public static void main(String[] args) {
int tests = 0, passed = 0;
// --- correctness: tiny case ---
{
tests++;
int P = 2, T = 2, A = 3;
List<Proposal> configured = makeProposals(P, T, A, 0);
List<Proposal> supplied = makeMatchingSupplied(configured, T, A);
long slowOps = slowProposalSelect(configured, supplied);
long fastOps = fastProposalSelect(configured, supplied);
// both should find a match (ops > 0); fast should use fewer ops
if (slowOps > 0 && fastOps > 0 && fastOps <= slowOps) {
passed++;
System.out.println("PASS correctness P=" + P + " T=" + T + " A=" + A
+ " slow_ops=" + slowOps + " fast_ops=" + fastOps);
} else {
System.out.println("FAIL correctness P=" + P + " T=" + T + " A=" + A
+ " slow_ops=" + slowOps + " fast_ops=" + fastOps);
}
}
// --- no-match worst case ---
{
tests++;
int P = 5, T = 3, A = 5;
List<Proposal> configured = makeProposals(P, T, A, 0);
// supplied has completely different algs — no match at all
List<Proposal> supplied = makeProposals(P, T, A, 10000);
long slowOps = slowProposalSelect(configured, supplied);
long fastOps = fastProposalSelect(configured, supplied);
double ratio = (double) slowOps / fastOps;
boolean ok = ratio >= 2.0;
if (ok) {
passed++;
System.out.printf("PASS no-match P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
P, T, A, slowOps, fastOps, ratio);
} else {
System.out.printf("FAIL no-match P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx (need >=2x)%n",
P, T, A, slowOps, fastOps, ratio);
}
}
// --- speedup benchmarks ---
int[][] benchParams = {
// {P, T, A} — worst case: no match until last comparison
{10, 5, 8},
{20, 5, 8},
{30, 7, 10},
{50, 7, 10},
};
for (int[] param : benchParams) {
tests++;
int P = param[0], T = param[1], A = param[2];
List<Proposal> configured = makeProposals(P, T, A, 0);
// no-match supplied: completely different algorithm ids
List<Proposal> supplied = makeProposals(P, T, A, 100000);
long slowOps = slowProposalSelect(configured, supplied);
long fastOps = fastProposalSelect(configured, supplied);
double ratio = (double) slowOps / fastOps;
// At A=8 we expect ~A ratio for select_algo alone; with P² multiplied, ratio should be >=3x
boolean ok = ratio >= 3.0;
if (ok) {
passed++;
System.out.printf("PASS speedup P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
P, T, A, slowOps, fastOps, ratio);
} else {
System.out.printf("FAIL speedup P=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx (need >=3x)%n",
P, T, A, slowOps, fastOps, ratio);
}
}
// --- large-scale attack simulation ---
// Attacker sends 50 non-matching proposals, server has 30 proposals
// This models a DoS amplification scenario
{
tests++;
int Pc = 30, Ps = 50, T = 5, A = 8;
List<Proposal> configured = makeProposals(Pc, T, A, 0);
List<Proposal> supplied = makeProposals(Ps, T, A, 100000);
long slowOps = slowProposalSelect(configured, supplied);
long fastOps = fastProposalSelect(configured, supplied);
double ratio = (double) slowOps / fastOps;
// With A=8: slow does A² ops per transform (64), fast does 2A (16) → ratio ~= A/2 = 4x
// The key point is the A² exponent is eliminated; require ratio >= 3x
boolean ok = ratio >= 3.0;
if (ok) {
passed++;
System.out.printf("PASS dos-scenario Pc=%d Ps=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx%n",
Pc, Ps, T, A, slowOps, fastOps, ratio);
} else {
System.out.printf("FAIL dos-scenario Pc=%d Ps=%d T=%d A=%d slow_ops=%d fast_ops=%d ratio=%.1fx (need >=3x)%n",
Pc, Ps, T, A, slowOps, fastOps, ratio);
}
}
System.out.println("\n" + passed + "/" + tests + " PASS");
if (passed != tests) System.exit(1);
}
}