Behind the Magic Numbers: PRNGs, Browser Fingerprinting, and the Math of Bit Mixing
Tags: #cryptography #algorithms #math #sec #infosec #til
What could be better on this scorching June Friday than a glass of cold Sec infused with a dash of Applied Cryptography?
1. Disclaimer
This article is written purely for educational, academic, and research purposes. It does not constitute a guide, technical tutorial, or inducement to bypass, circumvent, spoof, or forge any software security platforms, anti-fraud barriers, anti-cheat mechanisms, or browser fingerprinting technologies. The analysis provided below is intended solely to explore the underlying mathematical concepts of pseudo-random number generation, data isolation, and low-level bitwise manipulation within modern execution runtimes.
2. Introduction: An Investigative Glimpse into Advanced Fingerprint Spoofing
Out of purely academic and research curiosity—entirely separate from any practical application—I have recently been analyzing a sophisticated piece of JavaScript code designed as an anti-fingerprinting module. This script acts as an advanced spoofing layer injected directly at the earliest possible stage of execution via the Chrome DevTools Protocol (Page.addScriptToEvaluateOnNewDocument).
Its primary goal is to mask automation frameworks (like WebDriver, Puppeteer, or Selenium) and dynamically intercept high-signal browser APIs. By adding mathematically synthesized, deterministic noise on the fly, it targets fingerprinting vectors including Canvas, WebGL, AudioContext, Client-Hints, and the Navigator object, making automated scraper profiles indistinguishable from genuine user workstations to modern anti-bot setups.
3. Codebase Breakdown: Structural Pillars of Poltergeist FP
An analysis of the complete [avascript reveals a modular architecture. Each section isolates a specific high-signal vector or API leak used by modern anomaly detectors to flag automated browser sessions. Below is a structural outline tracking the code comments and milestones, highlighting the exact vectors of attack/spoofing and tools used:
- 0.0 - PRNG Functions (Seeded Random Number Generator): The computational foundation of the script. It couples
cyrb128(a 128-bit hash function) withsfc32(Small Fast Counter) to map any unique session seed string into a uniform, high-quality stream of deterministic pseudo-random numbers. - 1.0 - Initialize session-unique seed & 1.1 - RNG-Seeded Canvas Noise (deterministic per session): Combines standard client values (
navigator.hardwareConcurrency, screen dimensions, and a runtime millisecond stamp) into a unique session hash. Section 1.1 immediately deploys a JavaScriptProxyto wrap theHTMLCanvasElement.prototype.toDataURLfunction, adding micro-variations to canvas renders using a'multiply'blend mode. - 1.2 - Canvas getImageData Noise: Targets low-level pixel manipulation side-channels. It hooks
CanvasRenderingContext2D.prototype.getImageDatato loop over raw byte arrays and inject minor arithmetic deviations into color spaces ($R, G, B$), preventing hash matching on canvas elements. - 1.3 - WebGL Fingerprint Noise: Uses a dynamic
Proxyto interceptWebGLRenderingContext.prototype.getParametercalls. It substitutes core graphics identifiers—specifically spoofing raw unmasked GPU hardware strings likeUNMASKED_VENDOR_WEBGLandUNMASKED_RENDERER_WEBGLto simulate real commercial Nvidia devices. - 2.0 - Client-Hints Spoof (Chrome 110+): Intercepts advanced asynchronous user-agent validation headers. It overrides
navigator.userAgentData, replacing high-entropy dictionary fields (getHighEntropyValues) such as platform version, full brand lists, and specific sub-version tokens matching contemporary Chromium releases. - 3.0 - TLS/Crypto Proxy: Safeguards standard window object parameters against structural modifications. It wraps
window.cryptovia aProxyto ensure methods likegetRandomValuesandrandomUUIDroute correctly back to original bindings while maintaining expected prototypal encapsulation. - 4.0 - Remove WebDriver Detection: Strips primary automated testing traces. It resets
navigator.webdriverto clear its flag status and forces immediate deletions on common backdoor tokens left in global memory by specific automated control systems (cdc_adoQpoasnfa_...). - 5.0 - Mock Plugins & 5.1 - Mock MimeTypes: Re-architects missing hardware feature lists often skipped in standard headless runtimes. It maps hardcoded descriptive arrays containing mock values mimicking common system tools, such as native PDF wrappers and Chrome configurations.
- 6.0 - Mock Navigator Properties: Broadly hardcodes critical environmental and structural constraints on the
navigatorobject. It overwrites core values including locale arrays (languages), core execution architecture (platform),hardwareConcurrency, and systemdeviceMemoryconstants. - 7.0 - Mock Permissions API: Modifies response states of asynchronous platform queries. It mocks the resolution values of
navigator.permissions.query, explicitly intercepting requests for user notifications permissions to handle real-world prompt workflows smoothly. - 8.0 - Mock Chrome Runtime: Artificially constructs a standard runtime signature for native Chromium installations. It fakes internal asynchronous communication structures (
runtime.connect/sendMessage) and populatesloadTimesandcsimethods with synthetic metrics derived from the custom generator. - 9.0 - Mock Connection API: Overrides telemetry checks assessing mobile or cellular signaling metrics. It intercepts
navigator.connectionto return standardized values for round-trip times (rtt), downlink limits, and active structural parameters. - 10.0 - Mock Battery API: Hooks the asynchronous state pipeline monitoring hardware battery data (
navigator.getBattery). It wraps properties like charge presence, lifetime capacity, and current level to return plausible, randomized states. - 11.0 - Screen Properties: Intercepts low-level layout viewport data using a
Proxymapping straight over the active windowscreenobject. This guarantees total uniformity between internal geometric dimensions, window bounds, and outer monitor capabilities. - 12.0 - AudioContext Fingerprint Noise: Intercepts audio hardware pipeline analysis. It hooks
AudioContext.prototype.createOscillatorto inject micro-variations into standard sound engine oscillator values (frequency.value), scrambling acoustic signatures utilized by fraud detection algorithms.
4. The Birth of Custom PRNGs: Why Standard Tools Fail
Why do we even need to implement custom algorithms like Small Fast Counter (sfc32) or cyrb128 in JavaScript?
The answer is simple: reproducibility. JavaScript’s native Math.random() does not support seed values. You cannot tell it: “Give me the exact same sequence of ‘random’ numbers that you gave me five minutes ago.” For fingerprinting, cryptographic simulation, and game development, reproducibility is paramount.
Custom non-cryptographic PRNGs were born out of the necessity for raw speed, minimal memory overhead, and strict determinism. They trade cryptographic security (you shouldn’t use them to generate private keys) for blistering performance and excellent statistical properties.
5. The Anatomy of Bit Mixing and Magic Numbers
If you look under the hood of cyrb128, you will encounter constants like 1779033703 ($0x6A09E667$) or 597399067. These are not arbitrary choices; they are calculated to maximize the Strict Avalanche Criterion (SAC).
The avalanche effect dictates that if you change just a single bit in the input string, every single bit in the output hash should change with a 50% probability. To achieve this instantly, algorithms rely on bitwise shifts (>>>), XOR operations (^), and multiplication by these massive constants.
On a hardware level, multiplying a variable by a carefully designed constant forces the CPU to execute what is essentially a massive cascade of bit-shifting and overlapping additions in a single cycle. It forces the input bits to smear across the entire 32-bit register, completely erasing any predictable patterns or structures from the original input data.
6. The “Nothing Up My Sleeve” Principle
In computer science and cryptography, trust is everything. If the author of a hashing algorithm says, “Hey, just multiply your data by this random number I found: $0x7F4A2B99$,” security analysts get deeply suspicious. How do we know that specific number doesn’t contain a hidden backdoor or a mathematical resonance that causes predictable collisions?
To eliminate this suspicion, designers use “Nothing up my sleeve” numbers. These are constants derived from undeniable, universal mathematical constants like $\pi$, $e$, $\phi$ (the Golden Ratio), or the square roots of prime numbers.
For example, the constant 1779033703 ($0x6A09E667$) used in cyrb128 represents the first 32 bits of the fractional part of $\sqrt{2}$. The SHA-256 algorithm relies entirely on the fractional parts of the square and cube roots of the first prime numbers for its initialization constants. Because these numbers are irrational, their binary expansions behave like perfect “white noise” with an even 50/50 distribution of ones and zeros, proving that the creator didn’t engineer a hidden trapdoor.
7. Language Nuances: Why Python Demands 0xFFFFFFFF
When porting high-speed JavaScript PRNG implementations into Python, developers often encounter unexpected breaking behavior. This boils down to how both languages handle integers:
JavaScript treats numbers as 64-bit floats under the hood, but whenever you perform a bitwise operation (like ^ or <<), it forces the operands into 32-bit fixed-width integers. Any overflow beyond 32 bits is instantly and silently discarded at the CPU level.
Python, on the other hand, boasts arbitrary-precision integers (BigInt). Python integers will automatically expand in memory to accommodate massive values. A bitwise shift left (<<) or a large multiplication will grow indefinitely instead of overflowing.
To replicate JS behavior in Python, you must manually truncate the integers back down to 32 bits after every arithmetic operation using the bitwise mask. Without & 0xFFFFFFFF, the internal states of your variables will balloon in size, destroying the bit-shifting logic and altering the final sequence entirely.
8. Native Support: Where Fast PRNGs Live Professionally
If you are looking for ecosystems where these types of ultra-fast bit-mixing algorithms are first-class citizens, look into lower-level languages:
- Zig: The standard library (
std.rand) includes native, ultra-fast implementations ofSfc64(the 64-bit older brother ofsfc32) andXoshiro256. It features wrapping operators (+%,*%) that handle 32/64-bit overflow natively at the hardware level. - C++: Ever since C++11, the
<random>header provides access to highly optimized, natively compiled non-cryptographic engines (like Mersenne Twister) where unsigned integer types safely overflow by default, requiring no artificial masking. - Julia / Rust / Go: These languages provide built-in or standard ecosystem-backed frameworks (like Julia’s
RandomusingXoshiro256++, Go’smath/rand/v2PCG/ChaCha8 implementations , and Rust’srandcrate ) that run bare-metal bitwise instructions, mapping variables directly to CPU registers for maximum performance.
9. JS-HASH: Hash Functions and Seed Generation
Source: js-hash reference repository with many PRNG algorithms implementations
Pseudo-Random Number Generators (PRNGs) & Families
- Alea — An MWC-based generator featuring its own Mash string-hashing function.
- LCG (Linear Congruential Generator) — A classic family, including Lehmer RNG and Park–Miller constants.
- MWC (Multiply-with-Carry) — George Marsaglia’s engine family. Includes:
- mwc / MWC1616 — A KISS99 variant (used in Google Chrome until 2015).
- The Mother of All RNGs — An early 16-bit MWC variation.
- Xorshift — Fast shift/XOR-based engines (Marsaglia, 2003). Variants: xorshift32 (32-bit state), xorshift128 (128-bit state), xorwow (with a Weyl sequence counter), xorshift64* (LCG-multiplied), xorshift32m (32-bit adaptation of 64*), and xorshift32amx (byte-swapped via __builtin_bswap32).
- Xoroshiro — Shift/rotate evolution of Xorshift. Mentions xoroshiro64, xoroshiro64*, xoroshiro64+ (experimental), xoroshiro128+ (original & 32-bit experimental setup), and xorshift128+ (32-bit)—the V8 engine replacement for MWC in 2015.
- Xoshiro — Modern family (2018) split across 32-bit registers for a 128-bit state: xoshiro128, xoshiro128++, and xoshiro128+.
- JSF / smallprng — Bob Jenkins’ chaotic generator (variants: jsf32 and the 3-rotate jsf32b).
- gjrand32 — A 32-bit non-linear chaotic engine adaptation from David Blackman’s GJrand suite.
- sfc32 (Small Fast Counter) — A chaotic counter-based generator passing PracRand and TestU01.
- Tyche — Engines built on ChaCha cipher quarter-rounds (tyche and the 20% faster tychei).
- Mulberry32 — A minimalistic 32-bit state PRNG originally designed for embedded systems.
- SplitMix32 — A PRNG derived from MurmurHash3’s fmix32 finalizer. Includes a standard 32-bit JDK8 port and an improved 2018 variant with optimized constants.
- v3b — An unorthodox chaotic generator utilizing a variable-output counter.
Seeding & Mixing Functions
Addendum A tools designed to initialize uniform, high-quality PRNG states from arbitrary input strings:
- xmur3 — A lightweight seeding function using the MurmurHash3 mixer pipeline.
- xmur3a — A full 32-bit JavaScript implementation of MurmurHash3.
- xfnv1a — A function based on Bret Mulvey’s modified FNV-1a (Fowler–Noll–Vo) hash.
- initseed — Two experimental versions: an early attempt to optimize xfnv1a, and a 128-bit state initializer mixing MurmurHash3 and xxHash mechanics.
10.Summary
In the high-stakes game of advanced browser spoofing, generating believable noise requires stepping away from traditional randomness and embracing strict mathematical determinism. Implementing lightweight, non-cryptographic hash functions like cyrb128 (heavily derived from the concepts of MurmurHash ) alongside highly efficient pseudo-random engines like sfc32 ensures that an injected profile remains stable, reproducible, and completely transparent to modern anti-fraud systems. By building state mixers around universal mathematical constants, developers don’t just achieve an exceptional cascade of bit mixing—they also eliminate any suspicion of engineered backdoors by satisfying the rigorous Nothing-up-my-sleeve number criterion. Whether you are scaling automated workflows or porting low-level algorithms from JavaScript to Python, unlocking the hidden power of these magic numbers is the ultimate key to mastering execution runtimes and data isolation.
Further Reading
If you want to dig deeper into the math and mechanics behind these concepts, explore these topics:
js-hash reference repository with many PRNG algorithms implementations
Browser Fingerprinting Defenses & Canvas Noise — How modern anti-bot systems detect automation and how noise injection counters it.
Strict Avalanche Criterion (SAC) in Bit Mixing — The core mathematical concept behind bit smearing and diffusion.
Nothing-up-my-sleeve Numbers — Examples and history of trust-building constants derived from $\pi$, $e$, and prime roots.
Arbitrary-precision Integer Overflows: JS vs Python — Understanding execution runtime differences when porting low-level performance-critical code.
