aes-ctr-drbg
A Deterministic Random Bit Generator based on AES in Counter mode (AES-CTR-DRBG) as specified in NIST SP 800-90A
About aes-ctr-drbg
This large README is shown as a plain-text preview. Read the full README at the source
# AES-CTR-DRBG
[](LICENSE)
[](https://img.shields.io/github/go-mod/go-version/sixafter/aes-ctr-drbg)
[](https://pkg.go.dev/github.com/sixafter/aes-ctr-drbg)
[](https://context7.com/sixafter/aes-ctr-drbg)
[](FIPS-140.md)
---
## Status
### Build & Test
[](https://github.com/sixafter/aes-ctr-drbg/actions)
[](https://github.com/sixafter/aes-ctr-drbg/issues)
### Quality
[](https://sonarcloud.io/summary/new_code?id=six-after_aes-ctr-drbg)

[](https://sonarcloud.io/summary/new_code?id=six-after_aes-ctr-drbg)
[](https://www.bestpractices.dev/projects/11487)
[](https://scorecard.dev/viewer/?uri=github.com/sixafter/aes-ctr-drbg)
### Package and Deploy
[](https://github.com/sixafter/aes-ctr-drbg/actions)
---
## Overview
This package provides an `io.Reader` that generates random bytes with AES in counter mode, based on the CTR_DRBG mechanism of [NIST SP 800-90A Rev. 1](https://csrc.nist.gov/pubs/sp/800/90/a/r1/final) (§10.2.1, no derivation function). Conformance with that mechanism is opt-in through `WithBacktrackingResistance`; see [NIST-SP-800-90A.md](docs/NIST-SP-800-90A.md).
It uses only Go standard library cryptography (`crypto/aes`, `crypto/cipher`, `crypto/rand`) and works with Go's FIPS 140 mode (`GODEBUG=fips140=on`). It is not FIPS 140 validated; see [FIPS-140.md](FIPS-140.md).
Please see the [godoc](https://pkg.go.dev/github.com/sixafter/aes-ctr-drbg) for detailed documentation.
The documentation is also indexed on [Context7](https://context7.com/sixafter/aes-ctr-drbg) for use with AI coding assistants. Use the library ID `/sixafter/aes-ctr-drbg` with the Context7 MCP server or any Context7-aware tool.
---
## FIPS‑140 Mode
See [FIPS‑140.md](FIPS-140.md) for compliance, deployment, and configuration guidance.
---
## Features
* **NIST SP 800-90A CTR_DRBG:**
Based on the NIST SP 800-90A Rev. 1 CTR_DRBG mechanism (§10.2.1, AES, no derivation function), using `crypto/aes` and `crypto/cipher`, with 128-, 192-, and 256-bit keys. With `WithBacktrackingResistance`, instantiation, reseeding, generation, additional input, prediction resistance, and the request and reseed limits follow §10.2.1 and §9.3.1, known-answer self-tests run before first use (§11.3), and they are tested against NIST CAVP CTR_DRBG known-answer vectors (`2 allocs/op`). The default mode does not update the key and counter after each request (`0 allocs/op`).
* **Go FIPS 140 Mode:**
Works with Go's FIPS 140 mode (`GODEBUG=fips140=on`). Optional FIPS 140-2 features: AES known-answer test (`WithSelfTests`), continuous random number generator test (`WithContinuousHealthTest`), and key zeroization during rotation (`WithZeroization`). See [FIPS-140.md](FIPS-140.md).
* **Zero-Allocation Output Path:**
`Read` performs 0 allocations per call in the default mode, and 2 with `WithBacktrackingResistance`.
* **Asynchronous Key Rotation:**
Supports automatic key rotation after a configurable number of bytes have been generated (`MaxBytesPerKey`). Rekeying occurs asynchronously with exponential backoff and configurable retry limits, reducing long-term key exposure.
* **Prediction Resistance Mode:**
When enabled, the DRBG reseeds from system entropy before every output request. With `WithBacktrackingResistance`, that reseed follows NIST SP 800-90A §9.3.1 and §10.2.1.4.1 and mixes in the request's additional input.
* **Sharded Pooling for Concurrency:**
Instances are pooled across a configurable number of `sync.Pool` shards (`WithShards`, default `runtime.GOMAXPROCS(0)`); each `Read` uses one shard chosen at random.
* **Functional Options:**
Configuration is set with functional options, including:
* AES key size (128/192/256-bit)
* NIST SP 800-90A conformance (`WithBacktrackingResistance`)
* Maximum output per key (rekey threshold)
* Personalization string (domain separation)
* Shard/pool count
* Reseed interval and request count
* Buffer size controls
* Key rotation and rekey backoff parameters
* Prediction resistance
* Fork detection and reseeding
* Continuous health test, self-tests, and key zeroization
* **Concurrency and Determinism:**
`Read`, `ReadWithAdditionalInput`, and `Reseed` are safe for concurrent use; each pooled instance is used by one goroutine at a time. Output is fully determined by the entropy read from `crypto/rand.Reader`, the personalization string, and any additional input; it is reproducible only if `crypto/rand.Reader` is replaced with known input.
* **io.Reader Compatibility:**
Implements `io.Reader`, so it can be used wherever an `io.Reader` source of random bytes is accepted.
* **No External Dependencies:**
Depends exclusively on the Go standard library for cryptographic operations.
* **UUID Generation:**
Can be used as a cryptographically secure `io.Reader` with the [`google/uuid`](https://pkg.go.dev/github.com/google/uuid) package and similar libraries.
* **Testing:**
Unit, fuzz, concurrency (`-race`), allocation, and NIST CAVP known-answer tests.
* **Fork-Safety:**
The process ID is checked before each request, or every `ForkDetectionInterval` requests; after a fork, the child reseeds before producing output.
For an example of how this library can be consumed in practice, see [sixafter/nanoid](https://github.com/sixafter/nanoid).
NanoID uses [sixafter/prng-chacha](https://github.com/sixafter/prng-chacha) as its default high-performance PRNG, but can also use **`aes-ctr-drbg`** when
FIPS 140-2/3 alignment or deterministic AES-CTR-based randomness is required. This provides a clear, real-world example
of integrating this DRBG into an ID-generation workflow, including optional FIPS-centric operation. There is also a
[WithAutoRandReader()](https://github.com/sixafter/nanoid/blob/d8efbc63e5a5696a33f34b9fb5d24f5d7805a7ed/config.go#L229) option that automatically selects between `prng-chacha` and `aes-ctr-drbg` based on the
runtime FIPS mode.
## NIST SP 800-90A Specification Mapping
For a detailed mapping between the implementation and NIST SP 800-90A requirements, see [NIST-SP-800-90A.md](docs/NIST-SP-800-90A.md).
---
## Verify with Cosign
[Cosign](https://github.com/sigstore/cosign) is used to sign releases for integrity verification.
To verify the integrity of the release tarball, you can use Cosign to check the signature and checksums. Follow these steps:
```sh
# Fetch the latest release tag from GitHub API (e.g., "v1.22.0")
TAG=$(curl -s https://api.github.com/repos/sixafter/aes-ctr-drbg/releases/latest | jq -r .tag_name)
# Remove leading "v" for filenames (e.g., "v1.22.0" -> "1.22.0")
VERSION=${TAG#v}
# ---------------------------------------------------------------------
# Verify the source archive using Sigstore bundles
# ---------------------------------------------------------------------
# Download the release tarball and its signature bundle
curl -LO "https://github.com/sixafter/aes-ctr-drbg/releases/download/${TAG}/aes-ctr-drbg-${VERSION}.tar.gz"
curl -LO "https://github.com/sixafter/aes-ctr-drbg/releases/download/${TAG}/aes-ctr-drbg-${VERSION}.tar.gz.sigstore.json"
# Verify the tarball with Cosign using the published public key
cosign verify-blob \
--key "https://raw.githubusercontent.com/sixafter/aes-ctr-drbg/main/cosign.pub" \
--bundle "aes-ctr-drbg-${VERSION}.tar.gz.sigstore.json" \
"aes-ctr-drbg-${VERSION}.tar.gz"
# ---------------------------------------------------------------------
# Verify the checksums manifest using Sigstore bundles
# ---------------------------------------------------------------------
curl -LO "https://github.com/sixafter/aes-ctr-drbg/releases/download/${TAG}/checksums.txt"
curl -LO "https://github.com/sixafter/aes-ctr-drbg/releases/download/${TAG}/checksums.txt.sigstore.json"
cosign verify-blob \
--key "https://raw.githubusercontent.com/sixafter/aes-ctr-drbg/main/cosign.pub" \
--bundle "checksums.txt.sigstore.json" \
"checksums.txt"
# ---------------------------------------------------------------------
# Confirm local artifact integrity
# ---------------------------------------------------------------------
shasum -a 256 -c checksums.txt
```
If valid, Cosign will output:
```shell
Verified OK
```
---
## Verify Go module
To validate that the Go module archive served by GitHub, go mod download, and the Go
proxy are all consistent, run the `module-verify` target. This performs a full cross-check
of the tag archive and module ZIPs to confirm they match byte-for-byte.
---
## Installation
```bash
go get -u github.com/sixafter/aes-ctr-drbg
```
---
## Usage
### Basic Usage: Generate Secure Random Bytes With Reader
```go
package main
import (
"fmt"
"log"
"github.com/sixafter/aes-ctr-drbg"
)
func main() {
buf := make([]byte, 64)
n, err := ctrdrbg.Reader.Read(buf)
if err != nil {
log.Fatalf("failed to read random bytes: %v", err)
}
fmt.Printf("Read %d random bytes: %x\n", n, buf)
}
```
### Basic Usage: Generate Secure Random Bytes with NewReader
```go
package main
import (
"fmt"
"log"
"github.com/sixafter/aes-ctr-drbg"
)
func main() {
// Example: AES-256 (32 bytes) key
r, err := ctrdrbg.NewReader(ctrdrbg.WithKeySize(ctrdrbg.KeySize256))
if err != nil {
log.Fatalf("failed to create ctrdrbg.Reader: %v", err)
}
buf := make([]byte, 64)
n, err := r.Read(buf)
if err != nil {
log.Fatalf("failed to read random bytes: %v", err)
}
fmt.Printf("Read %d random bytes: %x\n", n, buf)
}
```
### Using Personalization and Additional Input
```go
package main
import (
"fmt"
"log"
"github.com/sixafter/aes-ctr-drbg"
)
func main() {
r, err := ctrdrbg.NewReader(
ctrdrbg.WithPersonalization([]byte("service-id-1")),
ctrdrbg.WithKeySize(ctrdrbg.KeySize256), // AES-256
)
if err != nil {
log.Fatalf("failed to create ctrdrbg.Reader: %v", err)
}
buf := make([]byte, 64)
n, err := r.Read(buf)
if err != nil {
log.Fatalf("failed to read random bytes: %v", err)
}
fmt.Printf("Read %d random bytes: %x\n", n, buf)
}
```
---
## Performance Benchmarks
### Raw Random Byte Generation
These `ctrdrbg.Reader` benchmarks demonstrate the package's focus on minimizing latency, memory usage, and allocation overhead, making it suitable for high-performance applications.
<details>
<summary>Expand to see results</summary>
```shell
make bench
go test -bench='^BenchmarkDRBG_' -run=^$ -benchmem -memprofile=mem.out -cpuprofile=cpu.out .
goos: darwin
goarch: arm64
pkg: github.com/sixafter/aes-ctr-drbg
cpu: Apple M4 Max
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G2-16 1000000000 0.6112 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G4-16 1000000000 0.6134 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G8-16 1000000000 0.5971 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G16-16 1000000000 0.5937 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G32-16 1000000000 0.5700 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G64-16 1000000000 0.5587 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_SyncPool_Baseline_Concurrent/G128-16 1000000000 0.5644 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_16Bytes-16 34062441 34.18 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_32Bytes-16 29895489 39.54 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_64Bytes-16 24042694 50.42 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_256Bytes-16 10366684 115.3 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_512Bytes-16 5998489 200.1 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_4096Bytes-16 871158 1377 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Serial/Serial_Read_16384Bytes-16 222878 5376 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_2Goroutines-16 19962072 85.99 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_4Goroutines-16 19596702 84.39 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_8Goroutines-16 19851349 80.85 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_16Goroutines-16 19654436 64.24 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_32Goroutines-16 19521876 81.78 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_64Goroutines-16 19818331 82.29 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16Bytes_128Goroutines-16 20155250 82.61 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_2Goroutines-16 18829485 88.29 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_4Goroutines-16 19666596 88.22 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_8Goroutines-16 19722001 83.93 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_16Goroutines-16 19440373 85.58 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_32Goroutines-16 19804376 84.60 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_64Goroutines-16 19879699 84.29 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_32Bytes_128Goroutines-16 19940580 82.55 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_2Goroutines-16 18446626 80.89 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_4Goroutines-16 19594609 84.27 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_8Goroutines-16 19475618 84.79 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_16Goroutines-16 19886919 85.68 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_32Goroutines-16 19558533 81.82 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_64Goroutines-16 19842116 82.01 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_64Bytes_128Goroutines-16 20000374 83.86 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_2Goroutines-16 10465203 161.9 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_4Goroutines-16 10905505 158.3 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_8Goroutines-16 10895772 154.3 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_16Goroutines-16 10901005 156.7 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_32Goroutines-16 10952331 151.1 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_64Goroutines-16 11080442 156.5 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_256Bytes_128Goroutines-16 11141888 156.4 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_2Goroutines-16 10665208 160.2 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_4Goroutines-16 10822701 161.1 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_8Goroutines-16 10816222 155.9 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_16Goroutines-16 11038309 148.1 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_32Goroutines-16 10907218 156.2 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_64Goroutines-16 11146635 156.9 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_512Bytes_128Goroutines-16 11120167 154.9 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_2Goroutines-16 7884975 205.0 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_4Goroutines-16 7952276 203.5 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_8Goroutines-16 6754220 200.4 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_16Goroutines-16 6395343 191.9 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_32Goroutines-16 6827542 203.3 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_64Goroutines-16 7638505 197.6 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_4096Bytes_128Goroutines-16 7684468 187.9 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16384Bytes_2Goroutines-16 1792022 730.2 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16384Bytes_4Goroutines-16 1782008 752.2 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16384Bytes_8Goroutines-16 1811017 811.3 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16384Bytes_16Goroutines-16 1553781 773.5 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16384Bytes_32Goroutines-16 1644704 775.5 ns/op 0 B/op 0 allocs/op
BenchmarkDRBG_Read_Concurrent/Concurrent_Read_16384Bytes_64Goroutines-16 1553227 758.5 ns/op 0 B/op 0 allocs/opFrequently Asked Questions
What is aes-ctr-drbg?
aes-ctr-drbg is a Security library for the Go programming language. A Deterministic Random Bit Generator based on AES in Counter mode (AES-CTR-DRBG) as specified in NIST SP 800-90A
How do I install aes-ctr-drbg?
Install aes-ctr-drbg with the Go module system using `go get sixafter/aes-ctr-drbg`. Check the repository for the current installation instructions.
What category does aes-ctr-drbg belong to?
aes-ctr-drbg is listed under Security, specifically Security.