forked from vuvuzela/vuvuzela
/
salsa20rand.go
107 lines (87 loc) · 1.9 KB
/
salsa20rand.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package rand
import (
"crypto/rand"
"encoding/binary"
"io"
"runtime"
"sync"
"golang.org/x/crypto/salsa20"
)
// TODO we should occasionally reseed the PRNG
type Salsa20Rand struct {
zeroes []byte
buffer []byte
bufferOffset int
key [32]byte
nonce uint64
}
func NewSalsa20Rand(base io.Reader) *Salsa20Rand {
n := 16 * 1024
sr := &Salsa20Rand{
zeroes: make([]byte, n),
buffer: make([]byte, n),
bufferOffset: n,
nonce: 0,
}
if n, err := base.Read(sr.key[:]); n != 32 || err != nil {
panic("NewSalsa20Rand: " + err.Error())
}
sr.fill()
return sr
}
func (sr *Salsa20Rand) Read(d []byte) (n int, err error) {
for len(d) > 0 {
if sr.bufferOffset == len(sr.buffer) {
sr.fill()
}
m := copy(d, sr.buffer[sr.bufferOffset:])
d = d[m:]
sr.bufferOffset += m
n += m
}
return
}
func (sr *Salsa20Rand) fill() {
var nonce [8]byte
binary.BigEndian.PutUint64(nonce[:], sr.nonce)
sr.nonce += 1
salsa20.XORKeyStream(sr.buffer, sr.zeroes, nonce[:], &sr.key)
sr.bufferOffset = 0
}
type MutexReader struct {
mu sync.Mutex
reader io.Reader
}
func NewMutexReader(reader io.Reader) io.Reader {
return &MutexReader{
reader: reader,
}
}
func (mr *MutexReader) Read(d []byte) (n int, err error) {
mr.mu.Lock()
n, err = mr.reader.Read(d)
mr.mu.Unlock()
return
}
type PerCPUReader struct {
readers []io.Reader
}
func NewPerCPUReader(initfunc func() io.Reader) io.Reader {
readers := make([]io.Reader, runtime.NumCPU())
for i := 0; i < runtime.NumCPU(); i++ {
readers[i] = initfunc()
}
return &PerCPUReader{
readers: readers,
}
}
func (pcr *PerCPUReader) Read(d []byte) (n int, err error) {
thiscpu := cpu()
return pcr.readers[thiscpu%uint64(runtime.NumCPU())].Read(d)
}
var Reader = NewPerCPUReader(func() io.Reader {
return NewMutexReader(NewSalsa20Rand(rand.Reader))
})
func Read(b []byte) (n int, err error) {
return io.ReadFull(Reader, b)
}