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 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 ( *[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
}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)