// 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 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{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 {iflen() != 6*8 || string([:8]) != "chacha8:" {returnnew(errUnmarshalChaCha8) } := byteorder.BeUint64([1*8:])if > (ctrMax/ctrInc)*chunk-reseed {returnnew(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 }returnnil}
The pages are generated with Goldsv0.7.0-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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds.