github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/chacha8rand/chacha8.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package chacha8rand implements a pseudorandom generator 6 // based on ChaCha8. It is used by both runtime and math/rand/v2 7 // and must have no dependencies. 8 package chacha8rand 9 10 const ( 11 ctrInc = 4 // increment counter by 4 between block calls 12 ctrMax = 16 // reseed when counter reaches 16 13 chunk = 32 // each chunk produced by block is 32 uint64s 14 reseed = 4 // reseed with 4 words 15 ) 16 17 // block is the chacha8rand block function. 18 func block(seed *[4]uint64, blocks *[32]uint64, counter uint32) 19 20 // A State holds the state for a single random generator. 21 // It must be used from one goroutine at a time. 22 // If used by multiple goroutines at a time, the goroutines 23 // may see the same random values, but the code will not 24 // crash or cause out-of-bounds memory accesses. 25 type State struct { 26 buf [32]uint64 27 seed [4]uint64 28 i uint32 29 n uint32 30 c uint32 31 } 32 33 // Next returns the next random value, along with a boolean 34 // indicating whether one was available. 35 // If one is not available, the caller should call Refill 36 // and then repeat the call to Next. 37 // 38 // Next is //go:nosplit to allow its use in the runtime 39 // with per-m data without holding the per-m lock. 40 // 41 //go:nosplit 42 func (s *State) Next() (uint64, bool) { 43 i := s.i 44 if i >= s.n { 45 return 0, false 46 } 47 s.i = i + 1 48 return s.buf[i&31], true // i&31 eliminates bounds check 49 } 50 51 // Init seeds the State with the given seed value. 52 func (s *State) Init(seed [32]byte) { 53 s.Init64([4]uint64{ 54 leUint64(seed[0*8:]), 55 leUint64(seed[1*8:]), 56 leUint64(seed[2*8:]), 57 leUint64(seed[3*8:]), 58 }) 59 } 60 61 // Init64 seeds the state with the given seed value. 62 func (s *State) Init64(seed [4]uint64) { 63 s.seed = seed 64 block(&s.seed, &s.buf, 0) 65 s.c = 0 66 s.i = 0 67 s.n = chunk 68 } 69 70 // Refill refills the state with more random values. 71 // After a call to Refill, an immediate call to Next will succeed 72 // (unless multiple goroutines are incorrectly sharing a state). 73 func (s *State) Refill() { 74 s.c += ctrInc 75 if s.c == ctrMax { 76 // Reseed with generated uint64s for forward secrecy. 77 // Normally this is done immediately after computing a block, 78 // but we do it immediately before computing the next block, 79 // to allow a much smaller serialized state (just the seed plus offset). 80 // This gives a delayed benefit for the forward secrecy 81 // (you can reconstruct the recent past given a memory dump), 82 // which we deem acceptable in exchange for the reduced size. 83 s.seed[0] = s.buf[len(s.buf)-reseed+0] 84 s.seed[1] = s.buf[len(s.buf)-reseed+1] 85 s.seed[2] = s.buf[len(s.buf)-reseed+2] 86 s.seed[3] = s.buf[len(s.buf)-reseed+3] 87 s.c = 0 88 } 89 block(&s.seed, &s.buf, s.c) 90 s.i = 0 91 s.n = uint32(len(s.buf)) 92 if s.c == ctrMax-ctrInc { 93 s.n = uint32(len(s.buf)) - reseed 94 } 95 } 96 97 // Reseed reseeds the state with new random values. 98 // After a call to Reseed, any previously returned random values 99 // have been erased from the memory of the state and cannot be 100 // recovered. 101 func (s *State) Reseed() { 102 var seed [4]uint64 103 for i := range seed { 104 for { 105 x, ok := s.Next() 106 if ok { 107 seed[i] = x 108 break 109 } 110 s.Refill() 111 } 112 } 113 s.Init64(seed) 114 } 115 116 // Marshal marshals the state into a byte slice. 117 // Marshal and Unmarshal are functions, not methods, 118 // so that they will not be linked into the runtime 119 // when it uses the State struct, since the runtime 120 // does not need these. 121 func Marshal(s *State) []byte { 122 data := make([]byte, 6*8) 123 copy(data, "chacha8:") 124 used := (s.c/ctrInc)*chunk + s.i 125 bePutUint64(data[1*8:], uint64(used)) 126 for i, seed := range s.seed { 127 lePutUint64(data[(2+i)*8:], seed) 128 } 129 return data 130 } 131 132 type errUnmarshalChaCha8 struct{} 133 134 func (*errUnmarshalChaCha8) Error() string { 135 return "invalid ChaCha8 encoding" 136 } 137 138 // Unmarshal unmarshals the state from a byte slice. 139 func Unmarshal(s *State, data []byte) error { 140 if len(data) != 6*8 || string(data[:8]) != "chacha8:" { 141 return new(errUnmarshalChaCha8) 142 } 143 used := beUint64(data[1*8:]) 144 if used > (ctrMax/ctrInc)*chunk-reseed { 145 return new(errUnmarshalChaCha8) 146 } 147 for i := range s.seed { 148 s.seed[i] = leUint64(data[(2+i)*8:]) 149 } 150 s.c = ctrInc * (uint32(used) / chunk) 151 block(&s.seed, &s.buf, s.c) 152 s.i = uint32(used) % chunk 153 s.n = chunk 154 if s.c == ctrMax-ctrInc { 155 s.n = chunk - reseed 156 } 157 return nil 158 } 159 160 // binary.bigEndian.Uint64, copied to avoid dependency 161 func beUint64(b []byte) uint64 { 162 _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 163 return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | 164 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 165 } 166 167 // binary.bigEndian.PutUint64, copied to avoid dependency 168 func bePutUint64(b []byte, v uint64) { 169 _ = b[7] // early bounds check to guarantee safety of writes below 170 b[0] = byte(v >> 56) 171 b[1] = byte(v >> 48) 172 b[2] = byte(v >> 40) 173 b[3] = byte(v >> 32) 174 b[4] = byte(v >> 24) 175 b[5] = byte(v >> 16) 176 b[6] = byte(v >> 8) 177 b[7] = byte(v) 178 } 179 180 // binary.littleEndian.Uint64, copied to avoid dependency 181 func leUint64(b []byte) uint64 { 182 _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 183 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | 184 uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 185 } 186 187 // binary.littleEndian.PutUint64, copied to avoid dependency 188 func lePutUint64(b []byte, v uint64) { 189 _ = b[7] // early bounds check to guarantee safety of writes below 190 b[0] = byte(v) 191 b[1] = byte(v >> 8) 192 b[2] = byte(v >> 16) 193 b[3] = byte(v >> 24) 194 b[4] = byte(v >> 32) 195 b[5] = byte(v >> 40) 196 b[6] = byte(v >> 48) 197 b[7] = byte(v >> 56) 198 }