diff --git a/app.py b/app.py index 9621125..d17be29 100644 --- a/app.py +++ b/app.py @@ -708,6 +708,11 @@ def index(): # Serve index.html from root directory return send_from_directory('.', 'index.html') +@app.route('/crypto') +def crypto_page(): + """SEO-friendly crypto subsystem page.""" + return render_template('crypto.html') + @app.route('/api/syscalls-realtime') def syscalls_realtime(): """API for real-time system calls""" @@ -3337,6 +3342,231 @@ def collect_algorithm_competition(requested_algorithm="aes"): "selection_policy": "max-priority" } +def collect_kernel_crypto_clients(items): + """Infer major kernel crypto clients from active process/protocol context.""" + client_rules = [ + ("kTLS", ["nginx", "haproxy", "envoy", "caddy", "apache", "httpd", "traefik"], "TLS"), + ("WireGuard", ["wg", "wireguard"], "WireGuard"), + ("IPsec/XFRM", ["charon", "strongswan", "ipsec", "racoon"], "TLS"), + ("dm-crypt", ["cryptsetup", "dmcrypt", "luks"], "CRYPTO API"), + ("fscrypt", ["fscrypt"], "CRYPTO API"), + ("AF_ALG", ["openssl", "python", "curl", "wget"], "CRYPTO API") + ] + results = [] + lowered_items = [] + for item in items: + lowered_items.append({ + "process": str(item.get("process", "")).lower(), + "protocol": str(item.get("protocol", "")), + "source_kind": str(item.get("source_kind", "")) + }) + + for name, tokens, proto_hint in client_rules: + flows = 0 + for item in lowered_items: + proc = item["process"] + proto = item["protocol"] + if any(token in proc for token in tokens): + flows += 1 + elif proto_hint and proto == proto_hint: + flows += 1 + status = "active" if flows > 0 else "idle" + results.append({ + "name": name, + "status": status, + "active_flows": int(flows) + }) + return results + +def collect_sync_async_queue(items): + """Estimate sync/async crypto execution pressure from active flows.""" + active_items = [i for i in items if str(i.get("status", "")).upper() != "LISTEN"] + async_items = [ + i for i in active_items + if str(i.get("source_kind", "")) == "connection" + or str(i.get("protocol", "")).upper() in {"TLS", "WIREGUARD", "SSH"} + ] + sync_items = max(len(active_items) - len(async_items), 0) + sum( + 1 for i in items if str(i.get("source_kind", "")) == "process" + ) + queue_depth = max(len(async_items) - 1, 0) + queue_latency_ms = round(0.35 + min(5.5, queue_depth * 0.42 + len(active_items) * 0.08), 2) + return { + "sync_ops_est": int(sync_items), + "async_ops_est": int(len(async_items)), + "queue_depth_est": int(queue_depth), + "queue_latency_ms_est": queue_latency_ms, + "mode": "heuristic" + } + +def collect_hw_offload_status(entries, algorithm_competitions): + """Estimate hardware acceleration availability from /proc/crypto drivers.""" + names = [] + for entry in entries: + n = str(entry.get("name", "")).lower() + d = str(entry.get("driver", "")).lower() + if n: + names.append(n) + if d: + names.append(d) + + def has_token(tokens): + return any(any(token in item for token in tokens) for item in names) + + selected_impls = { + key: str(value.get("selected", {}).get("name", "")).lower() + for key, value in (algorithm_competitions or {}).items() + } + selected_joined = " ".join(selected_impls.values()) + + engines = [ + { + "engine": "AES-NI / CPU INSTR", + "available": has_token(["aesni", "vaes"]), + "active": ("aesni" in selected_joined or "vaes" in selected_joined) + }, + { + "engine": "SIMD (AVX/NEON)", + "available": has_token(["avx", "sse", "simd", "neon"]), + "active": any(token in selected_joined for token in ["avx", "simd", "neon", "sse"]) + }, + { + "engine": "ARM CRYPTO EXT", + "available": has_token(["arm64", "ce", "neon"]), + "active": "arm64" in selected_joined + }, + { + "engine": "QAT OFFLOAD", + "available": has_token(["qat"]), + "active": "qat" in selected_joined + }, + { + "engine": "VIRTIO-CRYPTO", + "available": has_token(["virtio"]), + "active": "virtio" in selected_joined + } + ] + result = [] + for item in engines: + if item["active"]: + status = "active" + elif item["available"]: + status = "available" + else: + status = "unavailable" + result.append({ + "engine": item["engine"], + "status": status + }) + return result + +def collect_algorithm_requesters(items, kernel_clients): + """Infer likely requestor objects that trigger algorithm competition.""" + algo_map = {"aes": {}, "sha": {}, "chacha20": {}} + client_boost_rules = { + "aes": {"kTLS", "dm-crypt", "AF_ALG", "IPsec/XFRM"}, + "sha": {"kTLS", "AF_ALG", "IPsec/XFRM"}, + "chacha20": {"WireGuard", "AF_ALG"} + } + + for item in items or []: + process_name = str(item.get("process", "unknown")).lower() or "unknown" + protocol = str(item.get("protocol", "")).upper() + algorithm = str(item.get("algorithm", "")).upper() + status = str(item.get("status", "")).upper() + if status == "LISTEN": + continue + + matched_algorithms = set() + if "AES" in algorithm or protocol == "TLS": + matched_algorithms.add("aes") + if "SHA" in algorithm or protocol == "TLS": + matched_algorithms.add("sha") + if "CHACHA" in algorithm or protocol in {"WIREGUARD", "SSH"}: + matched_algorithms.add("chacha20") + if not matched_algorithms and protocol == "CRYPTO API": + matched_algorithms.update(["aes", "sha"]) + + for algo_key in matched_algorithms: + key = f"process:{process_name}" + bucket = algo_map[algo_key].setdefault(key, { + "name": process_name, + "kind": "process", + "score": 0 + }) + bucket["score"] += 1 + + for client in kernel_clients or []: + name = str(client.get("name", "")).strip() + flows = int(client.get("active_flows", 0) or 0) + if not name or flows <= 0: + continue + for algo_key, allowed_clients in client_boost_rules.items(): + if name not in allowed_clients: + continue + key = f"client:{name}" + bucket = algo_map[algo_key].setdefault(key, { + "name": name, + "kind": "kernel-client", + "score": 0 + }) + # Kernel clients are presented as primary requestor objects. + bucket["score"] += max(2, flows) + + result = {} + for algo_key, raw in algo_map.items(): + ranked = sorted(raw.values(), key=lambda x: x.get("score", 0), reverse=True) + if not ranked: + ranked = [{ + "name": "user/kernel request", + "kind": "generic", + "score": 1 + }] + result[algo_key] = ranked[:4] + return result + +def build_crypto_decision_pipelines(algorithm_competitions, kernel_clients, hw_offload, algorithm_requesters): + """Build visual decision pipeline metadata for each algorithm family.""" + hw_active = [h.get("engine") for h in (hw_offload or []) if h.get("status") == "active"] + hw_available = [h.get("engine") for h in (hw_offload or []) if h.get("status") == "available"] + capability_hint = ", ".join(hw_active[:2] or hw_available[:2]) if (hw_active or hw_available) else "generic-cpu-only" + + tfm_lookup_map = { + "AES": "crypto_alloc_skcipher(aes)", + "SHA": "crypto_alloc_shash(sha*)", + "CHACHA20": "crypto_alloc_skcipher(chacha20)" + } + + pipelines = {} + for key, comp in (algorithm_competitions or {}).items(): + request = str(comp.get("request", key)).upper() + impls = comp.get("implementations", []) or [] + shortlist = [str(x.get("name", "unknown")) for x in impls[:3]] + requesters = list((algorithm_requesters or {}).get(key, [])) + top_requester = requesters[0] if requesters else {"name": "user/kernel request", "kind": "generic"} + request_origin = f"{top_requester.get('kind', 'generic')}: {top_requester.get('name', 'unknown')}" + selected_driver = str((comp.get("selected") or {}).get("name", "unknown")) + fallback_driver = next((name for name in shortlist if "generic" in name.lower()), shortlist[-1] if shortlist else "none") + selected_is_generic = "generic" in selected_driver.lower() + selected_source = str((comp.get("selected") or {}).get("source", "kernel")).lower() + fallback_active = selected_is_generic and len(shortlist) > 1 + + pipelines[key] = { + "request": request, + "request_origin": request_origin, + "requesters": requesters, + "tfm_lookup": tfm_lookup_map.get(request, f"crypto_lookup({request.lower()})"), + "impl_shortlist": shortlist, + "priority_check": "max priority wins", + "capability_check": capability_hint, + "selected_driver": selected_driver, + "fallback_driver": fallback_driver, + "fallback_active": bool(fallback_active), + "fallback_reason": "higher-priority impl unavailable or unsupported" if fallback_active else "not-triggered", + "source": selected_source + } + return pipelines + def collect_crypto_realtime(): """ Build a near-realtime list of processes likely interacting with kernel crypto. @@ -3498,6 +3728,21 @@ def collect_crypto_realtime(): "sha": collect_algorithm_competition("sha"), "chacha20": collect_algorithm_competition("chacha20") } + proc_crypto_entries = parse_proc_crypto_entries() + kernel_clients = collect_kernel_crypto_clients(items) + hw_offload = collect_hw_offload_status(proc_crypto_entries, algorithm_competitions) + crypto_stage1 = { + "kernel_clients": kernel_clients, + "sync_async": collect_sync_async_queue(items), + "hw_offload": hw_offload + } + algorithm_requesters = collect_algorithm_requesters(items, kernel_clients) + crypto_decision_pipelines = build_crypto_decision_pipelines( + algorithm_competitions=algorithm_competitions, + kernel_clients=kernel_clients, + hw_offload=hw_offload, + algorithm_requesters=algorithm_requesters + ) return { "items": items[:24], @@ -3510,6 +3755,10 @@ def collect_crypto_realtime(): "tls_terminators": sorted(list(tls_listener_names))[:8], "algorithm_competition": algorithm_competitions["aes"], "algorithm_competitions": algorithm_competitions, + "algorithm_requesters": algorithm_requesters, + "crypto_stage1": crypto_stage1, + "crypto_decision_pipeline": crypto_decision_pipelines.get("aes", {}), + "crypto_decision_pipelines": crypto_decision_pipelines, "source": "live-heuristic-v2", "timestamp": datetime.utcnow().isoformat() + "Z" } diff --git a/index.html b/index.html index fb3efdb..ef2843c 100755 --- a/index.html +++ b/index.html @@ -96,11 +96,11 @@