Source File
chacha8.go
Belonging Package
internal/chacha8rand
// 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 chacha8randimportconst (ctrInc = 4 // increment counter by 4 between block callsctrMax = 16 // reseed when counter reaches 16chunk = 32 // each chunk produced by block is 32 uint64sreseed = 4 // reseed with 4 words)// block is the chacha8rand block function.func ( *[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]uint64seed [4]uint64i uint32n uint32c 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:nosplitfunc ( *State) () (uint64, bool) {:= .iif >= .n {return 0, false}.i = + 1return .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 += ctrIncif .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]uint64for := 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 + .ibyteorder.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 = chunkif .c == ctrMax-ctrInc {.n = chunk - reseed}return nil}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)