# webcrypto-interceptor v0.3.0
$ git clone https://github.com/cipherwire/webcrypto-interceptor.git
SECTION 01 // OVERVIEW

System Architecture & Pipeline

Chrome DevTools Protocol (CDP) instrumentation engine built for authorized security research, WebCrypto observation, and client-side reverse engineering in Chromium browsers.

[STATUS: v0.3.0 VERIFIED]

87 passing unit and live browser integration tests. Zero npm audit vulnerabilities. Maintenance LTS on Node.js 22, Active LTS on Node.js 24.

The Boundary Problem with Network Proxies

Modern web applications and streaming video players rely on the W3C Web Cryptography API (window.crypto.subtle) to encrypt data directly inside the browser execution context prior to transmission.

Network proxies such as Burp Suite or mitmproxy inspect traffic downstream on the wire. By the time a packet hits the proxy socket, the payload is already an opaque encrypted ciphertext. Decryption occurs client-side after the response arrives.

webcrypto-interceptor solves this by attaching directly to the Chromium browser engine via the Chrome DevTools Protocol. It intercepts cryptographic routines at the V8 engine boundary, capturing cleartext arguments, keys, initialization vectors, and return values.

Core Pipeline Architecture

+-------------------------------------------------------------------------+
| Chromium Browser Process (V8 Engine)                                    |
|                                                                         |
|  [Page / Worker Context]                                                |
|    crypto.subtle.encrypt(algorithm, key, cleartextBuffer)               |
|          |                                                              |
|          v                                                              |
|    [V8 Breakpoint / Stealth Trap]                                       |
|    - Normalizes ArrayBuffer and CryptoKey to JSON envelope              |
|    - Captures arguments, IV, cleartext, execution stack trace           |
|          |                                                              |
|          v                                                              |
|    [CDP Binding: __wci(payload)]                                        |
+----------|--------------------------------------------------------------+
           | (Stdio Pipe Transport: pipe=true, zero open TCP listening ports)
           v
+-------------------------------------------------------------------------+
| Node.js Interceptor Process (capture_server.js)                         |
|                                                                         |
|  [CDP Protocol Dispatcher]                                              |
|    - Runtime.bindingCalled event listener                               |
|    - Formats timestamp, context ID, and event category                 |
|          |                                                              |
|          +---> stdout (colorized live console stream)                   |
|          +---> session_capture_<timestamp>.jsonl (offline archive)      |
|          +---> wasm_modules/module_<hash>.wasm (reconstructed bytecode) |
+-------------------------------------------------------------------------+

Security & Stealth Guarantees

  • Out-of-Process Pipe Transport: Puppeteer launches Chromium using anonymous standard input/output pipes (pipe: true). It never opens exposed TCP listening ports on localhost, preventing local process hijacking.
  • V8 Breakpoints: Non-intrusive breakpoints armed via Debugger.setBreakpointOnFunctionCall. Native function prototypes retain their original toString() signatures, evading anti-tamper heuristics.
  • CDP Binding Relay: Uses Runtime.addBinding (__wci) to stream structured event envelopes from sandboxed Web Workers and cross-origin iframes directly to Node.js.

Installation & Quickstart

terminal
# Clone repository
git clone https://github.com/cipherwire/webcrypto-interceptor.git
cd webcrypto-interceptor

# Install dependencies (0 vulnerabilities verified by npm audit)
npm install

# Launch interactive inspection
node capture_server.js "http://127.0.0.1:8080/app" --gui
SECTION 02 // WEBCRYPTO

SubtleCrypto Observation & Serialization

Full-spectrum observation of window.crypto.subtle operations, key parameters, nonces, and call-stack attribution directly from the V8 execution engine.

Monitored SubtleCrypto Routines

API Method Intercepted Data Target Algorithms
crypto.subtle.encrypt() Algorithm parameters, IV/nonce, Key, Cleartext data AES-GCM, AES-CBC, AES-CTR, RSA-OAEP
crypto.subtle.decrypt() Algorithm parameters, IV/nonce, Key, Ciphertext data AES-GCM, AES-CBC, AES-CTR, RSA-OAEP
crypto.subtle.importKey() Key format, raw key bytes, extractable flag, key usages raw, pkcs8, spki, jwk
crypto.subtle.exportKey() Export format, CryptoKey reference, exported bytes raw, pkcs8, spki, jwk
crypto.subtle.generateKey() Algorithm parameters, extractable flag, usages AES, HMAC, RSA, ECDSA, ECDH
crypto.subtle.deriveKey() / deriveBits() KDF algorithm, salt, iterations, base key PBKDF2, HKDF, ECDH
crypto.subtle.sign() / verify() Signature algorithm, key, signature bytes, cleartext HMAC, RSASSA-PKCS1-v1_5, ECDSA, Ed25519
crypto.subtle.digest() Hash algorithm, data buffer, digest output SHA-256, SHA-384, SHA-512

Binary Serialization Protocol

JavaScript cryptographic APIs operate on binary buffers (ArrayBuffer, Uint8Array) and opaque CryptoKey handles. Because these types cannot be passed directly across JSON IPC boundaries, webcrypto-interceptor normalizes them using structured envelopes:

session_capture.jsonl envelope
{
  "__t": "Uint8Array",
  "len": 16,
  "hex": "f3a19b88210344c8b291845199201a4e",
  "utf8": "..."
}

{
  "__t": "CryptoKey",
  "type": "secret",
  "extractable": true,
  "algorithm": { "name": "AES-GCM", "length": 256 },
  "usages": ["encrypt", "decrypt"]
}

Nonces and getRandomValues Rate Limiting

Initialization vectors (IVs) and cryptographic nonces are frequently generated via crypto.getRandomValues() immediately prior to encryption.

To prevent terminal flooding from WebGL canvases or math libraries calling random functions thousands of times per second, the interceptor applies a token-bucket rate limiter that selectively logs cryptographic-length buffers (12 bytes for AES-GCM IVs, 16 bytes for AES-CBC IVs).

SECTION 03 // STREAMING

DRM & HLS Stream Key Extraction

Capturing and decrypting HTTP Live Streaming (HLS) media segments (MPEG-TS, fMP4) and correlating them with raw cryptographic keys.

HLS Envelope Encryption Mechanism

HLS streams frequently protect media segments using envelope encryption (METHOD=AES-128 or METHOD=SAMPLE-AES). The player fetches a playlist manifest containing key tags:

master.m3u8
#EXT-X-KEY:METHOD=AES-128,URI="https://stream.target.local/keys/key_01.bin",IV=0x00000000000000000000000000000001
#EXTINF:6.000,
segment_001.ts
#EXTINF:6.000,
segment_002.ts

When video players use JavaScript libraries (like hls.js, shaka-player, or video.js), segment decryption is delegated to the browser's crypto.subtle.decrypt() routine, often inside a dedicated Web Worker thread.

Worker Thread Interception

Because Puppeteer's standard console output does not capture background worker contexts by default, webcrypto-interceptor registers automatic CDP listeners on every created Web Worker target (Target.attachedToTarget).

When a worker calls crypto.subtle.importKey() to load the 16-byte raw AES key and then calls crypto.subtle.decrypt() to unpack media segments, the interceptor logs the exact key bytes and initialization vectors.

Offline Correlation Utility

terminal
node scripts/extract-keys.js session_capture_1730000000.jsonl
stdout
url:  https://media.example.local/stream/segment_001.ts
key:  f3a19b88210344c8b291845199201a4e
iv:   00000000000000000000000000000001
cmd:  openssl enc -d -aes-128-cbc -K f3a19b88210344c8b291845199201a4e -iv 00000000000000000000000000000001 -in segment_001.ts -out segment_001_decrypted.ts
SECTION 04 // BINARY

WebAssembly Bytecode Dumping

Intercepting client-side WebAssembly module compilation, streaming instantiations, and saving reconstructed binary bytecode to disk for reverse engineering.

Bytecode Interception Hooks

Target applications frequently embed compiled C/Rust/Go cryptography routines inside WebAssembly binaries to defeat pure JavaScript deobfuscation.

webcrypto-interceptor instruments the standard WebAssembly lifecycle APIs in src/page/stealth.js:

  • WebAssembly.compile(bytes)
  • WebAssembly.instantiate(bytes, importObject)
  • WebAssembly.compileStreaming(responsePromise)
  • WebAssembly.instantiateStreaming(responsePromise, importObject)

Payload Chunking & Reassembly

Large WebAssembly modules can exceed individual CDP IPC string length limits. The in-page hook splits large bytecode buffers into 32KB hex-encoded chunks tagged with sequence numbers (seq, of, hash).

In Node.js, src/cdp/events.js reassembles the chunks in order, verifies the hash integrity, and writes the reconstructed binary to:

filesystem output
wasm_modules/module_<hash>.wasm

Export Function Wrapping

When an instantiated WebAssembly instance exposes exported functions (e.g., instance.exports.encryptBlock), the toolkit wraps each export function to log its pointer arguments and return values across the JavaScript/WASM boundary:

stdout
[00:00.820] [WASM COMPILE] WebAssembly.compile called with 14,280 bytes
[00:00.825] [WASM DUMPED] Saved bytecode to wasm_modules/module_d41d8cd9.wasm
[00:00.831] [WASM HOOK] Wrapped exports: ["encryptBlock", "deriveSecret", "memory"]
SECTION 05 // REFERENCE

CLI Flags & Environment Variables

Complete manual for all command-line arguments, runtime flags, and configuration options supported by capture_server.js.

Command Synopsis

syntax
node capture_server.js <URL> [options]

Options & Flags

Flag / Option Type Default Description
<URL> string required Target URL to open and observe. Must use http:// or https://.
--gui flag false (headless) Launch Chromium with a visible browser window for manual interaction.
--brave flag false Prefer Brave Browser binary over default Google Chrome or Chromium.
--full flag false Save all loaded scripts, sourcemaps, responses, and WASM bytecode to disk.
--out <dir> string ./<host>_extracted_<ts> Custom output directory for assets dumped under --full mode.
--proxy <url> string none Route browser network requests through an upstream HTTP/SOCKS proxy.
--ua <string> string none Override default Chromium User-Agent header string.
--hook <expr> string none Trap and log every invocation of JavaScript expression with arguments (repeatable).
--hook-return <expr> string none Trap arguments AND return values of expression (repeatable).
--heap-diff <sec> number disabled Take baseline heap snapshot on load, second snapshot after N seconds, diff new objects.
--timeout <sec> number 60 Maximum navigation and capture timeout in seconds.
--help flag false Print CLI option help and exit.

Environment Variables

Variable Default Description
PUPPETEER_EXECUTABLE_PATH auto-detected Explicit filesystem path to Chrome, Chromium, or Brave executable.
PUPPETEER_SKIP_DOWNLOAD false Skip downloading Chromium archive on npm install.
SKIP_E2E false Skip live browser tests when running npm test.
SECTION 06 // VERIFICATION

Verification Oracle (verify-reimpl.js)

An automated testing oracle for validating standalone JavaScript reimplementations of obfuscated client-side functions against recorded ground-truth session data.

The Clean-Room Verification Loop

When reverse-engineering proprietary client-side signers, token generators, or custom obfuscators, researchers frequently rewrite the logic into a standalone library.

scripts/verify-reimpl.js provides an automated oracle that proves whether your candidate implementation produces mathematically identical outputs across all recorded real-world inputs:

workflow
Record Ground Truth -> Write Clean-Room Candidate -> verify-reimpl.js -> VERIFIED

1. Recording Input/Output Pairs

terminal
node capture_server.js "https://target.local" --hook-return "window.signPayload" --gui

2. Executing the Oracle

terminal
node scripts/verify-reimpl.js --session session_capture_1730000000.jsonl --candidate ./my_signer.js --label "window.signPayload"

3. Sandboxed Execution Security

  • Network Isolation: The oracle executes candidate code inside an isolated headless browser page loaded to about:blank with zero network privileges.
  • No Target Access: The candidate cannot communicate with the target website or exfiltrate session data.
  • Strict Deep Equality: Compares candidate outputs against captured ground truth using type-strict, order-aware array checks and key-set-aware object diffing.
SECTION 07 // WORKBENCH

Live CDP Stream Simulator

Interactive console simulator showing real-time event logs, binary envelopes, and telemetry streamed by webcrypto-interceptor during live target capture.