// Copyright 2023 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 chacha8rand implements a pseudorandom generator // based on ChaCha8. It is used by both runtime and math/rand/v2 // and must have no dependencies.
package chacha8rand const ( ctrInc = 4 // increment counter by 4 between block calls ctrMax = 16 // reseed when counter reaches 16 chunk = 32 // each chunk produced by block is 32 uint64s reseed = 4 // reseed with 4 words ) // block is the chacha8rand block function. func block( *[4]uint64, *[32]uint64, uint32) // A State holds the state for a single random generator. // It must be used from one goroutine at a time. // If used by multiple goroutines at a time, the goroutines // may see the same random values, but the code will not // crash or cause out-of-bounds memory accesses. type State struct { buf [32]uint64 seed [4]uint64 i uint32 n uint32 c uint32 } // Next returns the next random value, along with a boolean // indicating whether one was available. // If one is not available, the caller should call Refill // and then repeat the call to Next. // // Next is //go:nosplit to allow its use in the runtime // with per-m data without holding the per-m lock. //go:nosplit func ( *State) () (uint64, bool) { := .i if >= .n { return 0, false } .i = + 1 return .buf[&31], true // i&31 eliminates bounds check } // Init seeds the State with the given seed value. func ( *State) ( [32]byte) { .Init64([4]uint64{ leUint64([0*8:]), leUint64([1*8:]), leUint64([2*8:]), leUint64([3*8:]), }) } // Init64 seeds the state with the given seed value. func ( *State) ( [4]uint64) { .seed = block(&.seed, &.buf, 0) .c = 0 .i = 0 .n = chunk } // Refill refills the state with more random values. // After a call to Refill, an immediate call to Next will succeed // (unless multiple goroutines are incorrectly sharing a state). func ( *State) () { .c += ctrInc if .c == ctrMax { // Reseed with generated uint64s for forward secrecy. // Normally this is done immediately after computing a block, // but we do it immediately before computing the next block, // to allow a much smaller serialized state (just the seed plus offset). // This gives a delayed benefit for the forward secrecy // (you can reconstruct the recent past given a memory dump), // which we deem acceptable in exchange for the reduced size. .seed[0] = .buf[len(.buf)-reseed+0] .seed[1] = .buf[len(.buf)-reseed+1] .seed[2] = .buf[len(.buf)-reseed+2] .seed[3] = .buf[len(.buf)-reseed+3] .c = 0 } block(&.seed, &.buf, .c) .i = 0 .n = uint32(len(.buf)) if .c == ctrMax-ctrInc { .n = uint32(len(.buf)) - reseed } } // Reseed reseeds the state with new random values. // After a call to Reseed, any previously returned random values // have been erased from the memory of the state and cannot be // recovered. func ( *State) () { var [4]uint64 for := range { for { , := .Next() if { [] = break } .Refill() } } .Init64() } // Marshal marshals the state into a byte slice. // Marshal and Unmarshal are functions, not methods, // so that they will not be linked into the runtime // when it uses the State struct, since the runtime // does not need these. func ( *State) []byte { := make([]byte, 6*8) copy(, "chacha8:") := (.c/ctrInc)*chunk + .i bePutUint64([1*8:], uint64()) for , := range .seed { lePutUint64([(2+)*8:], ) } return } type errUnmarshalChaCha8 struct{} func (*errUnmarshalChaCha8) () string { return "invalid ChaCha8 encoding" } // Unmarshal unmarshals the state from a byte slice. func ( *State, []byte) error { if len() != 6*8 || string([:8]) != "chacha8:" { return new(errUnmarshalChaCha8) } := beUint64([1*8:]) if > (ctrMax/ctrInc)*chunk-reseed { return new(errUnmarshalChaCha8) } for := range .seed { .seed[] = leUint64([(2+)*8:]) } .c = ctrInc * (uint32() / chunk) block(&.seed, &.buf, .c) .i = uint32() % chunk .n = chunk if .c == ctrMax-ctrInc { .n = chunk - reseed } return nil } // binary.bigEndian.Uint64, copied to avoid dependency func beUint64( []byte) uint64 { _ = [7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64([7]) | uint64([6])<<8 | uint64([5])<<16 | uint64([4])<<24 | uint64([3])<<32 | uint64([2])<<40 | uint64([1])<<48 | uint64([0])<<56 } // binary.bigEndian.PutUint64, copied to avoid dependency func bePutUint64( []byte, uint64) { _ = [7] // early bounds check to guarantee safety of writes below [0] = byte( >> 56) [1] = byte( >> 48) [2] = byte( >> 40) [3] = byte( >> 32) [4] = byte( >> 24) [5] = byte( >> 16) [6] = byte( >> 8) [7] = byte() } // binary.littleEndian.Uint64, copied to avoid dependency func leUint64( []byte) uint64 { _ = [7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64([0]) | uint64([1])<<8 | uint64([2])<<16 | uint64([3])<<24 | uint64([4])<<32 | uint64([5])<<40 | uint64([6])<<48 | uint64([7])<<56 } // binary.littleEndian.PutUint64, copied to avoid dependency func lePutUint64( []byte, uint64) { _ = [7] // early bounds check to guarantee safety of writes below [0] = byte() [1] = byte( >> 8) [2] = byte( >> 16) [3] = byte( >> 24) [4] = byte( >> 32) [5] = byte( >> 40) [6] = byte( >> 48) [7] = byte( >> 56) }