A high-performance gRPC service implementing the NIST SP 800-22 statistical test suite for random number generator validation. This service provides a modern API for executing all 15 NIST tests specified in the Special Publication 800-22 revision 1a.
The implementation maintains 95.4% test coverage with comprehensive benchmarking and observability features including request tracking and performance profiling.
The Pure Go implementation has been validated against the original NIST C reference implementation across multiple datasets:
| Dataset | Status | Tests Passed | P-Value Match |
|---|---|---|---|
| data.pi | Validated | 15/15 | ±0.0001 |
| data.e | Validated | 15/15 | ±0.0001 |
| data.sqrt2 | Validated | 15/15 | ±0.0001 |
| data.bad_rng | Validated | 15/15 | ±0.0001 |
All tests produce numerically identical P-values to the NIST reference implementation within floating-point epsilon tolerance.
# Start the service with monitoring stack
docker-compose up -d
# The service is now available on:
# - gRPC: localhost:9090
# - Metrics: localhost:9091
# - Prometheus: localhost:9092
# - Grafana: localhost:3000# Install dependencies
make deps
# Build the service
make build
# Run locally
make runThe service will start on port 9090 (gRPC) and 9091 (metrics).
nist-800-22-test-suite/
├── api/nist/v1/ # Protobuf API definitions
├── cmd/server/ # Service entry point
├── internal/
│ ├── config/ # Configuration management
│ ├── metrics/ # Prometheus metrics
│ ├── middleware/ # Request interceptors (Request-ID, logging)
│ ├── nist/ # Pure Go test implementations
│ └── service/ # gRPC service handlers
├── pkg/pb/ # Generated protobuf code
└── testdata/ # NIST test datasets
NIST Tests Implementation (internal/nist/)
All 15 NIST SP 800-22 tests are implemented in pure Go:
- Frequency (Monobit) Test
- Block Frequency Test
- Cumulative Sums Test
- Runs Test
- Longest Run of Ones Test
- Binary Matrix Rank Test
- Discrete Fourier Transform Test
- Non-Overlapping Template Matching Test
- Overlapping Template Matching Test
- Universal Statistical Test
- Approximate Entropy Test
- Random Excursions Test
- Random Excursions Variant Test
- Serial Test
- Linear Complexity Test
Service Layer (internal/service/)
gRPC service implementation with:
- Request validation (bit count requirements)
- Parallel test execution
- Metrics collection (Prometheus)
- Request-ID tracking for distributed tracing
- Structured logging with zerolog
- Error handling
Middleware (internal/middleware/)
gRPC interceptors for observability:
- Request-ID generation (UUID-based)
- Automatic request/response logging with duration tracking
- Metadata injection for client-side tracing
Configuration (internal/config/)
Environment-based configuration:
GRPC_PORT- gRPC service port (default: 9090)METRICS_PORT- Prometheus metrics and pprof profiling port (default: 9091)LOG_LEVEL- Logging verbosity (debug, info, warn, error)AUTH_ENABLED- Enable OAuth2/OIDC token validation for gRPC calls (default: false)AUTH_ISSUER- Expected token issuer (required when auth is enabled)AUTH_AUDIENCE- Expected token audience (required when auth is enabled)AUTH_TOKEN_TYPE- Token mode:jwt(default) oropaqueAUTH_JWKS_URL- Optional custom JWKS endpoint (JWT mode; defaults to issuer well-known URL)AUTH_INTROSPECTION_URL- OAuth2 introspection endpoint (required in opaque mode)AUTH_INTROSPECTION_AUTH_METHOD- Introspection client auth (client_secret_basicdefault orprivate_key_jwt)AUTH_INTROSPECTION_CLIENT_ID/AUTH_INTROSPECTION_CLIENT_SECRET- Introspection client credentials (required forclient_secret_basic)AUTH_INTROSPECTION_PRIVATE_KEY- Private key content forprivate_key_jwt(PEM, JWK JSON, or Zitadel key JSON)AUTH_INTROSPECTION_PRIVATE_KEY_FILE- Alternative file path forAUTH_INTROSPECTION_PRIVATE_KEY(mutually exclusive)AUTH_INTROSPECTION_PRIVATE_KEY_JWT_KID- Optional JWT headerkidoverride forprivate_key_jwtAUTH_INTROSPECTION_PRIVATE_KEY_JWT_ALG- Optional signing alg override (RS256orES256)AUTHZ_REQUIRED_ROLES/AUTHZ_REQUIRED_SCOPES- Optional required roles/scopes (comma-separated); enables authorization checks when setAUTHZ_ROLE_MATCH_MODE/AUTHZ_SCOPE_MATCH_MODE- Matching mode for required roles/scopes (anyorall; default:any)AUTHZ_ROLE_CLAIM_PATHS/AUTHZ_SCOPE_CLAIM_PATHS- Optional claim paths (comma-separated, dot-notation supported) used for role/scope extractionTLS_ENABLED- Enable TLS for the gRPC server (default: false)TLS_CERT_FILE/TLS_KEY_FILE- Server certificate and key (required when TLS is enabled)TLS_CA_FILE- Optional CA bundle for client cert verification (mTLS)TLS_CLIENT_AUTH- Client auth mode (none,request,requireany,verifyifgiven,requireandverify; default:none)TLS_MIN_VERSION- Minimum TLS version (1.2or1.3; default:1.2)
ZITADEL private_key_jwt examples:
# PEM (inline or from file)
AUTH_TOKEN_TYPE=opaque
AUTH_INTROSPECTION_URL=https://<zitadel-domain>/oauth/v2/introspect
AUTH_INTROSPECTION_AUTH_METHOD=private_key_jwt
AUTH_INTROSPECTION_CLIENT_ID=<client-id>
AUTH_INTROSPECTION_PRIVATE_KEY_FILE=/run/secrets/zitadel-private-key.pem
AUTH_INTROSPECTION_PRIVATE_KEY_JWT_ALG=RS256# Zitadel key JSON envelope (contains keyId/key/clientId)
AUTH_TOKEN_TYPE=opaque
AUTH_INTROSPECTION_URL=https://<zitadel-domain>/oauth/v2/introspect
AUTH_INTROSPECTION_AUTH_METHOD=private_key_jwt
AUTH_INTROSPECTION_PRIVATE_KEY='{"keyId":"...","key":"-----BEGIN PRIVATE KEY-----\n...","clientId":"..."}'To add custom test implementations:
- Implement test function in
internal/nist/:
func CustomTest(bits []byte) (pValue float64, passed bool) {
// Your test logic
return pValue, pValue >= 0.01
}- Register in
internal/nist/run_all.go:
results = append(results, TestResult{
Name: "custom_test",
PValue: pValue,
Passed: passed,
})- Update protobuf if needed and regenerate:
make proto
# Run all tests
make test
# Run with coverage report
make coverage
# Run with race detector
make test-raceThe project maintains comprehensive test coverage with a 90% minimum threshold enforced in CI.
Current coverage: 95.4%
| Package | Coverage |
|---|---|
| internal/config | 100.0% |
| internal/metrics | 100.0% |
| internal/middleware | 100.0% |
| internal/nist | 95.3% |
| internal/service | 97.6% |
| cmd/server | 90.6% |
Generate detailed HTML coverage report:
make cover-htmlValidation tests compare the Pure Go implementation against the original NIST C reference:
# Run validation tests
go test -v ./internal/nist/
# Expected output:
# === RUN TestMatchesSTSReferenceOnSamples
# --- PASS: TestMatchesSTSReferenceOnSamples (2.34s)The project includes comprehensive benchmarks for all 15 NIST tests. Run benchmarks using:
# Quick benchmark run
make bench
# Run with 10 iterations for statistical significance
make bench-all
# Capture baseline for regression testing
make bench-baseline
# Compare current performance with baseline
make bench-compareFor statistical comparison, install benchstat:
go install golang.org/x/perf/cmd/benchstat@latestTypical performance on 1,000,000 bits (125KB) measured on AMD Ryzen 7 PRO 7840U:
| Test | Time per Operation | Allocations |
|---|---|---|
| Frequency (Monobit) | 29 µs | 0 |
| Block Frequency | 665 µs | 0 |
| Cumulative Sums | 8.7 ms | 1 |
| Runs | 3.5 ms | 0 |
| Longest Run of Ones | 4.3 ms | 2 |
| Binary Matrix Rank | 15 ms | 31,233 |
| Discrete Fourier Transform | 41 ms | 5 |
| Non-Overlapping Template | 655 ms | 1 |
| Overlapping Template | 5.4 ms | 1 |
| Universal Statistical | 3.6 ms | 2 |
| Approximate Entropy | 10 ms | 1 |
| Random Excursions | 8.5 ms | 1 |
| Random Excursions Variant | 8.9 ms | 1 |
| Serial | 23 ms | 1 |
| Linear Complexity | 13 ms | 1 |
| Full Suite (all 15 tests) | 1.42 s | 42,000 |
- Minimum bits: 387,840 (required for Universal Statistical Test)
- Maximum bits: 10,000,000 (performance limit)
- Recommended: 1,000,000 bits for optimal reliability
The service includes pprof endpoints for detailed runtime analysis. Access profiling data at http://localhost:9091/debug/pprof/:
# CPU profiling (30 second sample)
curl http://localhost:9091/debug/pprof/profile?seconds=30 > cpu.prof
go tool pprof -http=:8080 cpu.prof
# Memory heap profiling
curl http://localhost:9091/debug/pprof/heap > heap.prof
go tool pprof -http=:8080 heap.prof
# Goroutine analysis
curl http://localhost:9091/debug/pprof/goroutine > goroutine.prof
go tool pprof -http=:8080 goroutine.profAvailable pprof profiles: heap, goroutine, threadcreate, block, mutex, profile (CPU), trace
Metrics are exposed at http://localhost:9091/metrics:
nist_tests_total- Total number of test executionsnist_test_duration_seconds- Test execution duration histogramnist_test_failures_total- Count of failed testsnist_requests_total- Total gRPC requests
Access Grafana at http://localhost:3000 (default credentials: admin/admin) after starting with docker-compose.
Every gRPC request is assigned a unique Request-ID (UUID) for distributed tracing:
- Logged in all server logs under the
request_idfield - Returned to clients via gRPC metadata header
x-request-id - Enables end-to-end request correlation across distributed systems
Example log entry:
{
"level": "debug",
"request_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"method": "/nist.v1.NISTTestService/RunTests",
"duration": 1423,
"message": "gRPC request completed"
}The service uses zerolog for high-performance structured logging with zero allocations. Log output includes request IDs, method names, durations, and error details for comprehensive observability.
Detailed architectural and API documentation is available in the docs/ directory:
- Architecture Overview - System design, component responsibilities, request processing pipeline, security model, observability, validation strategy, and CI/CD pipeline
- API Reference - Complete gRPC service specification, message schemas, HTTP endpoints, Prometheus metrics reference, and authentication details
This project is licensed under the MIT License. See the LICENSE file for details.
This implementation is based on the algorithms described in NIST Special Publication 800-22 Revision 1a (April 2010): "A Statistical Test Suite for Random and Pseudorandom Number Generators for Cryptographic Applications". The original NIST specification is a public domain work of the United States Government.
This project reimplements the NIST test suite algorithms in pure Go. All test implementations have been validated to produce numerically identical results to the original NIST C reference implementation.
Test datasets from the original NIST Statistical Test Suite are included in the testdata directory for validation and testing purposes.
Note: While this implementation has been validated against the NIST reference implementation, users are responsible for determining whether it meets their specific requirements. For applications requiring FIPS compliance or other regulatory certifications, appropriate validation and certification procedures must be followed.
Reference: NIST SP 800-22 Rev. 1a
- Go 1.25 or later
- Protocol Buffers compiler (
protoc) - Docker and Docker Compose (for containerized deployment)
- GCC (only if modifying C reference code)
make help # Show all available targets
make proto # Generate protobuf code
make build # Build binary
make build-arm64 # Build for ARM64
make dev # Run in development mode
make clean # Remove build artifacts
make fmt # Format code
make lint # Run linters
make tools # Install development tools
# Testing
make test # Run unit tests
make test-race # Run tests with race detector
make coverage # Generate coverage report with 90% threshold check
make cover-html # Generate HTML coverage report
# Benchmarking
make bench # Run performance benchmarks
make bench-all # Run benchmarks with 10 iterations
make bench-baseline # Capture baseline for comparison
make bench-compare # Compare current benchmarks with baselineThe project includes two GitHub Actions workflows:
Continuous Integration (ci.yml):
- Format checking and code style validation
- Static analysis (staticcheck, go vet)
- Security scanning (gosec, govulncheck)
- Unit tests with coverage gating (90% threshold)
- Race condition detection
- Binary builds (native and ARM64)
- Artifact uploads for test results and binaries
NIST Validation (nist-validation.yml):
- Builds and runs original NIST C reference suite
- Generates reference P-values from multiple datasets
- Validates Pure Go implementation matches C reference
- Tests 6 datasets (data.pi, data.e, data.sqrt2, data.sqrt3, data.sha1, data.bad_rng)
- Automatic failure if P-values differ from reference
Both workflows run on push and pull requests to main branch.
Service won't start
- Check port availability:
lsof -i :9090 - Verify protobuf generation:
make proto - Check logs for configuration errors
Tests fail validation
- Ensure dataset has sufficient bits (minimum 387,840)
- Verify data format (raw binary, not text)
- Check for data corruption
Performance issues
- Reduce input size (max 10M bits recommended)
- Check system resources (2 CPU cores, 512MB RAM minimum)
- Review metrics at
/metricsendpoint
- Fork the repository
- Create a feature branch
- Make changes with tests
- Ensure
make testandmake lintpass - Submit a pull request
All contributions must maintain validation against the NIST reference implementation.