init: v1.0.0
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
package entropy
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
)
|
||||
|
||||
var OutOfEntropyErr = errors.New("out of entropy")
|
||||
|
||||
// Read reads at most 32 bytes into p.
|
||||
// For efficiency, reads bytes are always multiples of 4.
|
||||
func (e *EntropyPool) Read(p []byte) (int, error) {
|
||||
entropyPool.mu.Lock()
|
||||
|
||||
if entropyPool.entropySize <= 0 {
|
||||
return 0, OutOfEntropyErr
|
||||
}
|
||||
// n = min(8, entropyPool.entropySize, len(p) / 4)
|
||||
n := 8
|
||||
if entropyPool.entropySize < 8 {
|
||||
n = entropyPool.entropySize
|
||||
}
|
||||
|
||||
if (len(p) / 4) < n {
|
||||
n = len(p) / 4
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
x, _ := entropyPool.get() // err should not happen
|
||||
binary.BigEndian.PutUint32(p[4*i:], x)
|
||||
}
|
||||
|
||||
entropyPool.mu.Unlock()
|
||||
|
||||
go e.update() // update the entropy pool for next reading.
|
||||
|
||||
return 4 * n, nil
|
||||
}
|
||||
|
||||
// EntropyPool 系统熵池
|
||||
type EntropyPool struct {
|
||||
mu sync.Mutex //使用互斥锁保证独占性
|
||||
pool [128]uint32 //熵池,128个字(512字节)
|
||||
// i - j 之间是未取的熵(mod 128)
|
||||
i int //熵索引开始
|
||||
j int //熵索引结束
|
||||
entropySize int //熵池中可用熵值
|
||||
source []EntropySource
|
||||
}
|
||||
|
||||
// entropySource 熵源
|
||||
type EntropySource interface {
|
||||
GetEntropy(minEntropy int64, minEntropyInputLength int64, maxEntropyInputLength int64) ([]byte, error)
|
||||
}
|
||||
|
||||
var entropyPool *EntropyPool
|
||||
var initEntropyPool sync.Once
|
||||
|
||||
func init() {
|
||||
initEntropyPool.Do(
|
||||
func() {
|
||||
entropyPool = newEntropyPool()
|
||||
})
|
||||
go func() {
|
||||
// 定时从系统中获取
|
||||
tickle := time.NewTicker(10 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-tickle.C:
|
||||
entropyPool.update()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func GetEntropyPool() *EntropyPool {
|
||||
return entropyPool
|
||||
}
|
||||
|
||||
func newEntropyPool() *EntropyPool {
|
||||
// FIXME: SysTimeEntropySource?
|
||||
p := &EntropyPool{
|
||||
source: []EntropySource{&OSEntropySource{}, &SysTimeEntropySource{}},
|
||||
// source: []drng.EntropySource{&OSEntropySource{}},
|
||||
}
|
||||
|
||||
buf := make([]byte, 128*4)
|
||||
_, _ = rand.Reader.Read(buf)
|
||||
for i := 0; i < 128; i++ {
|
||||
p.pool[i] = binary.BigEndian.Uint32(buf[4*i:])
|
||||
}
|
||||
|
||||
// 从每个熵源获取至少256比特
|
||||
p.update()
|
||||
|
||||
// 对buf置0
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
// for i := range e {
|
||||
// e[i] = 0
|
||||
// }
|
||||
return p
|
||||
}
|
||||
|
||||
func (e *EntropyPool) get() (uint32, error) {
|
||||
if e.entropySize <= 0 {
|
||||
return 0, OutOfEntropyErr
|
||||
}
|
||||
n := e.pool[e.i]
|
||||
e.i = (e.i + 1) & 127
|
||||
e.entropySize--
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// lshr 添加新的熵数据g到熵池中。按照GM/T 0105 A.3的方式
|
||||
func (e *EntropyPool) lshr(g uint32) {
|
||||
var table = [8]uint32{0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278}
|
||||
temp := g ^ e.pool[e.j]
|
||||
for _, i := range []int{1, 25, 51, 76, 103} {
|
||||
temp ^= e.pool[(e.j+i)&127]
|
||||
}
|
||||
temp = (temp >> 3) ^ table[temp&7]
|
||||
e.pool[e.j] = temp
|
||||
e.j = (e.j + 1) & 127
|
||||
|
||||
// 置0
|
||||
temp = 0
|
||||
_ = temp
|
||||
}
|
||||
|
||||
// Update 熵池更新
|
||||
func (e *EntropyPool) update() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
// 设置超时时间
|
||||
tickle := time.NewTicker(100 * time.Millisecond)
|
||||
defer tickle.Stop()
|
||||
|
||||
for i := 0; e.entropySize < 128; i = (i + 1) % len(e.source) {
|
||||
select {
|
||||
case <-tickle.C:
|
||||
break
|
||||
default:
|
||||
s := e.source[i]
|
||||
b, _ := s.GetEntropy(4, 4, internal.MaxEntropyInputLength)
|
||||
if len(b) >= 4 {
|
||||
e.lshr(binary.BigEndian.Uint32(b))
|
||||
e.entropySize++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EntropyPool) GetEntropy(minEntropy int64, minEntropyInputLength int64, maxEntropyInputLength int64) ([]byte, error) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
if minEntropyInputLength < internal.MinEntropyInputLength || maxEntropyInputLength > internal.MaxEntropyInputLength {
|
||||
return nil, internal.ErrEntropyLength
|
||||
}
|
||||
|
||||
entropy := make([]byte, minEntropyInputLength)
|
||||
buf := make([]byte, 4)
|
||||
var i int
|
||||
for i = 0; int64(i) < minEntropyInputLength && e.entropySize > 0; i += 4 {
|
||||
binary.BigEndian.PutUint32(buf, e.pool[e.i])
|
||||
copy(entropy[i:], buf)
|
||||
e.i = (e.i + 1) & 127
|
||||
e.entropySize--
|
||||
}
|
||||
return entropy[:i], nil
|
||||
}
|
||||
Reference in New Issue
Block a user