Awesome Go

aes-ctr-drbg

CategorySecurity
SubcategorySecurity
Stars9

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: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](LICENSE)
[![Go](https://img.shields.io/github/go-mod/go-version/sixafter/aes-ctr-drbg)](https://img.shields.io/github/go-mod/go-version/sixafter/aes-ctr-drbg)
[![Go Reference](https://pkg.go.dev/badge/github.com/sixafter/aes-ctr-drbg.svg)](https://pkg.go.dev/github.com/sixafter/aes-ctr-drbg)
[![Context7](https://img.shields.io/badge/Context7-docs-blue?style=flat-square)](https://context7.com/sixafter/aes-ctr-drbg)
[![FIPS‑140 Mode Compatible](https://img.shields.io/badge/FIPS‑140--Mode-Compatible-brightgreen)](FIPS-140.md)

---

## Status

### Build & Test

[![CI](https://github.com/sixafter/aes-ctr-drbg/workflows/ci/badge.svg)](https://github.com/sixafter/aes-ctr-drbg/actions)
[![GitHub issues](https://img.shields.io/github/issues/sixafter/aes-ctr-drbg)](https://github.com/sixafter/aes-ctr-drbg/issues)

### Quality

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=six-after_aes-ctr-drbg&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=six-after_aes-ctr-drbg)
![CodeQL](https://github.com/sixafter/aes-ctr-drbg/actions/workflows/codeql-analysis.yaml/badge.svg)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=six-after_aes-ctr-drbg&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=six-after_aes-ctr-drbg)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/11487/badge)](https://www.bestpractices.dev/projects/11487)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/sixafter/aes-ctr-drbg/badge)](https://scorecard.dev/viewer/?uri=github.com/sixafter/aes-ctr-drbg)

### Package and Deploy

[![Release](https://github.com/sixafter/aes-ctr-drbg/workflows/release/badge.svg)](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/op

Frequently 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.

← Back to Security