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