1
2
3
4
5
6
7
8 package chacha8rand
9
10 import (
11 "internal/byteorder"
12 "internal/cpu"
13 "unsafe"
14 )
15
16
17 const (
18 offsetLOONG64HasLSX = unsafe.Offsetof(cpu.Loong64.HasLSX)
19 offsetRISCV64HasV = unsafe.Offsetof(cpu.RISCV64.HasV)
20 )
21
22 const (
23 ctrInc = 4
24 ctrMax = 16
25 chunk = 32
26 reseed = 4
27 )
28
29
30 func block(seed *[4]uint64, blocks *[32]uint64, counter uint32)
31
32
33
34
35
36
37 type State struct {
38 buf [32]uint64
39 seed [4]uint64
40 i uint32
41 n uint32
42 c uint32
43 }
44
45
46
47
48
49
50
51
52
53
54 func (s *State) Next() (uint64, bool) {
55 i := s.i
56 if i >= s.n {
57 return 0, false
58 }
59 s.i = i + 1
60 return s.buf[i&31], true
61 }
62
63
64 func (s *State) Init(seed [32]byte) {
65 s.Init64([4]uint64{
66 byteorder.LEUint64(seed[0*8:]),
67 byteorder.LEUint64(seed[1*8:]),
68 byteorder.LEUint64(seed[2*8:]),
69 byteorder.LEUint64(seed[3*8:]),
70 })
71 }
72
73
74 func (s *State) Init64(seed [4]uint64) {
75 s.seed = seed
76 block(&s.seed, &s.buf, 0)
77 s.c = 0
78 s.i = 0
79 s.n = chunk
80 }
81
82
83
84
85 func (s *State) Refill() {
86 s.c += ctrInc
87 if s.c == ctrMax {
88
89
90
91
92
93
94
95 s.seed[0] = s.buf[len(s.buf)-reseed+0]
96 s.seed[1] = s.buf[len(s.buf)-reseed+1]
97 s.seed[2] = s.buf[len(s.buf)-reseed+2]
98 s.seed[3] = s.buf[len(s.buf)-reseed+3]
99 s.c = 0
100 }
101 block(&s.seed, &s.buf, s.c)
102 s.i = 0
103 s.n = uint32(len(s.buf))
104 if s.c == ctrMax-ctrInc {
105 s.n = uint32(len(s.buf)) - reseed
106 }
107 }
108
109
110
111
112
113 func (s *State) Reseed() {
114 var seed [4]uint64
115 for i := range seed {
116 for {
117 x, ok := s.Next()
118 if ok {
119 seed[i] = x
120 break
121 }
122 s.Refill()
123 }
124 }
125 s.Init64(seed)
126 }
127
128
129
130
131
132
133 func Marshal(s *State) []byte {
134 data := make([]byte, 6*8)
135 copy(data, "chacha8:")
136 used := (s.c/ctrInc)*chunk + s.i
137 byteorder.BEPutUint64(data[1*8:], uint64(used))
138 for i, seed := range s.seed {
139 byteorder.LEPutUint64(data[(2+i)*8:], seed)
140 }
141 return data
142 }
143
144 type errUnmarshalChaCha8 struct{}
145
146 func (*errUnmarshalChaCha8) Error() string {
147 return "invalid ChaCha8 encoding"
148 }
149
150
151 func Unmarshal(s *State, data []byte) error {
152 if len(data) != 6*8 || string(data[:8]) != "chacha8:" {
153 return new(errUnmarshalChaCha8)
154 }
155 used := byteorder.BEUint64(data[1*8:])
156 if used > (ctrMax/ctrInc)*chunk-reseed {
157 return new(errUnmarshalChaCha8)
158 }
159 for i := range s.seed {
160 s.seed[i] = byteorder.LEUint64(data[(2+i)*8:])
161 }
162 s.c = ctrInc * (uint32(used) / chunk)
163 block(&s.seed, &s.buf, s.c)
164 s.i = uint32(used) % chunk
165 s.n = chunk
166 if s.c == ctrMax-ctrInc {
167 s.n = chunk - reseed
168 }
169 return nil
170 }
171
View as plain text