// 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 minimal dependencies.
package chacha8rand import 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{ byteorder.LeUint64([0*8:]), byteorder.LeUint64([1*8:]), byteorder.LeUint64([2*8:]), byteorder.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 byteorder.BePutUint64([1*8:], uint64()) for , := range .seed { byteorder.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) } := byteorder.BeUint64([1*8:]) if > (ctrMax/ctrInc)*chunk-reseed { return new(errUnmarshalChaCha8) } for := range .seed { .seed[] = byteorder.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 }