diff --git a/static/js/crypto-belt.js b/static/js/crypto-belt.js
index 1f1d870..ab9ab43 100644
--- a/static/js/crypto-belt.js
+++ b/static/js/crypto-belt.js
@@ -1,7 +1,7 @@
// Crypto subsystem realtime interaction visualization
-// Version: 10
+// Version: 15 — Architecture + consumer/primitive morph
-debugLog('🔐 crypto-belt.js v10: Script loading...');
+debugLog('🔐 crypto-belt.js v15: Script loading...');
class CryptoSubsystemVisualization {
constructor() {
@@ -24,7 +24,21 @@ class CryptoSubsystemVisualization {
this.selectedClientFilters = new Set();
this.selectedRequesterFilter = null;
this.selectedImplementationClassFilter = null;
+ // Default: AES LAB first; Architecture and Live Flow are secondary.
this.activeCryptoView = 'LINEAR_ANALYSIS';
+ this.archFocus = null; // { layer, id, label, hint }
+ this.archMorphTarget = null; // { id, label, layer }
+ this.archMorphNode = null;
+ this.schemeSource = null; // { id, label } — opened from Architecture click
+ this.schemeKind = 'aes-gcm'; // aes-gcm | wg-chacha
+ this.schemePhase = 0;
+ this.schemePlaying = false;
+ this._schemePlayTimer = null;
+ this.schemeRendered = false;
+ this.schemeNr = 10; // AES-128=10, AES-256=14
+ this.schemeInspectByte = 0;
+ this._archGhostEl = null;
+ this._archGhostTimer = null;
this.titleNode = null;
this.subtitleNode = null;
this.viewToggleNode = null;
@@ -112,7 +126,7 @@ class CryptoSubsystemVisualization {
'z-index: 1001',
'text-shadow: 0 0 8px rgba(180, 210, 255, 0.25)'
].join(';');
- title.textContent = 'KERNEL CRYPTO LIVE INTERACTIONS (in development)';
+ title.textContent = 'KERNEL CRYPTO ARCHITECTURE';
this.container.appendChild(title);
this.titleNode = title;
@@ -127,7 +141,7 @@ class CryptoSubsystemVisualization {
'font-size: 11px',
'z-index: 1001'
].join(';');
- subtitle.textContent = 'process -> protocol -> crypto subsystem -> algorithm';
+ subtitle.textContent = 'consumers → Kernel Crypto API → primitives / drivers / acceleration';
this.container.appendChild(subtitle);
this.subtitleNode = subtitle;
@@ -198,6 +212,23 @@ class CryptoSubsystemVisualization {
].join(';');
this.container.appendChild(hoverCard);
this.hoverCard = hoverCard;
+
+ const morph = document.createElement('div');
+ morph.className = 'crypto-arch-morph-host';
+ morph.style.cssText = [
+ 'position: absolute',
+ 'left: 50%',
+ 'top: 50%',
+ 'transform: translate(-50%, -50%)',
+ 'width: min(560px, 86vw)',
+ 'max-height: min(72vh, 640px)',
+ 'overflow: auto',
+ 'z-index: 1005',
+ 'display: none',
+ 'pointer-events: auto'
+ ].join(';');
+ this.container.appendChild(morph);
+ this.archMorphNode = morph;
}
setTerminatorBadge(statusText) {
@@ -242,7 +273,9 @@ class CryptoSubsystemVisualization {
updateCryptoViewToggle() {
if (!this.viewToggleNode) return;
const views = [
- ['LINEAR_ANALYSIS', 'AES INTERNALS'],
+ ['LINEAR_ANALYSIS', 'AES LAB'],
+ ['ARCHITECTURE', 'ARCHITECTURE'],
+ ['HANDSHAKE', 'HANDSHAKE'],
['LIVE_FLOW', 'LIVE FLOW']
];
this.viewToggleNode.innerHTML = '';
@@ -250,6 +283,12 @@ class CryptoSubsystemVisualization {
const btn = document.createElement('button');
const isActive = this.activeCryptoView === id;
btn.textContent = label;
+ btn.title = ({
+ ARCHITECTURE: 'Consumers → Crypto API → implementations · click kTLS/AES for scheme',
+ HANDSHAKE: 'TLS 1.3: ClientHello → X25519 → HKDF → AES-GCM',
+ LIVE_FLOW: 'Live interaction lanes',
+ LINEAR_ANALYSIS: 'AES linear analysis demo'
+ })[id] || label;
btn.style.cssText = [
'padding: 5px 12px',
`background: ${isActive ? 'rgba(35, 58, 88, 0.94)' : 'rgba(8, 12, 18, 0.86)'}`,
@@ -263,8 +302,15 @@ class CryptoSubsystemVisualization {
'box-shadow: none'
].join(';');
btn.onclick = () => {
+ if (id !== 'ARCHITECTURE') this.closeArchMorph();
+ if (id !== 'SCHEME') {
+ this.stopSchemePlay();
+ this.schemeSource = null;
+ this.schemeRendered = false;
+ }
this.activeCryptoView = id;
this.updateCryptoViewToggle();
+ this.syncOverlayForCurrentView();
this.renderFlowMap(this.lastPayload || this.normalizeTelemetry(this.getFallbackTelemetry()));
};
this.viewToggleNode.appendChild(btn);
@@ -273,20 +319,41 @@ class CryptoSubsystemVisualization {
syncOverlayForCurrentView() {
const isLinear = this.activeCryptoView === 'LINEAR_ANALYSIS';
+ const isArch = this.activeCryptoView === 'ARCHITECTURE';
+ const isScheme = this.activeCryptoView === 'SCHEME';
+ const isHandshake = this.activeCryptoView === 'HANDSHAKE';
if (this.titleNode) {
this.titleNode.style.display = isLinear ? 'none' : 'block';
+ if (isArch) this.titleNode.textContent = 'KERNEL CRYPTO ARCHITECTURE';
+ else if (isScheme) {
+ const src = this.schemeSource?.label || this.schemeSource?.id || 'AES';
+ const tail = this.schemeKind === 'wg-chacha' ? 'CHACHA20-POLY1305' : 'AES-GCM';
+ this.titleNode.textContent = `SCHEME · ${String(src).toUpperCase()} → ${tail}`;
+ } else if (isHandshake) this.titleNode.textContent = 'TLS 1.3 · HANDSHAKE → KEYS';
+ else if (!isLinear) this.titleNode.textContent = 'KERNEL CRYPTO LIVE INTERACTIONS';
}
if (this.subtitleNode) {
this.subtitleNode.style.display = isLinear ? 'none' : 'block';
+ if (isArch) {
+ this.subtitleNode.textContent = 'click kTLS/AES or WireGuard/ChaCha for textbook SCHEME · other nodes → morph';
+ } else if (isScheme) {
+ const kind = this.schemeKind === 'wg-chacha' ? 'WireGuard · ChaCha20-Poly1305' : 'kTLS · AES-GCM';
+ this.subtitleNode.textContent = `opened from Architecture · ${kind} · CODE refs → Elixir`;
+ } else if (isHandshake) {
+ this.subtitleNode.textContent = 'ClientHello → X25519 → HKDF → AES-GCM keys — ECC bridge to symmetric';
+ } else if (!isLinear) {
+ this.subtitleNode.textContent = 'process -> protocol -> crypto subsystem -> algorithm';
+ }
}
if (this.terminatorNode) {
- this.terminatorNode.style.display = isLinear ? 'none' : 'block';
+ // Architecture map is structural — hide TLS terminator chrome.
+ this.terminatorNode.style.display = (isLinear || isArch) ? 'none' : 'block';
}
if (this.viewToggleNode) {
- this.viewToggleNode.style.top = isLinear ? '18px' : '112px';
- this.viewToggleNode.style.left = isLinear ? 'auto' : '50%';
- this.viewToggleNode.style.right = isLinear ? '170px' : 'auto';
- this.viewToggleNode.style.transform = isLinear ? 'none' : 'translateX(-50%)';
+ this.viewToggleNode.style.top = '18px';
+ this.viewToggleNode.style.left = 'auto';
+ this.viewToggleNode.style.right = '170px';
+ this.viewToggleNode.style.transform = 'none';
}
}
@@ -4014,200 +4081,2874 @@ class CryptoSubsystemVisualization {
.text('Vogel spiral = distribution of candidate linear masks; Fibonacci trail = bias decay across rounds');
}
- renderFlowMap(payload) {
- if (!this.svg) return;
- this.lastPayload = payload;
- this.activeAnimationTick += 1;
- const tickId = this.activeAnimationTick;
- this.syncOverlayForCurrentView();
- if (this.activeCryptoView === 'LINEAR_ANALYSIS') {
- this.linearAnalysisRendered = true;
- this.lastLinearAnalysisRenderAt = Date.now();
- } else {
- this.linearAnalysisRendered = false;
- if (this.aesOverlay) this.closeAesOpsOverlay();
+ escapeArchHtml(value) {
+ return String(value ?? '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+ }
+
+ getConsumerMorphScript(id) {
+ const scripts = {
+ wireguard: {
+ title: 'WIREGUARD → CRYPTO TRANSLATION',
+ tagline: 'a tunnel is not “encrypted packets” — it is Noise + AEAD through the crypto API',
+ ghost: 'crypto_aead_encrypt()',
+ accent: 'rgba(150,255,190,0.45)',
+ steps: [
+ { sym: 'wg_encrypt / noise_handshake', title: '1 · CONSUMER', body: 'WireGuard builds a Noise_IK message
peer keys · counters · packet payload' },
+ { sym: 'crypto_alloc_aead / crypto_alloc_kpp', title: '2 · CRYPTO API', body: 'request aead + kpp
name lookup → tfm allocation' },
+ { sym: 'struct crypto_aead *', title: '3 · TFM HANDLE', body: 'tfm holds setkey / encrypt / decrypt ops
one handle, many backends' },
+ { sym: 'chacha20poly1305 + curve25519', title: '4 · PRIMITIVES', body: 'ChaCha20-Poly1305 for data
Curve25519 (X25519) for handshake' },
+ { sym: 'chacha20-simd / generic', title: '5 · IMPLEMENTATION', body: 'priority race picks SIMD/generic path
same API — faster bytes' }
+ ]
+ },
+ ktls: {
+ title: 'kTLS → CRYPTO TRANSLATION',
+ tagline: 'TLS records leave userspace — AEAD runs beside the TCP stack',
+ ghost: 'tls_sw_sendmsg()',
+ accent: 'rgba(103,190,224,0.5)',
+ openHandshake: true,
+ steps: [
+ { sym: 'tls_sw_sendmsg / kTLS', title: '1 · CONSUMER', body: 'socket send path hits kernel TLS
record framing stays in-kernel' },
+ { sym: 'crypto_aead_encrypt', title: '2 · CRYPTO API', body: 'kTLS asks the unified AEAD API
no userspace crypto round-trip' },
+ { sym: 'crypto_alloc_aead(aes-gcm)', title: '3 · TFM HANDLE', body: 'tfm bound to TLS keys / IV / seq
per-connection crypto state' },
+ { sym: 'AES-GCM', title: '4 · PRIMITIVE', body: 'AES + GHASH over the record
confidentiality + integrity together' },
+ { sym: 'aesni / cryptd(__aes-aesni)', title: '5 · IMPLEMENTATION', body: 'AES-NI (+PCLMUL) wins on x86
cryptd may wrap for async' }
+ ]
+ },
+ af_alg: {
+ title: 'AF_ALG → CRYPTO TRANSLATION',
+ tagline: 'userspace speaks sockets — the kernel hears crypto_tfm',
+ ghost: 'af_alg_sendmsg()',
+ accent: 'rgba(230,193,90,0.5)',
+ steps: [
+ { sym: 'socket(AF_ALG) / accept', title: '1 · CONSUMER', body: 'userspace opens an alg socket
bind type+name · setkey · sendmsg' },
+ { sym: 'af_alg → crypto_skcipher/aead', title: '2 · CRYPTO API', body: 'AF_ALG is a thin gateway into crypto API
same alloc/lookup as in-kernel clients' },
+ { sym: 'struct crypto_tfm *', title: '3 · TFM HANDLE', body: 'accepted fd holds a live transform
ops dispatched per request' },
+ { sym: 'AES / SHA / ChaCha…', title: '4 · PRIMITIVE', body: 'name string selects the algorithm family
one socket model · many algos' },
+ { sym: 'aesni / sha*-avx2 / simd', title: '5 · IMPLEMENTATION', body: 'best registered driver for this CPU
transparent to the application' }
+ ]
+ },
+ dm_crypt: {
+ title: 'dm-crypt → CRYPTO TRANSLATION',
+ tagline: 'block I/O becomes skcipher requests on the way to disk',
+ ghost: 'crypt_convert()',
+ accent: 'rgba(232,96,104,0.45)',
+ steps: [
+ { sym: 'dm-crypt map / crypt_convert', title: '1 · CONSUMER', body: 'bio hits the crypto target
sector → IV → cipher request' },
+ { sym: 'crypto_skcipher_encrypt', title: '2 · CRYPTO API', body: 'dm-crypt talks skcipher (often XTS)
same API as AF_ALG / fscrypt' },
+ { sym: 'crypto_alloc_skcipher', title: '3 · TFM HANDLE', body: 'per-device tfm with volume key
setkey once · encrypt many bios' },
+ { sym: 'AES-XTS', title: '4 · PRIMITIVE', body: 'AES in XTS mode for disk blocks
tweakable encryption per sector' },
+ { sym: 'aes-aesni / cryptd', title: '5 · IMPLEMENTATION', body: 'AES-NI preferred · cryptd if async needed
storage latency meets crypto throughput' }
+ ]
+ },
+ fscrypt: {
+ title: 'fscrypt → CRYPTO TRANSLATION',
+ tagline: 'files and directories encrypt through the same skcipher spine',
+ ghost: 'fscrypt_encrypt_pagecache_blocks()',
+ accent: 'rgba(196,176,255,0.5)',
+ steps: [
+ { sym: 'fscrypt / inode policy', title: '1 · CONSUMER', body: 'VFS pagecache write hits fscrypt
per-file key derived from master' },
+ { sym: 'crypto_skcipher_encrypt', title: '2 · CRYPTO API', body: 'contents via skcipher · names via hashes
API shared with dm-crypt' },
+ { sym: 'crypto_alloc_skcipher', title: '3 · TFM HANDLE', body: 'tfm cached with derived key
reuse across pages' },
+ { sym: 'AES / Adiantum', title: '4 · PRIMITIVE', body: 'typically AES modes · sometimes Adiantum
policy chooses the primitive' },
+ { sym: 'aesni / generic', title: '5 · IMPLEMENTATION', body: 'CPU-accelerated when available
filesystem never picks registers itself' }
+ ]
+ },
+ ipsec: {
+ title: 'IPsec/XFRM → CRYPTO TRANSLATION',
+ tagline: 'ESP/AH transforms are just crypto API clients on the packet path',
+ ghost: 'xfrm_output()',
+ accent: 'rgba(103,190,224,0.45)',
+ steps: [
+ { sym: 'xfrm_output / ESP', title: '1 · CONSUMER', body: 'XFRM applies a transform to the skb
policy → state → crypto' },
+ { sym: 'crypto_aead_encrypt', title: '2 · CRYPTO API', body: 'ESP almost always uses AEAD
encrypt + auth in one call' },
+ { sym: 'crypto_alloc_aead', title: '3 · TFM HANDLE', body: 'per-SA tfm with keys from IKE
lifetime tied to xfrm_state' },
+ { sym: 'AES-GCM / SHA', title: '4 · PRIMITIVE', body: 'modern stacks prefer AES-GCM
older: cipher + auth separately' },
+ { sym: 'aesni / offload', title: '5 · IMPLEMENTATION', body: 'AES-NI or NIC IPsec offload
same SA · different engine' }
+ ]
+ },
+ ima: {
+ title: 'IMA/EVM → CRYPTO TRANSLATION',
+ tagline: 'integrity is hashing and signatures — still through crypto API',
+ ghost: 'ima_calc_file_hash()',
+ accent: 'rgba(230,193,90,0.45)',
+ steps: [
+ { sym: 'ima_file_check / evm', title: '1 · CONSUMER', body: 'measure or appraise a file
policy hooks into LSM path' },
+ { sym: 'crypto_shash_digest', title: '2 · CRYPTO API', body: 'hashes via shash · sigs via akcipher
unified digest/verify entry points' },
+ { sym: 'crypto_alloc_shash', title: '3 · TFM HANDLE', body: 'hash tfm for measurement
optional ECDSA/RSA verify tfm' },
+ { sym: 'SHA-2 + ECDSA', title: '4 · PRIMITIVES', body: 'SHA-256/512 measure
ECDSA can appraise' },
+ { sym: 'sha*-avx2 / generic', title: '5 · IMPLEMENTATION', body: 'SIMD hash when present
signature path may stay generic' }
+ ]
+ },
+ random: {
+ title: 'random/CRNG → CRYPTO TRANSLATION',
+ tagline: 'the entropy pool’s output mixer is ChaCha20 in disguise',
+ ghost: 'crng_fast_key_erasure()',
+ accent: 'rgba(150,255,190,0.4)',
+ steps: [
+ { sym: 'get_random_bytes', title: '1 · CONSUMER', body: 'kernel clients ask for random bytes
keys · nonces · IV material' },
+ { sym: 'CRNG core', title: '2 · CRYPTO ENGINE', body: 'ChaCha20-based CRNG mixes state
fast path after initial seed' },
+ { sym: 'chacha_block', title: '3 · PRIMITIVE', body: 'ChaCha20 expands a secret state
not a tfm alloc every call' },
+ { sym: 'SIMD ChaCha', title: '4 · IMPLEMENTATION', body: 'arch SIMD helpers when available
same stream cipher family as WireGuard' },
+ { sym: 'reuse across kernel', title: '5 · MAGIC', body: 'one primitive · many consumers
VPN AEAD and RNG share ChaCha DNA' }
+ ]
+ }
+ };
+ return scripts[id] || {
+ title: 'CONSUMER → CRYPTO TRANSLATION',
+ tagline: 'subsystem request becomes a crypto API transform',
+ ghost: 'crypto_alloc_tfm()',
+ accent: 'rgba(169,212,232,0.45)',
+ steps: [
+ { sym: 'subsystem hook', title: '1 · CONSUMER', body: 'a kernel client needs crypto
encrypt · hash · sign · agree' },
+ { sym: 'crypto_alloc_*', title: '2 · CRYPTO API', body: 'unified entry by type + name
lookup · priority · tfm' },
+ { sym: 'struct crypto_tfm *', title: '3 · TFM', body: 'opaque handle to algorithm ops
setkey · encrypt · digest' },
+ { sym: 'primitive', title: '4 · PRIMITIVE', body: 'AES · ChaCha · SHA · ECC…
math the subsystem asked for' },
+ { sym: 'driver / acceleration', title: '5 · IMPLEMENTATION', body: 'generic · simd · aesni · offload
fastest registered winner' }
+ ]
+ };
+ }
+
+ getPrimitiveMorphScript(id) {
+ const scripts = {
+ aes: {
+ title: 'AES → KERNEL DRILL',
+ tagline: 'one block cipher · GCM for TLS · XTS for disks · many consumers',
+ ghost: 'aesni_enc()',
+ accent: 'rgba(230,193,90,0.55)',
+ openAesLab: true,
+ steps: [
+ { sym: 'who asks for AES?', title: '1 · CONSUMERS', body: 'kTLS · IPsec · dm-crypt · fscrypt · AF_ALG
same primitive · different I/O paths' },
+ { sym: 'crypto_alloc_aead / skcipher', title: '2 · API SHAPE', body: 'AES-GCM → aead
AES-XTS → skcipher
mode decides the API type' },
+ { sym: 'rounds · SubBytes · MixColumns', title: '3 · INSIDE THE CIPHER', body: '10/12/14 rounds transform the state
demo of those rounds → AES LAB' },
+ { sym: 'GHASH / XTS tweak', title: '4 · MODE MAGIC', body: 'GCM authenticates · XTS tweaks per sector
AES is the engine · mode is the mission' },
+ { sym: 'aes-aesni / cryptd / offload', title: '5 · IMPLEMENTATION', body: 'AES-NI wins on modern x86
generic/simd/offload as fallbacks' }
+ ]
+ },
+ curve25519: {
+ title: 'CURVE25519 → KERNEL DRILL',
+ tagline: 'elliptic-curve DH — short keys, fast agreement, no AES involved',
+ ghost: 'curve25519_generic()',
+ accent: 'rgba(169,212,232,0.55)',
+ openHandshake: true,
+ steps: [
+ { sym: 'who needs X25519?', title: '1 · CONSUMERS', body: 'WireGuard Noise_IK · TLS 1.3 ECDHE
handshake / key agreement only' },
+ { sym: 'crypto_alloc_kpp', title: '2 · KPP API', body: 'key-agreement type in crypto API
set_secret · generate_public · compute_shared' },
+ { sym: 'X25519 scalar mult', title: '3 · THE MATH', body: 'clamp scalar · Montgomery ladder on Curve25519
32-byte public · 32-byte shared secret' },
+ { sym: 'shared secret → HKDF/Noise', title: '4 · AFTER ECDH', body: 'secret feeds key schedule — not the record cipher
WireGuard → ChaCha · TLS → often AES-GCM' },
+ { sym: 'curve25519-generic / fiat', title: '5 · IMPLEMENTATION', body: 'constant-time software paths in-tree
no AES-NI here — different silicon story' }
+ ]
+ },
+ chacha: {
+ title: 'CHACHA20 → KERNEL DRILL',
+ tagline: 'stream cipher DNA shared by VPN AEAD and the CRNG',
+ ghost: 'chacha_permute()',
+ accent: 'rgba(150,255,190,0.5)',
+ steps: [
+ { sym: 'who streams ChaCha?', title: '1 · CONSUMERS', body: 'WireGuard AEAD · random CRNG · AF_ALG
one ARX design · two worlds' },
+ { sym: 'aead vs CRNG core', title: '2 · API SHAPE', body: 'packets → chacha20poly1305 aead
entropy → in-kernel ChaCha CRNG
not always a tfm alloc' },
+ { sym: '20 rounds · quarter-round', title: '3 · INSIDE', body: 'add-rotate-xor mixes a 512-bit state
software-friendly · SIMD loves it' },
+ { sym: 'Poly1305 tag', title: '4 · WITH POLY', body: 'AEAD pairs ChaCha with Poly1305
encrypt + authenticate together' },
+ { sym: 'chacha20-simd / generic', title: '5 · IMPLEMENTATION', body: 'AVX/NEON when present
same family powers get_random_bytes' }
+ ]
+ },
+ sha2: {
+ title: 'SHA-2 → KERNEL DRILL',
+ tagline: 'the measurement workhorse — IMA, HMAC, key derivation helpers',
+ ghost: 'sha256_transform()',
+ accent: 'rgba(196,176,255,0.5)',
+ steps: [
+ { sym: 'who hashes?', title: '1 · CONSUMERS', body: 'IMA/EVM · AF_ALG · IPsec auth · fscrypt names
integrity more often than secrecy' },
+ { sym: 'crypto_alloc_shash', title: '2 · SHASH API', body: 'init/update/final on a shash tfm
HMAC built on the same digest' },
+ { sym: 'SHA-256 / SHA-512', title: '3 · PRIMITIVE', body: 'Merkle–Damgård compression of blocks
fixed-size digest · one-way' },
+ { sym: 'reuse with AES/ECC', title: '4 · IN PROTOCOLS', body: 'TLS finished / HKDF often sit on SHA-2
companion to AES-GCM or X25519' },
+ { sym: 'sha256-avx2 / generic', title: '5 · IMPLEMENTATION', body: 'SIMD digest paths when available
IMA loves throughput here' }
+ ]
+ },
+ ecdsa: {
+ title: 'ECDSA → KERNEL DRILL',
+ tagline: 'signatures on NIST curves — appraisal, modules, trust',
+ ghost: 'ecdsa_verify()',
+ accent: 'rgba(232,150,150,0.5)',
+ steps: [
+ { sym: 'who verifies?', title: '1 · CONSUMERS', body: 'IMA/EVM appraisal · module signing paths
prove origin · not encrypt bytes' },
+ { sym: 'crypto_alloc_akcipher', title: '2 · AKCIPHER API', body: 'asymmetric verify/sign through crypto API
keys as cert/raw coordinates' },
+ { sym: 'P-256 / P-384', title: '3 · CURVE', body: 'ECDSA over prime-field NIST curves
different curve family than Curve25519' },
+ { sym: 'hash-then-sign', title: '4 · WITH SHA-2', body: 'digest first (often SHA-2) · then verify
two primitives · one trust decision' },
+ { sym: 'ecdsa-generic', title: '5 · IMPLEMENTATION', body: 'mostly software · careful constant-time
no AES-NI analogue for ECDSA' }
+ ]
+ },
+ poly: {
+ title: 'POLY1305 → KERNEL DRILL',
+ tagline: 'one-time authenticator — the “tag” half of ChaCha20-Poly1305',
+ ghost: 'poly1305_core()',
+ accent: 'rgba(150,255,190,0.4)',
+ steps: [
+ { sym: 'who needs a MAC?', title: '1 · CONSUMERS', body: 'WireGuard · any chacha20poly1305 aead user
integrity for the ciphertext' },
+ { sym: 'inside AEAD', title: '2 · NOT ALONE', body: 'almost always paired with ChaCha20
one-time key from the cipher state' },
+ { sym: 'polynomial MAC', title: '3 · THE MATH', body: 'evaluate a poly over the message in prime field
fast in software · forgery-resistant with OTKs' },
+ { sym: '16-byte tag', title: '4 · OUTPUT', body: 'auth tag appended / checked on decrypt
fail closed on mismatch' },
+ { sym: 'poly1305-simd / generic', title: '5 · IMPLEMENTATION', body: 'SIMD helpers beside ChaCha
ships as part of the AEAD driver' }
+ ]
+ }
+ };
+ return scripts[id] || null;
+ }
+
+ getArchMorphScript(target) {
+ const id = target?.id;
+ const layer = target?.layer || 'consumers';
+ if (layer === 'primitives') {
+ return this.getPrimitiveMorphScript(id) || {
+ title: 'PRIMITIVE → KERNEL DRILL',
+ tagline: 'algorithm reused across subsystems through the crypto API',
+ ghost: 'crypto_alg_lookup()',
+ accent: 'rgba(150,255,190,0.4)',
+ steps: [
+ { sym: 'consumers', title: '1 · WHO USES IT', body: 'multiple kernel paths may request this alg
reuse is the point of the framework' },
+ { sym: 'crypto_alloc_*', title: '2 · API', body: 'allocated by type + name
aead · skcipher · shash · kpp' },
+ { sym: 'tfm', title: '3 · HANDLE', body: 'ops table bound to this primitive
setkey · encrypt · digest' },
+ { sym: 'driver race', title: '4 · IMPLEMENTATION', body: 'priority picks simd/cpu/offload
same name · faster bytes' }
+ ]
+ };
+ }
+ return this.getConsumerMorphScript(id);
+ }
+
+ clearArchGhost() {
+ if (this._archGhostTimer) {
+ clearTimeout(this._archGhostTimer);
+ this._archGhostTimer = null;
}
+ if (this._archGhostEl) {
+ this._archGhostEl.remove();
+ this._archGhostEl = null;
+ }
+ }
- const width = window.innerWidth;
- const height = window.innerHeight;
- this.svg.attr('viewBox', `0 0 ${width} ${height}`);
- this.svg.selectAll('.crypto-flow-layer').remove();
+ flashArchGhost(code) {
+ this.clearArchGhost();
+ if (!this.container) return;
+ const el = document.createElement('div');
+ el.textContent = String(code || 'crypto_alloc_tfm()');
+ el.style.cssText = [
+ 'position:absolute',
+ 'left:50%',
+ 'top:18%',
+ 'transform:translate(-50%,-8px)',
+ 'opacity:0',
+ 'pointer-events:none',
+ 'z-index:1006',
+ 'font:13px "Share Tech Mono", monospace',
+ 'letter-spacing:0.5px',
+ 'color:rgba(230,193,90,0.92)',
+ 'text-shadow:0 0 14px rgba(230,193,90,0.45)',
+ 'background:rgba(8,12,20,0.55)',
+ 'border:1px solid rgba(230,193,90,0.35)',
+ 'border-radius:4px',
+ 'padding:5px 12px',
+ 'transition:opacity 240ms ease, transform 240ms ease'
+ ].join(';');
+ this.container.appendChild(el);
+ this._archGhostEl = el;
+ requestAnimationFrame(() => {
+ el.style.opacity = '1';
+ el.style.transform = 'translate(-50%,0)';
+ });
+ this._archGhostTimer = setTimeout(() => {
+ el.style.opacity = '0';
+ el.style.transform = 'translate(-50%,-10px)';
+ setTimeout(() => {
+ if (this._archGhostEl === el) {
+ el.remove();
+ this._archGhostEl = null;
+ }
+ }, 280);
+ }, 1700);
+ }
- const layer = this.svg.append('g').attr('class', 'crypto-flow-layer');
- this.drawGrid(layer, width, height);
- if (this.activeCryptoView === 'LINEAR_ANALYSIS') {
- this.drawLinearAnalysisView(layer, payload, width, height, tickId);
- if (this.aesOverlay) this.svg.select('.aes-ops-overlay').raise();
- return;
+ closeArchMorph() {
+ this.archMorphTarget = null;
+ this.clearArchGhost();
+ if (this.archMorphNode) {
+ this.archMorphNode.style.display = 'none';
+ this.archMorphNode.innerHTML = '';
}
- this.drawProtocolLegend(layer);
- this.drawRuntimeSourcesPanel(layer, payload, width, height);
- this.drawEntropyCloud(layer, payload?.meta || {}, width, height);
- this.drawAlgorithmCompetition(layer, payload?.meta || {}, width, height);
- this.drawDecisionPipeline(layer, payload?.meta || {}, width, height);
- this.drawAlgorithmMaterialCard(layer, payload?.meta || {}, width, height);
- this.drawStage1Panels(layer, payload?.meta || {}, width, height);
- this.drawProtectedKernelZones(layer, payload, width, height);
+ }
- const sourceLanes = Array.isArray(payload.items) ? payload.items : [];
- const lanes = sourceLanes.filter((lane) => (
- this.laneMatchesSelectedClients(lane)
- && this.laneMatchesSelectedRequester(lane)
- && this.laneMatchesSelectedImplementationClass(lane)
- ));
- const topY = 172;
- const protocolY = 265;
- const cryptoY = 358;
- const algoY = 452;
- const endpointY = 532;
+ isSchemeNode(id) {
+ return ['ktls', 'aes', 'aead', 'aesni', 'wireguard', 'chacha', 'poly'].includes(String(id || ''));
+ }
- const liveLayout = this.getCryptoLayout(width, height);
- const startX = width * 0.14;
- const flowRightX = Math.max(startX + 220, liveLayout.rightColumnX - 82);
- const usableWidth = Math.max(160, flowRightX - startX);
- const laneCount = Math.max(lanes.length, 1);
- const laneStep = laneCount > 1 ? usableWidth / (laneCount - 1) : 0;
+ resolveSchemeKind(id) {
+ const x = String(id || '');
+ if (['wireguard', 'chacha', 'poly'].includes(x)) return 'wg-chacha';
+ return 'aes-gcm';
+ }
- if (!lanes.length) {
- const selectedLabel = this.selectedClientFilters.size
- ? Array.from(this.selectedClientFilters).join(' + ')
- : 'ALL';
- const requesterLabel = this.selectedRequesterFilter
- ? ` | requester:${this.selectedRequesterFilter.name}`
- : '';
- const classLabel = this.selectedImplementationClassFilter
- ? ` | class:${this.selectedImplementationClassFilter}`
- : '';
- layer.append('text')
- .attr('x', width * 0.42)
- .attr('y', 320)
- .attr('text-anchor', 'middle')
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '12px')
- .style('fill', '#8fa0b6')
- .text(`NO ACTIVE PATHS FOR ${selectedLabel.toUpperCase()}${requesterLabel.toUpperCase()}${classLabel.toUpperCase()}`);
+ stopSchemePlay() {
+ this.schemePlaying = false;
+ if (this._schemePlayTimer) {
+ clearInterval(this._schemePlayTimer);
+ this._schemePlayTimer = null;
}
+ }
- lanes.forEach((lane, idx) => {
- const x = startX + laneStep * idx;
- const intensity = Math.min(1 + lane.weight * 0.35, 2.2);
- const emphasis = Boolean(
- lane.isNew
- || lane.isHot
- || this.selectedRequesterFilter
- || this.selectedImplementationClassFilter
- );
- const laneGroup = layer.append('g').attr('class', 'crypto-lane');
+ openSchemeDiagram(source) {
+ this.closeArchMorph();
+ this.stopSchemePlay();
+ const id = source?.id || 'aes';
+ this.schemeKind = this.resolveSchemeKind(id);
+ this.schemeSource = {
+ id,
+ label: source?.label || id,
+ layer: source?.layer || ''
+ };
+ this.schemePhase = 0;
+ this.schemeRendered = false;
+ this.archFocus = null;
+ this.activeCryptoView = 'SCHEME';
+ this.updateCryptoViewToggle();
+ this.syncOverlayForCurrentView();
+ this.renderFlowMap(this.lastPayload || this.normalizeTelemetry(this.getFallbackTelemetry()));
+ // Auto-play once so the diagram feels alive immediately.
+ setTimeout(() => this.startSchemePlay(), 280);
+ }
- const pNode = this.drawNode(laneGroup, x, topY, lane.process, 'process', intensity, lane.palette, emphasis);
- const protoNode = this.drawNode(laneGroup, x, protocolY, lane.protocol, 'protocol', intensity, lane.palette, emphasis);
- const cNode = this.drawNode(laneGroup, x, cryptoY, 'crypto subsystem', 'crypto', intensity, lane.palette, emphasis);
- const aNode = this.drawNode(laneGroup, x, algoY, lane.algorithm, 'algorithm', intensity, lane.palette, emphasis);
+ startSchemePlay() {
+ if (this.activeCryptoView !== 'SCHEME') return;
+ this.stopSchemePlay();
+ this.schemePlaying = true;
+ this.schemePhase = 0;
+ if (this.svg) this.svg.select('.scheme-play-label').text('■ STOP');
+ this.applySchemePhase(0);
+ this._schemePlayTimer = setInterval(() => {
+ if (!this.isActive || this.activeCryptoView !== 'SCHEME' || !this.schemePlaying) {
+ this.stopSchemePlay();
+ if (this.svg) this.svg.select('.scheme-play-label').text('▶ PLAY');
+ return;
+ }
+ this.schemePhase = (this.schemePhase + 1) % 7;
+ this.applySchemePhase(this.schemePhase);
+ }, 1100);
+ }
- const p1 = [pNode.bottom, protoNode.top];
- const p2 = [protoNode.bottom, cNode.top];
- const p3 = [cNode.bottom, aNode.top];
+ schemeElixirIdent(sym) {
+ const clean = String(sym || '')
+ .replace(/\(\)$/, '')
+ .replace(/\(.*\)$/, '')
+ .split(/[\s/]+/)[0]
+ .trim();
+ if (!clean || clean.startsWith('…')) return null;
+ return `https://elixir.bootlin.com/linux/latest/A/ident/${encodeURIComponent(clean)}`;
+ }
- this.drawPath(laneGroup, p1, intensity, lane.palette, emphasis);
- this.drawPath(laneGroup, p2, intensity, lane.palette, emphasis);
- this.drawPath(laneGroup, p3, intensity, lane.palette, emphasis);
+ schemeElixirFile(path) {
+ const clean = String(path || '').replace(/^\/+/, '');
+ if (!clean) return null;
+ return `https://elixir.bootlin.com/linux/latest/source/${clean}`;
+ }
- this.animatePacket(
- laneGroup,
- [pNode.bottom, protoNode.top, protoNode.bottom, cNode.top, cNode.bottom, aNode.top],
- intensity,
- tickId,
- lane.palette,
- emphasis
- );
+ openSchemeCodeRef(ref) {
+ if (!ref) return;
+ const url = ref.url || this.schemeElixirIdent(ref.sym) || this.schemeElixirFile(ref.file);
+ if (!url) return;
+ try {
+ window.open(url, '_blank', 'noopener,noreferrer');
+ } catch (e) {
+ /* ignore popup blockers quietly */
+ }
+ this.flashArchGhost(ref.sym || ref.file || 'kernel source');
+ }
- laneGroup.append('text')
- .attr('x', x)
- .attr('y', endpointY)
- .attr('text-anchor', 'middle')
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '10px')
- .style('fill', '#9ba5b4')
- .style('letter-spacing', '0.2px')
- .text(`pid:${lane.pid || '?'} ${lane.endpoint || '-'}`);
+ getSchemePhaseMeta(phase) {
+ if (this.schemeKind === 'wg-chacha') return this.getWgChachaSchemePhaseMeta(phase);
+ return this.getAesGcmSchemePhaseMeta(phase);
+ }
- if (lane.isNew || lane.isHot) {
- laneGroup.append('text')
- .attr('x', x)
- .attr('y', 118)
- .attr('text-anchor', 'middle')
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '10px')
- .style('fill', lane.isNew ? '#86ffd0' : '#ffd38c')
- .style('letter-spacing', '0.3px')
- .text(lane.isNew ? 'NEW' : 'HOT');
+ getWgChachaSchemePhaseMeta(phase) {
+ const table = [
+ {
+ narr: 'WireGuard packet ready — Noise keys already agreed, AEAD protects the payload',
+ ghost: 'wg_packet_encrypt_worker()',
+ kernel: 0,
+ keyStage: 0,
+ inspect: [
+ 'WIREGUARD',
+ 'skb enters encrypt path',
+ 'peer keys / counters ready',
+ 'ChaCha20-Poly1305 is the AEAD'
+ ],
+ refs: [
+ { sym: 'wg_packet_encrypt_worker', file: 'drivers/net/wireguard/send.c', note: 'encrypt worker' },
+ { sym: 'wg_socket_send_buffer_as_reply_to_skb', file: 'drivers/net/wireguard/socket.c', note: 'send path' },
+ { sym: 'curve25519_generic', file: 'lib/crypto/curve25519.c', note: 'handshake ECDH' }
+ ]
+ },
+ {
+ narr: 'crypto_alloc_aead("chacha20poly1305") — unified crypto API entry',
+ ghost: 'crypto_alloc_aead()',
+ kernel: 1,
+ keyStage: 1,
+ inspect: [
+ 'CRYPTO API',
+ 'name lookup → tfm',
+ 'same alloc path as kTLS',
+ 'priority race picks SIMD/generic'
+ ],
+ refs: [
+ { sym: 'crypto_alloc_aead', file: 'crypto/api.c', note: 'tfm allocation' },
+ { sym: 'chacha20poly1305_encrypt', file: 'lib/crypto/chacha20poly1305.c', note: 'lib AEAD helper' },
+ { sym: 'crypto_register_aeads', file: 'crypto/aead.c', note: 'register AEAD algs' }
+ ]
+ },
+ {
+ narr: 'ChaCha20 · 20 rounds of ARX quarter-rounds on 512-bit state',
+ ghost: 'chacha_permute()',
+ kernel: 2,
+ keyStage: 3,
+ inspect: [
+ 'CHACHA20',
+ 'add-rotate-xor quarter-rounds',
+ 'counter + nonce → keystream',
+ 'software-friendly · SIMD loves it'
+ ],
+ refs: [
+ { sym: 'chacha_block_generic', file: 'lib/crypto/chacha.c', note: 'generic block' },
+ { sym: 'chacha_2block_xor_avx2', file: 'arch/x86/crypto/chacha_x86_64_glue.c', note: 'AVX2 path' },
+ { sym: 'chacha_init_generic', file: 'lib/crypto/chacha.c', note: 'state init' }
+ ]
+ },
+ {
+ narr: 'rounds continue · 10 double-rounds (20 quarter-round layers)',
+ ghost: '… 20 rounds …',
+ kernel: 2,
+ keyStage: 4,
+ inspect: [
+ 'MIDDLE ROUNDS',
+ 'diagram compresses like textbook · · ·',
+ 'keystream fills 64-byte blocks',
+ 'no AES-NI here — different silicon story'
+ ],
+ refs: [
+ { sym: 'chacha_permute', file: 'lib/crypto/chacha.c', note: 'core permute' },
+ { sym: 'chacha_crypt_generic', file: 'lib/crypto/chacha.c', note: 'XOR keystream' }
+ ]
+ },
+ {
+ narr: 'keystream ⊕ plaintext → ciphertext (same length)',
+ ghost: 'chacha20poly1305_encrypt()',
+ kernel: 2,
+ keyStage: 3,
+ inspect: [
+ 'XOR KEYSTREAM',
+ 'stream cipher · no block padding',
+ 'WireGuard packet body encrypted',
+ 'auth still pending (Poly1305)'
+ ],
+ refs: [
+ { sym: 'chacha20poly1305_encrypt', file: 'lib/crypto/chacha20poly1305.c', note: 'encrypt+tag API' },
+ { sym: 'chacha_crypt_generic', file: 'lib/crypto/chacha.c', note: 'keystream XOR' }
+ ]
+ },
+ {
+ narr: 'Poly1305 one-time MAC → 16-byte tag over AAD + ciphertext',
+ ghost: 'poly1305_core()',
+ kernel: 3,
+ keyStage: 2,
+ inspect: [
+ 'POLY1305',
+ 'polynomial MAC in prime field',
+ 'one-time key from ChaCha state',
+ 'forgery-resistant with OTKs'
+ ],
+ refs: [
+ { sym: 'poly1305_core_blocks', file: 'lib/crypto/poly1305.c', note: 'Poly1305 core' },
+ { sym: 'poly1305_update', file: 'crypto/poly1305_generic.c', note: 'generic update' },
+ { sym: 'chacha20poly1305_encrypt', file: 'lib/crypto/chacha20poly1305.c', note: 'AEAD wrapper' }
+ ]
+ },
+ {
+ narr: 'ciphertext ∥ tag → WireGuard UDP — Noise handshake already done',
+ ghost: 'wg_socket_send_skb()',
+ kernel: 4,
+ keyStage: 0,
+ inspect: [
+ 'WIRE OUT',
+ 'encrypted transport message',
+ 'X25519 only in handshake path',
+ '→ HANDSHAKE for ECDH story'
+ ],
+ refs: [
+ { sym: 'wg_packet_create_data_done', file: 'drivers/net/wireguard/send.c', note: 'packet done' },
+ { sym: 'udp_sendmsg', file: 'net/ipv4/udp.c', note: 'UDP transmit' },
+ { sym: 'curve25519_generic', file: 'lib/crypto/curve25519.c', note: 'handshake only' }
+ ]
+ }
+ ];
+ const row = table[phase] || table[0];
+ row.refs = (row.refs || []).map((r) => ({
+ ...r,
+ url: this.schemeElixirIdent(r.sym) || this.schemeElixirFile(r.file)
+ }));
+ return row;
+ }
+
+ getAesGcmSchemePhaseMeta(phase) {
+ const nr = this.schemeNr || 10;
+ const driver = (() => {
+ try {
+ const meta = this.lastPayload?.meta || {};
+ const comp = this.getCompetitionPayload(meta) || {};
+ return String(comp?.selected?.name || 'aesni / ce').replace(/^_+/, '').slice(0, 28);
+ } catch (e) {
+ return 'aesni / ce';
}
+ })();
+ const isArmCe = /(-ce\b|neon|armv8|aes-ce)/i.test(driver);
+ const aesImpl = isArmCe
+ ? { sym: 'ce_aes_ecb_encrypt', file: 'arch/arm64/crypto/aes-ce-glue.c', label: 'AES-CE glue' }
+ : { sym: 'aesni_encrypt', file: 'arch/x86/crypto/aesni-intel_glue.c', label: 'AES-NI glue' };
+ const ghashImpl = isArmCe
+ ? { sym: 'gcm_setkey', file: 'arch/arm64/crypto/aes-ce-ccm-glue.c', label: 'ARM CE GCM' }
+ : { sym: 'ghash_clmulni_digest', file: 'arch/x86/crypto/ghash-clmulni-intel_glue.c', label: 'PCLMUL GHASH' };
+
+ const table = [
+ {
+ narr: 'TLS record lands in kTLS — 128-bit AES state ready',
+ ghost: 'tls_sw_sendmsg()',
+ kernel: 0,
+ keyStage: 0,
+ inspect: [
+ 'KERNEL',
+ 'tls_sw_sendmsg / kTLS record path',
+ 'plaintext + AAD prepared for AEAD',
+ `driver waiting: ${driver}`
+ ],
+ refs: [
+ { sym: 'tls_sw_sendmsg', file: 'net/tls/tls_sw.c', note: 'kTLS software send' },
+ { sym: 'tls_sw_recvmsg', file: 'net/tls/tls_sw.c', note: 'kTLS software recv' },
+ { sym: 'crypto_alloc_aead', file: 'crypto/api.c', note: 'tfm allocation gate' }
+ ]
+ },
+ {
+ narr: 'round 1 · AddRoundKey ⊕ K₀ → SubBytes → ShiftRows → MixColumns',
+ ghost: 'crypto_aead_encrypt()',
+ kernel: 1,
+ keyStage: 3,
+ inspect: [
+ 'ROUND 1',
+ '⊕ K₀ mixes key into state',
+ 'S-box · ShiftRows · MixColumns',
+ 'first diffusion of the block'
+ ],
+ refs: [
+ { sym: 'crypto_aead_encrypt', file: 'include/linux/crypto.h', note: 'AEAD encrypt entry' },
+ { sym: 'crypto_aead_setkey', file: 'crypto/aead.c', note: 'bind traffic key' },
+ { sym: aesImpl.sym, file: aesImpl.file, note: aesImpl.label }
+ ]
+ },
+ {
+ narr: 'round 2 · same spine · next round key K₁',
+ ghost: `${aesImpl.sym}()`,
+ kernel: 2,
+ keyStage: 4,
+ inspect: [
+ 'ROUND 2',
+ 'silicon path preferred when present',
+ `selected: ${driver}`,
+ 'same API — faster bytes'
+ ],
+ refs: [
+ { sym: aesImpl.sym, file: aesImpl.file, note: aesImpl.label },
+ { sym: 'crypto_aes_encrypt', file: 'crypto/aes_generic.c', note: 'generic fallback' },
+ { sym: 'crypto_register_algs', file: 'crypto/algapi.c', note: 'priority registration' }
+ ]
+ },
+ {
+ narr: `rounds 3…${nr - 1} · omitted middle (Nr=${nr})`,
+ ghost: `… ${nr - 2} rounds …`,
+ kernel: 2,
+ keyStage: 4,
+ inspect: [
+ 'MIDDLE ROUNDS',
+ `AES-${nr === 14 ? '256' : '128'} → Nr=${nr}`,
+ 'diagram compresses like textbook · · ·',
+ 'toggle Nr chips to switch story length'
+ ],
+ refs: [
+ { sym: 'crypto_aes_set_key', file: 'crypto/aes_generic.c', note: 'key expand / Nr' },
+ { sym: aesImpl.sym, file: aesImpl.file, note: 'hot round loop' },
+ { sym: 'aes_expandkey', file: 'lib/crypto/aes.c', note: 'lib/crypto expand' }
+ ]
+ },
+ {
+ narr: `final round ${nr} · no MixColumns · ⊕ Kₙ`,
+ ghost: `${aesImpl.sym}()`,
+ kernel: 2,
+ keyStage: 5,
+ inspect: [
+ 'FINAL ROUND',
+ 'SubBytes + ShiftRows + ⊕ Kₙ',
+ 'MixColumns omitted on last round',
+ 'state is now ciphertext block'
+ ],
+ refs: [
+ { sym: aesImpl.sym, file: aesImpl.file, note: 'final round in asm/glue' },
+ { sym: 'crypto_aes_encrypt', file: 'crypto/aes_generic.c', note: 'C reference path' }
+ ]
+ },
+ {
+ narr: 'GHASH authenticates AAD + ciphertext → tag',
+ ghost: 'ghash_update()',
+ kernel: 3,
+ keyStage: 2,
+ inspect: [
+ 'AEAD TAG',
+ 'GHASH over AAD ∥ ciphertext',
+ 'PCLMULQDQ helps on x86',
+ 'integrity without a separate HMAC'
+ ],
+ refs: [
+ { sym: ghashImpl.sym, file: ghashImpl.file, note: ghashImpl.label },
+ { sym: 'crypto_gcm_encrypt', file: 'crypto/gcm.c', note: 'GCM mode wrapper' },
+ { sym: 'ghash_update', file: 'crypto/ghash-generic.c', note: 'generic GHASH' }
+ ]
+ },
+ {
+ narr: 'ciphertext ∥ tag leaves on the TCP / kTLS path',
+ ghost: 'tls_sw_sendmsg()',
+ kernel: 4,
+ keyStage: 1,
+ inspect: [
+ 'WIRE OUT',
+ 'encrypted TLS record on the socket',
+ 'X25519 already left the hot path',
+ '→ HANDSHAKE for the key-agreement story'
+ ],
+ refs: [
+ { sym: 'tls_sw_sendmsg', file: 'net/tls/tls_sw.c', note: 'push encrypted record' },
+ { sym: 'tcp_sendmsg', file: 'net/ipv4/tcp.c', note: 'TCP transmit' },
+ { sym: 'crypto_aead_encrypt', file: 'include/linux/crypto.h', note: 'completed AEAD op' }
+ ]
+ }
+ ];
+ const row = table[phase] || table[0];
+ row.refs = (row.refs || []).map((r) => ({
+ ...r,
+ url: this.schemeElixirIdent(r.sym) || this.schemeElixirFile(r.file)
+ }));
+ return row;
+ }
- laneGroup
- .style('cursor', 'crosshair')
- .on('mouseenter', (event) => this.showHoverCard(lane, event))
- .on('mousemove', (event) => this.positionHoverCard(event))
- .on('mouseleave', () => this.hideHoverCard());
+ applySchemePhase(phase) {
+ if (!this.svg) return;
+ const root = this.svg.select('.crypto-scheme-view');
+ if (root.empty()) return;
+ const meta = this.getSchemePhaseMeta(phase);
+ const nr = this.schemeNr || 10;
+
+ root.selectAll('.scheme-phase-group').style('opacity', function opacity() {
+ const p = Number(this.getAttribute('data-phase'));
+ if (Number.isNaN(p)) return 0.55;
+ return p === phase ? 1 : (Math.abs(p - phase) === 1 ? 0.72 : 0.28);
});
+ root.selectAll('.scheme-phase-group').select('rect.scheme-phase-glow')
+ .style('opacity', function glowOp() {
+ const p = Number(this.parentNode.getAttribute('data-phase'));
+ return p === phase ? 0.55 : 0;
+ });
- const legend = layer.append('g').attr('class', 'crypto-legend');
- const lx = 26;
- const ly = 160;
- legend.append('text')
- .attr('x', lx)
- .attr('y', ly)
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '11px')
- .style('fill', '#d2d9e5')
- .text(
- this.selectedClientFilters.size || this.selectedRequesterFilter
- || this.selectedImplementationClassFilter
- ? `ACTIVE PATHS (${[
- this.selectedClientFilters.size
- ? Array.from(this.selectedClientFilters).join(' + ')
- : null,
- this.selectedRequesterFilter
- ? `requester:${this.selectedRequesterFilter.name}`
- : null,
- this.selectedImplementationClassFilter
- ? `class:${this.selectedImplementationClassFilter}`
- : null
- ].filter(Boolean).join(' | ')})`
- : 'ACTIVE PATHS'
- );
+ root.select('.scheme-narrator').text(meta.narr || '');
+ root.select('.scheme-phase-pip').text(
+ this.schemeKind === 'wg-chacha'
+ ? `phase ${phase + 1}/7 · ChaCha 20 rounds · PLAY / STEP / click stage`
+ : `phase ${phase + 1}/7 · Nr=${nr} · PLAY / STEP / click stage`
+ );
- lanes.slice(0, 8).forEach((lane, idx) => {
- legend.append('text')
- .attr('x', lx)
- .attr('y', ly + 22 + idx * 15)
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '10px')
- .style('fill', lane.palette.label)
- .text(`${lane.process} -> ${lane.protocol} -> ${lane.algorithm}`);
+ // Kernel call rail
+ root.selectAll('.scheme-kernel-step').style('opacity', function kOp() {
+ const k = Number(this.getAttribute('data-kernel'));
+ return k === meta.kernel ? 1 : (Math.abs(k - meta.kernel) === 1 ? 0.55 : 0.22);
});
+ root.selectAll('.scheme-kernel-step').select('rect')
+ .style('stroke', function kStroke() {
+ const k = Number(this.parentNode.getAttribute('data-kernel'));
+ return k === meta.kernel ? '#e6c15a' : 'rgba(120,140,170,0.35)';
+ });
- const goneY = ly + 165;
- legend.append('text')
- .attr('x', lx)
- .attr('y', goneY)
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '10px')
- .style('fill', '#9fa9b9')
- .text('RECENTLY CLOSED');
+ // Key schedule sync
+ root.selectAll('.scheme-key-stage').style('opacity', function keyOp() {
+ const k = Number(this.getAttribute('data-key-stage'));
+ return k === meta.keyStage ? 1 : 0.35;
+ });
+ root.selectAll('.scheme-key-stage').select('ellipse, rect')
+ .style('stroke', function keyStroke() {
+ const k = Number(this.parentNode.getAttribute('data-key-stage'));
+ return k === meta.keyStage ? '#e6c15a' : 'rgba(196,176,255,0.45)';
+ });
- this.recentlyGone.slice(0, 5).forEach((item, idx) => {
- const age = Math.max(0, Math.round((Date.now() - item.at) / 1000));
- legend.append('text')
- .attr('x', lx)
- .attr('y', goneY + 16 + idx * 14)
- .style('font-family', 'Share Tech Mono, monospace')
- .style('font-size', '10px')
- .style('fill', '#8e98a9')
- .text(`- ${item.label} (${age}s)`);
+ // Mini state grid + optional demo byte
+ let demoHex = null;
+ try {
+ const pt = this.aesDemo?.demo_vectors?.plaintext;
+ if (pt && typeof pt === 'string') demoHex = pt.replace(/\s/g, '');
+ } catch (e) { /* ignore */ }
+ root.selectAll('.scheme-state-cell').each((d, i, nodes) => {
+ const el = d3.select(nodes[i]);
+ const mine = Number(el.attr('data-i'));
+ const hot = (mine === ((this.schemeInspectByte + phase * 3) % 16));
+ el.style('fill', hot
+ ? 'rgba(230,193,90,0.9)'
+ : (mine % 4 === phase % 4 ? 'rgba(150,255,190,0.45)' : 'rgba(40,55,75,0.85)'));
+ el.style('stroke', hot ? '#e6c15a' : 'rgba(140,160,180,0.35)');
+ });
+
+ // Inspect panel
+ const lines = meta.inspect || [];
+ root.selectAll('.scheme-inspect-line').each(function insp(d, i) {
+ d3.select(this).text(lines[i] || '');
+ });
+ if (this.schemeKind === 'wg-chacha') {
+ const labels = ['const', 'const', 'const', 'const', 'key', 'key', 'key', 'key',
+ 'key', 'key', 'key', 'key', 'ctr', 'nonce', 'nonce', 'nonce'];
+ const li = this.schemeInspectByte % 16;
+ root.select('.scheme-inspect-byte')
+ .text(`ChaCha state[${li}] · ${labels[li]} word · 512-bit matrix`);
+ } else if (demoHex && demoHex.length >= 32) {
+ const bi = (this.schemeInspectByte % 16) * 2;
+ const byte = demoHex.slice(bi, bi + 2).toUpperCase();
+ root.select('.scheme-inspect-byte')
+ .text(`demo state[${this.schemeInspectByte}] = 0x${byte} (educational vector)`);
+ } else {
+ root.select('.scheme-inspect-byte')
+ .text('demo vector: load AES LAB data for live bytes');
+ }
+
+ // Code refs for this phase
+ const refs = meta.refs || [];
+ this._schemePhaseRefs = refs;
+ root.selectAll('.scheme-code-ref').each(function refRow(d, i) {
+ const row = d3.select(this);
+ const ref = refs[i];
+ if (!ref) {
+ row.style('display', 'none');
+ return;
+ }
+ row.style('display', null).attr('data-ref-i', i);
+ row.select('.scheme-code-sym').text(ref.sym || '');
+ row.select('.scheme-code-file').text(ref.file || '');
+ row.select('.scheme-code-note').text(ref.note || '');
+ });
+ root.select('.scheme-code-hint')
+ .text(refs[0] ? `click a symbol → Elixir · primary: ${refs[0].sym}` : 'no code refs');
+
+ root.selectAll('.scheme-key-inject').style('stroke-opacity', function inj() {
+ const p = Number(this.getAttribute('data-phase'));
+ return p === phase ? 1 : 0.25;
+ });
+
+ root.selectAll('.scheme-nr-chip').style('opacity', function nrOp() {
+ return Number(this.getAttribute('data-nr')) === nr ? 1 : 0.4;
});
+
+ if (meta.ghost) this.flashArchGhost(meta.ghost);
}
- renderTelemetryPayload(normalized) {
+ openArchMorph(target) {
+ if (!target?.id) return;
+ // Textbook scheme opens from relevant Architecture nodes — not a separate menu tab.
+ if (this.isSchemeNode(target.id)) {
+ this.openSchemeDiagram(target);
+ return;
+ }
+ this.archFocus = {
+ layer: target.layer || 'consumers',
+ id: target.id,
+ label: target.label,
+ hint: target.hint
+ };
+ this.archMorphTarget = target;
+ this.renderArchMorphRibbon();
+ this.renderFlowMap(this.lastPayload || this.normalizeTelemetry(this.getFallbackTelemetry()));
+ }
+
+ renderArchMorphRibbon() {
+ if (!this.archMorphNode || !this.archMorphTarget) return;
+ const script = this.getArchMorphScript(this.archMorphTarget);
+ const esc = (v) => this.escapeArchHtml(v);
+ const stepsHtml = (script.steps || []).map((step, idx) => (
+ '