-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathentropy.go
151 lines (131 loc) · 4.42 KB
/
entropy.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// entropy.go - collect environmental entropy into pools
// Copyright (C) 2013 Jochen Voss <[email protected]>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package fortuna
import (
"crypto/sha256"
"time"
)
const channelBufferSize = 4
// addRandomEvent should be called periodically to add entropy to the
// state of the random number generator. Different sources of
// randomness should use different values for the 'source' argument.
// The .allocateSource() method can be used to allocate source
// numbers.
//
// The value 'seq' is used to spread out entropy over the available
// entropy pools; for each entropy source, sequence values 0, 1, 2,
// ... should be passed in. Finally, the argument 'data' gives the
// randomness to add to the pool. 'data' should be at most 32 bytes
// long; longer values should be hashed by the caller and the hash be
// submitted instead.
func (acc *Accumulator) addRandomEvent(source uint8, seq uint, data []byte) {
pool := seq % numPools
acc.poolMutex.Lock()
defer acc.poolMutex.Unlock()
poolHash := acc.pool[pool]
poolHash.Write([]byte{source, byte(len(data))})
poolHash.Write(data)
if pool == 0 {
acc.poolZeroSize += 2 + len(data)
}
}
// allocateSource allocates a new source index for an entropy source.
func (acc *Accumulator) allocateSource() uint8 {
acc.sourceMutex.Lock()
defer acc.sourceMutex.Unlock()
source := acc.nextSource
acc.nextSource++
return source
}
// NewEntropyDataSink returns a channel through which data can be
// submitted to the Accumulator's entropy pools. Data should be
// written to the returned channel periodically to add entropy to the
// state of the random number generator. The written data should be
// derived from quantities which change between calls and which cannot
// be (completely) known to an attacker. Typical sources of
// randomness include noise from a microphone/camera, CPU cycle
// counters, or the number of processes running on the system.
//
// If the data written to the channel is longer than 32 bytes, the
// data is hashed internally and the hash is submitted to the entropy
// pools instead of the data itself.
//
// The channel can be closed by the caller to indicate that no more
// entropy will be sent via this channel.
func (acc *Accumulator) NewEntropyDataSink() chan<- []byte {
source := acc.allocateSource()
c := make(chan []byte, channelBufferSize)
acc.sources.Add(1)
go func() {
defer acc.sources.Done()
seq := uint(0)
loop:
for {
select {
case data, ok := <-c:
if !ok {
break loop
}
if len(data) > 32 {
hash := sha256.New()
hash.Write(data)
data = hash.Sum(nil)
}
acc.addRandomEvent(source, seq, data)
seq++
case <-acc.stopSources:
break loop
}
}
}()
return c
}
// NewEntropyTimeStampSink returns a channel through which timing data
// can be submitted to the Accumulator's entropy pools. The current
// time should be written to the returned channel regularly to add
// entropy to the state of the random number generator. The submitted
// times should be chosen such that they cannot be (completely) known
// to an attacker. Typical sources of randomness include the arrival
// times of network packets or the times of key-presses by the user.
//
// The channel can be closed by the caller to indicate that no more
// entropy will be sent via this channel.
func (acc *Accumulator) NewEntropyTimeStampSink() chan<- time.Time {
source := acc.allocateSource()
c := make(chan time.Time, channelBufferSize)
acc.sources.Add(1)
go func() {
defer acc.sources.Done()
seq := uint(0)
lastRequest := time.Now()
loop:
for {
select {
case now, ok := <-c:
if !ok {
break loop
}
dt := now.Sub(lastRequest)
lastRequest = now
acc.addRandomEvent(source, seq, int64ToBytes(int64(dt)))
seq++
case <-acc.stopSources:
break loop
}
}
}()
return c
}