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