// 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 chacha8randconst ( 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.typeStatestruct { 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:nosplitfunc ( *State) () (uint64, bool) { := .iif >= .n {return0, 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{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 += 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 + .ibePutUint64([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 {iflen() != 6*8 || string([:8]) != "chacha8:" {returnnew(errUnmarshalChaCha8) } := beUint64([1*8:])if > (ctrMax/ctrInc)*chunk-reseed {returnnew(errUnmarshalChaCha8) }for := range .seed { .seed[] = leUint64([(2+)*8:]) } .c = ctrInc * (uint32() / chunk)block(&.seed, &.buf, .c) .i = uint32() % chunk .n = chunkif .c == ctrMax-ctrInc { .n = chunk - reseed }returnnil}// binary.bigEndian.Uint64, copied to avoid dependencyfunc beUint64( []byte) uint64 { _ = [7] // bounds check hint to compiler; see golang.org/issue/14808returnuint64([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 dependencyfunc 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 dependencyfunc leUint64( []byte) uint64 { _ = [7] // bounds check hint to compiler; see golang.org/issue/14808returnuint64([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 dependencyfunc 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)}
The pages are generated with Goldsv0.6.9-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.