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 }