1
2
3
4
5 package rand
6
7 import (
8 "errors"
9 "internal/byteorder"
10 "internal/chacha8rand"
11 )
12
13
14
15 type ChaCha8 struct {
16 state chacha8rand.State
17
18
19 readBuf [8]byte
20 readLen int
21 }
22
23
24 func NewChaCha8(seed [32]byte) *ChaCha8 {
25 c := new(ChaCha8)
26 c.state.Init(seed)
27 return c
28 }
29
30
31 func (c *ChaCha8) Seed(seed [32]byte) {
32 c.state.Init(seed)
33 c.readLen = 0
34 c.readBuf = [8]byte{}
35 }
36
37
38 func (c *ChaCha8) Uint64() uint64 {
39 for {
40 x, ok := c.state.Next()
41 if ok {
42 return x
43 }
44 c.state.Refill()
45 }
46 }
47
48
49
50
51
52
53
54 func (c *ChaCha8) Read(p []byte) (n int, err error) {
55 if c.readLen > 0 {
56 n = copy(p, c.readBuf[len(c.readBuf)-c.readLen:])
57 c.readLen -= n
58 p = p[n:]
59 }
60 for len(p) >= 8 {
61 byteorder.LePutUint64(p, c.Uint64())
62 p = p[8:]
63 n += 8
64 }
65 if len(p) > 0 {
66 byteorder.LePutUint64(c.readBuf[:], c.Uint64())
67 n += copy(p, c.readBuf[:])
68 c.readLen = 8 - len(p)
69 }
70 return
71 }
72
73
74 func (c *ChaCha8) UnmarshalBinary(data []byte) error {
75 data, ok := cutPrefix(data, []byte("readbuf:"))
76 if ok {
77 var buf []byte
78 buf, data, ok = readUint8LengthPrefixed(data)
79 if !ok {
80 return errors.New("invalid ChaCha8 Read buffer encoding")
81 }
82 c.readLen = copy(c.readBuf[len(c.readBuf)-len(buf):], buf)
83 }
84 return chacha8rand.Unmarshal(&c.state, data)
85 }
86
87 func cutPrefix(s, prefix []byte) (after []byte, found bool) {
88 if len(s) < len(prefix) || string(s[:len(prefix)]) != string(prefix) {
89 return s, false
90 }
91 return s[len(prefix):], true
92 }
93
94 func readUint8LengthPrefixed(b []byte) (buf, rest []byte, ok bool) {
95 if len(b) == 0 || len(b) < int(1+b[0]) {
96 return nil, nil, false
97 }
98 return b[1 : 1+b[0]], b[1+b[0]:], true
99 }
100
101
102 func (c *ChaCha8) MarshalBinary() ([]byte, error) {
103 if c.readLen > 0 {
104 out := []byte("readbuf:")
105 out = append(out, uint8(c.readLen))
106 out = append(out, c.readBuf[len(c.readBuf)-c.readLen:]...)
107 return append(out, chacha8rand.Marshal(&c.state)...), nil
108 }
109 return chacha8rand.Marshal(&c.state), nil
110 }
111
View as plain text