Randomness Under Different Constraints: A ChaCha20-Based PRNG in Go

#computing #distributed systems #software #cryptography

“A system is not determined by the algorithms it uses, but by the assumptions it makes.”
— Leslie Lamport

Randomness appears deceptively uniform in most software systems. A call is made, bytes are returned, and execution proceeds. The details of how those bytes were produced rarely matter—until they do. As with many foundational components, the assumptions baked into a randomness source tend to surface only when systems scale, diversify, or are subjected to constraints they were not originally designed to accommodate.

In the previous article, I explored deterministic cryptographic randomness under compliance and auditability constraints, where explicit state evolution and reproducibility were not optional. That work followed inevitably from environments shaped by regulation and forensic accountability. This article explores a different design space: one where the constraints are imposed not by certification regimes, but by software portability, concurrency, and operational simplicity.

The result is a ChaCha20-based pseudorandom number generator (PRNG) designed for predictable behavior in real systems, rather than alignment with formal validation programs.

What distinguishes this space from compliance-driven designs is not weaker security assumptions, but different priorities. Software systems are often judged not by formal proofs or certifications, but by how they behave under sustained load, across diverse platforms, and in the presence of concurrency. In those environments, simplicity is not aesthetic—it is operational. Every implicit dependency, hidden allocation, or global coordination point becomes a liability over time.

Constraints Shape Constructions

Cryptographic primitives are often discussed as interchangeable building blocks. In practice, the surrounding constraints determine which properties matter and which can be safely ignored. A construction optimized for auditability and deterministic replay will look very different from one optimized for simplicity, portability, and high-throughput concurrent use.

In software-heavy environments—particularly those spanning multiple platforms or deployed as libraries rather than systems—certain requirements surface repeatedly. The randomness source must behave predictably under concurrency. It must avoid surprising allocation behavior. It must scale without coordination bottlenecks. And it must do so without pulling in complex dependencies or imposing policy decisions on its callers.

These are not compliance constraints. They are engineering constraints.

Engineering constraints tend to accumulate quietly. A library that is easy to use in isolation may behave very differently when embedded in larger systems, reused across services, or exercised under high concurrency. Over time, small assumptions compound: about allocation behavior, about shared state, about initialization cost. What begins as a convenient abstraction can become a systemic bottleneck. Designs that survive this environment are rarely the most general ones; they are the ones that make their tradeoffs explicit early.

ChaCha20 fits naturally into this space. As a stream cipher designed for efficient software implementation, it offers strong cryptographic properties without reliance on specialized hardware instructions or platform-specific optimizations. That makes it a practical foundation for a general-purpose PRNG intended to behave consistently across environments.

State, Not Entropy, Does the Work

As with any PRNG, the core of the design is not entropy acquisition but state evolution. Entropy enters the system at initialization. From that point forward, output is a function of internal state advancing according to a defined algorithm.

This distinction matters. By treating entropy acquisition as a boundary rather than a continuous process, the generator remains honest about its dependencies. The operating system provides unpredictability at startup. The PRNG provides expansion, isolation, and repeatable behavior thereafter.

ChaCha20’s design reinforces this model. Once keyed and initialized, the cipher produces a stream of pseudorandom bytes derived entirely from its internal state. There are no conditional branches based on environmental input, no opportunistic refreshes, and no hidden coordination with external sources. State advances monotonically. Output follows deterministically.

Thinking in terms of state rather than entropy also clarifies responsibility. Once initialization completes, the generator no longer depends on the operating system or the environment for correctness. The burden shifts entirely to the implementation and its callers. That boundary is important: it allows failures, assumptions, and limitations to be localized rather than diffused across layers. When something goes wrong, there is no ambiguity about where to look.

This makes the generator easier to reason about, test, and integrate. The absence of implicit behavior is not a limitation; it is a deliberate choice that keeps responsibility visible.

Concurrency Without Contention

Concurrency tends to expose the weaknesses of otherwise elegant designs. Shared mutable state invites contention. Global locks introduce unpredictable latency. Per-call initialization imposes overhead that scales poorly as systems grow.

The ChaCha20 PRNG approaches concurrency as a distribution problem rather than a synchronization problem. Independent generator instances maintain independent state. A pooling strategy amortizes initialization costs while avoiding shared counters or global coordination. Each request draws from a generator whose behavior is local, predictable, and isolated from others.

This approach mirrors patterns commonly used in distributed systems: partition state, avoid global serialization, and accept modest duplication in exchange for clarity and scalability.

In practice, this means resisting the temptation to centralize randomness behind a single shared construct. While such designs appear simpler at first, they introduce hidden coupling that surfaces under load. Contention becomes observable latency. Synchronization becomes an availability concern. By allowing each generator instance to evolve independently, the system preserves the same semantic behavior regardless of how many consumers are active.

Runtime Semantics and Predictable Behavior

Allocation behavior is treated as a semantic concern rather than a micro-optimization. A PRNG that allocates unpredictably during output generation cedes part of its execution model to the runtime.

The effects of these choices are observable in practice. Small reads primarily reflect fixed overhead. Larger reads scale linearly with the amount of data processed, as expected from a stream cipher advancing its internal counter. Under concurrent workloads, latency remains stable because requests are serviced independently rather than serialized behind shared state.

What matters is not the absolute numbers, but their predictability. The behavior matches the design model, rather than fluctuating with runtime conditions.

This predictability has secondary effects that are easy to underestimate. Monitoring becomes more meaningful when latency distributions are stable. Capacity planning becomes tractable when throughput degrades linearly rather than catastrophically. Perhaps most importantly, anomalies become visible. When behavior deviates from expectation, it signals a real change in conditions rather than noise introduced by the runtime.

Failure Modes and Explicit Boundaries

Boundaries are explicit. Initialization depends on a trusted entropy source. Once initialized, the generator’s behavior is deterministic and bounded by construction. Errors are surfaced rather than hidden.

Surfacing errors is not merely a defensive choice; it is an architectural one. Silent degradation may preserve uptime in the short term, but it undermines trust in the system over time. By making failure explicit, the generator avoids the pretense of correctness under conditions it was not designed to handle. This makes integration more demanding, but also more honest.

This approach does not attempt to cover every possible threat model. It does not claim resistance to state compromise beyond what the underlying primitive provides. It does not attempt to retrofit compliance semantics onto a software-oriented design.

Closing Thoughts

This ChaCha20-based PRNG is used as the randomness source in other systems, including my NanoID implementation, where predictable behavior, low overhead, and concurrency safety are practical requirements rather than abstract goals.

Randomness is not a single problem. Different environments impose different constraints, and those constraints shape the constructions that make sense within them.

This ChaCha20-based PRNG exists because certain systems demand a randomness source that behaves predictably under concurrency, avoids unnecessary complexity, and makes its assumptions explicit. It treats randomness as evolving state rather than as an opaque service call.

The tension between generality and specificity is unavoidable in systems design. Libraries that attempt to serve every use case often end up serving none particularly well. By contrast, designs that accept their constraints openly tend to age more gracefully. They are easier to reason about, easier to integrate, and harder to misuse.