// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package entropy implements a CPU jitter-based SP 800-90B entropy source.
package entropy import ( ) // Version returns the version of the entropy source. // // This is independent of the FIPS 140-3 module version, in order to reuse the // ESV certificate across module versions. func () string { return "v1.0.0" } // ScratchBuffer is a large buffer that will be written to using atomics, to // generate noise from memory access timings. Its contents do not matter. type ScratchBuffer [1 << 25]byte // Seed returns a 384-bit seed with full entropy. // // memory is passed in to allow changing the allocation strategy without // modifying the frozen and certified entropy source in this package. // // Seed returns an error if the entropy source startup health tests fail, which // has a non-negligible chance of happening. func ( *ScratchBuffer) ([48]byte, error) { // Collect w = 1024 samples, each certified to provide no less than h = 0.5 // bits of entropy, for a total of hᵢₙ = w × h = 512 bits of entropy, over // nᵢₙ = w × n = 8192 bits of input data. var [1024]byte if := Samples([:], ); != nil { return [48]byte{}, } // Use a vetted unkeyed conditioning component, SHA-384, with nw = 384 and // nₒᵤₜ = 384. Per the formula in SP 800-90B Section 3.1.5.1.2, the output // entropy hₒᵤₜ is: // // sage: n_in = 8192 // sage: n_out = 384 // sage: nw = 384 // sage: h_in = 512 // sage: P_high = 2^(-h_in) // sage: P_low = (1 - P_high) / (2^n_in - 1) // sage: n = min(n_out, nw) // sage: ψ = 2^(n_in - n) * P_low + P_high // sage: U = 2^(n_in - n) + sqrt(2 * n * 2^(n_in - n) * ln(2)) // sage: ω = U * P_low // sage: h_out = -log(max(ψ, ω), 2) // sage: h_out.n() // 384.000000000000 // // According to Implementation Guidance D.K, Resolution 19, since // // - the conditioning component is vetted, // - hᵢₙ = 512 ≥ nₒᵤₜ + 64 = 448, and // - nₒᵤₜ ≤ security strength of SHA-384 = 384 (per SP 800-107 Rev. 1, Table 1), // // we can claim the output has full entropy. return SHA384(&), nil } // Samples starts a new entropy source, collects the requested number of // samples, conducts startup health tests, and returns the samples or an error // if the health tests fail. // // The health tests have a non-negligible chance of failing. func ( []uint8, *ScratchBuffer) error { if len() < 1024 { return errors.New("entropy: at least 1024 samples are required for startup health tests") } := newSource() for range 4 { // Warm up the source to avoid any initial bias. _ = .Sample() } for := range { [] = .Sample() } if := RepetitionCountTest(); != nil { return } if := AdaptiveProportionTest(); != nil { return } return nil } type source struct { memory *ScratchBuffer lcgState uint32 previous int64 } func newSource( *ScratchBuffer) *source { return &source{ memory: , lcgState: uint32(time.HighPrecisionNow()), previous: time.HighPrecisionNow(), } } // touchMemory performs a write to memory at the given index. // // The memory slice is passed in and may be shared across sources e.g. to avoid // the significant (~500µs) cost of zeroing a new allocation on every [Seed] call. func touchMemory( *ScratchBuffer, uint32) { = / 4 * 4 // align to 32 bits := (*uint32)(unsafe.Pointer(&[])) := atomic.LoadUint32() atomic.SwapUint32(, +13) } func ( *source) () uint8 { // Perform a few memory accesses in an unpredictable pattern to expose the // next measurement to as much system noise as possible. , := .memory, .lcgState if == nil { // remove the nil check from the inlined touchMemory calls panic("entropy: nil memory buffer") } for range 64 { = 1664525* + 1013904223 // Discard the lower bits, which tend to fall into short cycles. := ( >> 6) & (1<<25 - 1) touchMemory(, ) } .lcgState = := time.HighPrecisionNow() := - .previous .previous = // Reduce the symbol space to 256 values, assuming most of the entropy is in // the least-significant bits, which represent the highest-resolution timing // differences. return uint8() } // RepetitionCountTest implements the repetition count test from SP 800-90B // Section 4.4.1. It returns an error if any symbol is repeated C = 41 or more // times in a row. // // This C value is calculated from a target failure probability α = 2⁻²⁰ and a // claimed min-entropy per symbol h = 0.5 bits, using the formula in SP 800-90B // Section 4.4.1. // // sage: α = 2^-20 // sage: H = 0.5 // sage: 1 + ceil(-log(α, 2) / H) // 41 func ( []uint8) error { := [0] := 1 for , := range [1:] { if == { ++ if >= 41 { return errors.New("entropy: repetition count health test failed") } } else { = = 1 } } return nil } // AdaptiveProportionTest implements the adaptive proportion test from SP 800-90B // Section 4.4.2. It returns an error if any symbol appears C = 410 or more // times in the last W = 512 samples. // // This C value is calculated from a target failure probability α = 2⁻²⁰, a // window size W = 512, and a claimed min-entropy per symbol h = 0.5 bits, using // the formula in SP 800-90B Section 4.4.2, equivalent to the Microsoft Excel // formula 1+CRITBINOM(W, power(2,(−H)),1−α). // // sage: from scipy.stats import binom // sage: α = 2^-20 // sage: H = 0.5 // sage: W = 512 // sage: C = 1 + binom.ppf(1 - α, W, 2**(-H)) // sage: ceil(C) // 410 func ( []uint8) error { var [256]int for , := range { []++ if >= 512 { [[-512]]-- } if [] >= 410 { return errors.New("entropy: adaptive proportion health test failed") } } return nil }