init: v1.0.0
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Package grand is the random number generators.
|
||||
|
||||
Use the following io.Reader to generate random numbers:
|
||||
- grand.Reader, the go's crypto/rand (/dev/urandom in *nix)
|
||||
- grand.SM3Rng, the DRNG of SM3_RNG
|
||||
- grand.SM4Rng, the DRNG of SM4_RNG
|
||||
|
||||
example:
|
||||
|
||||
buf := make([]byte,32)
|
||||
if _, err := grand.SM3Rng.Read(buf);err != nil{
|
||||
//
|
||||
}
|
||||
|
||||
随机数检测相关的xdx.jelly/
|
||||
*/
|
||||
package grand
|
||||
@@ -0,0 +1,38 @@
|
||||
package ctrrng
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
)
|
||||
|
||||
var defaultConfig1 = &internal.DrngConfig{
|
||||
Seedlen: 256 >> 3,
|
||||
Outlen: 128 >> 3,
|
||||
ReseedIntervalInTime: time.Second * 600,
|
||||
ReseedIntervalInCounter: 1 << 20,
|
||||
MinEntropy: 256 >> 3,
|
||||
MinEntropyInputLength: 256 >> 3,
|
||||
MaxEntropyInputLength: (1 << 35) >> 3,
|
||||
}
|
||||
|
||||
var defaultConfig2 = &internal.DrngConfig{
|
||||
Seedlen: 256 >> 3,
|
||||
Outlen: 128 >> 3,
|
||||
ReseedIntervalInTime: time.Second * 60,
|
||||
ReseedIntervalInCounter: 1 << 10,
|
||||
MinEntropy: 256 >> 3,
|
||||
MinEntropyInputLength: 256 >> 3,
|
||||
MaxEntropyInputLength: (1 << 35) >> 3,
|
||||
}
|
||||
|
||||
func Config(level internal.SecureLevel) *internal.DrngConfig {
|
||||
switch level {
|
||||
case internal.SecureLevel1:
|
||||
return defaultConfig1
|
||||
case internal.SecureLevel2:
|
||||
return defaultConfig2
|
||||
default:
|
||||
panic("unsupported secure level")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package ctrrng
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
"xdx.jelly/xgcl/internal/xor"
|
||||
"xdx.jelly/xgcl/sm/sm4"
|
||||
)
|
||||
|
||||
type SM4BlockNewer struct{}
|
||||
|
||||
func (b SM4BlockNewer) BlockSize() int {
|
||||
return sm4.BlockSize
|
||||
}
|
||||
|
||||
func (b SM4BlockNewer) New(key []byte) cipher.Block {
|
||||
if len(key) != sm4.BlockSize {
|
||||
panic("ctrrng: invalid block key length")
|
||||
}
|
||||
|
||||
block, _ := sm4.NewCipher(key)
|
||||
return block
|
||||
}
|
||||
|
||||
func NewCtrDrng(newBlock NewBlock, config *internal.DrngConfig) (internal.RawDrng, error) {
|
||||
hd := &CtrDrng{
|
||||
config: *config,
|
||||
newBlock: newBlock,
|
||||
df: newDF(newBlock),
|
||||
}
|
||||
|
||||
// 析构时对内部状态置0
|
||||
runtime.SetFinalizer(hd, func(hd *CtrDrng) {
|
||||
hd.state.zeromize()
|
||||
})
|
||||
return hd, nil
|
||||
}
|
||||
|
||||
type internalState struct {
|
||||
v []byte // blockLen
|
||||
k []byte // keyLen
|
||||
|
||||
reseedCounter uint32
|
||||
lastReseedTime time.Time
|
||||
}
|
||||
|
||||
// zeromize 对内部状态置0
|
||||
func (s *internalState) zeromize() {
|
||||
for i := 0; i < len(s.v); i++ {
|
||||
s.v[i] = 0
|
||||
}
|
||||
|
||||
for i := 0; i < len(s.k); i++ {
|
||||
s.k[i] = 0
|
||||
}
|
||||
|
||||
s.reseedCounter = 0
|
||||
}
|
||||
|
||||
type CtrDrng struct {
|
||||
// mu sync.Mutex
|
||||
|
||||
config internal.DrngConfig
|
||||
newBlock NewBlock
|
||||
df func(input []byte, n int) []byte
|
||||
state internalState
|
||||
personalizationString []byte //个性化字符串,来自应用的比特串
|
||||
}
|
||||
|
||||
type NewBlock interface {
|
||||
BlockSize() int
|
||||
New(key []byte) cipher.Block
|
||||
}
|
||||
|
||||
// newDF 返回一个使用block作为杂凑的df函数
|
||||
// 例:SM3_df = newDF(sm.SM3)
|
||||
func newDF(newBlock NewBlock) func(input []byte, n int) []byte {
|
||||
keylen := newBlock.BlockSize()
|
||||
outlen := newBlock.BlockSize()
|
||||
keymatial := []byte{0, 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, 31}
|
||||
|
||||
return func(input []byte, n int) []byte {
|
||||
L := len(input)
|
||||
|
||||
// S = L || n || input || 0x80 || 00 such that len(S) % n = 0
|
||||
S := make([]byte, ((9+L+n-1)/n)*n)
|
||||
binary.BigEndian.PutUint32(S, uint32(L))
|
||||
binary.BigEndian.PutUint32(S[4:], uint32(n))
|
||||
copy(S[8:], input)
|
||||
S[8+L] = 0x80
|
||||
|
||||
K := keymatial[:keylen]
|
||||
temp := make([]byte, 0, keylen+n)
|
||||
ivs := make([]byte, outlen+len(S)) // iv || S
|
||||
copy(ivs[4:], S)
|
||||
i := uint32(0)
|
||||
block := newBlock.New(K)
|
||||
for len(temp) < keylen+n {
|
||||
binary.BigEndian.PutUint32(ivs, i)
|
||||
i++
|
||||
temp = append(temp, cbcMac(block, ivs)...)
|
||||
}
|
||||
|
||||
K = temp[:keylen]
|
||||
X := temp[keylen:]
|
||||
block = newBlock.New(K)
|
||||
temp = []byte{}
|
||||
for len(temp) < n {
|
||||
block.Encrypt(X, X)
|
||||
temp = append(temp, X...)
|
||||
}
|
||||
return temp[:n]
|
||||
}
|
||||
}
|
||||
|
||||
// len(data) % block.BlockSize() == 0
|
||||
func cbcMac(block cipher.Block, data []byte) []byte {
|
||||
blockSize := block.BlockSize()
|
||||
iv := make([]byte, blockSize)
|
||||
n := 0
|
||||
for n+blockSize <= len(data) {
|
||||
xor.XorBytes(iv, iv, data[n:])
|
||||
block.Encrypt(iv, iv)
|
||||
n += blockSize
|
||||
}
|
||||
return iv
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) keyLen() int {
|
||||
return cd.newBlock.BlockSize()
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) blockLen() int {
|
||||
return cd.newBlock.BlockSize()
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) seedLen() int {
|
||||
return cd.keyLen() + cd.OutLen()
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) OutLen() int {
|
||||
return cd.config.Outlen
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) Config() *internal.DrngConfig {
|
||||
return &cd.config
|
||||
}
|
||||
|
||||
// Instantiate 初始化HashDrng
|
||||
func (cd *CtrDrng) Instantiate(personalizationString []byte, nonce []byte, entropySource io.Reader) (internal.Drng, error) {
|
||||
// cd.mu.Lock()
|
||||
// defer cd.mu.Unlock()
|
||||
|
||||
cfg := cd.config
|
||||
cd.personalizationString = personalizationString
|
||||
cd.state.k = make([]byte, cd.keyLen())
|
||||
cd.state.v = make([]byte, cd.blockLen())
|
||||
|
||||
seedMaterial := make([]byte, 2*cfg.MinEntropyInputLength, int(2*cfg.MinEntropyInputLength)+len(nonce)+len(personalizationString))
|
||||
var m, n int
|
||||
var err error
|
||||
var errCount int
|
||||
for n < int(cfg.MinEntropyInputLength) {
|
||||
m, err = entropySource.Read(seedMaterial[n:])
|
||||
n += m
|
||||
if err != nil {
|
||||
errCount++
|
||||
}
|
||||
if errCount > 3 {
|
||||
return cd, err
|
||||
}
|
||||
}
|
||||
seedMaterial = append(seedMaterial, nonce...)
|
||||
seedMaterial = append(seedMaterial, personalizationString...)
|
||||
seedMaterial = cd.df(seedMaterial, cd.seedLen())
|
||||
cd.update(seedMaterial)
|
||||
return cd, nil
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) update(seedMaterial []byte) {
|
||||
seedlen := cd.seedLen()
|
||||
temp := make([]byte, seedlen)
|
||||
n := 0
|
||||
block := cd.newBlock.New(cd.state.k)
|
||||
for n+cd.blockLen() < seedlen {
|
||||
internal.IncBytes(cd.state.v)
|
||||
block.Encrypt(temp[n:n+cd.blockLen()], cd.state.v)
|
||||
n += cd.blockLen()
|
||||
}
|
||||
xor.XorBytes(temp, temp, seedMaterial)
|
||||
copy(cd.state.k, temp)
|
||||
copy(cd.state.v, temp[cd.keyLen():])
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) Reseed(entropyInput, additionalInput []byte) {
|
||||
// cd.mu.Lock()
|
||||
// defer cd.mu.Unlock()
|
||||
|
||||
seedMaterial := make([]byte, len(entropyInput)+len(additionalInput))
|
||||
copy(seedMaterial, entropyInput)
|
||||
copy(seedMaterial[len(entropyInput):], additionalInput)
|
||||
seedMaterial = cd.df(seedMaterial, cd.seedLen())
|
||||
cd.update(seedMaterial)
|
||||
cd.state.reseedCounter = 1
|
||||
cd.state.lastReseedTime = time.Now()
|
||||
for i := 0; i < len(seedMaterial); i++ {
|
||||
seedMaterial[i] = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (cd *CtrDrng) Generate(b []byte, additionalInput []byte) error {
|
||||
// cd.mu.Lock()
|
||||
// defer cd.mu.Unlock()
|
||||
|
||||
if len(b) > cd.config.Outlen {
|
||||
return internal.ErrReturnedBitsTooLong
|
||||
}
|
||||
|
||||
// 需要重播种
|
||||
if cd.state.reseedCounter > uint32(cd.config.ReseedIntervalInCounter) ||
|
||||
time.Since(cd.state.lastReseedTime) > cd.config.ReseedIntervalInTime {
|
||||
return internal.ErrNeedReseed
|
||||
}
|
||||
|
||||
if len(additionalInput) > 0 {
|
||||
additionalInput = cd.df(additionalInput, cd.seedLen())
|
||||
cd.update(additionalInput)
|
||||
} else {
|
||||
additionalInput = make([]byte, cd.seedLen())
|
||||
}
|
||||
block := cd.newBlock.New(cd.state.k)
|
||||
internal.IncBytes(cd.state.v)
|
||||
outputBlock := make([]byte, block.BlockSize())
|
||||
block.Encrypt(outputBlock, cd.state.v)
|
||||
copy(b, outputBlock)
|
||||
cd.update(additionalInput)
|
||||
cd.state.reseedCounter++
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ctrrng
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
)
|
||||
|
||||
// func TestSm4CtrDrng(t *testing.T) {
|
||||
// rnd := make([]byte, 1280)
|
||||
// _, _ = Reader.Read(rnd)
|
||||
// fmt.Printf("%x\n", rnd)
|
||||
// }
|
||||
|
||||
// func TestSm4DrngRandomness(t *testing.T) {
|
||||
// // 测试Sm4Drng的随机性测试通过率
|
||||
// total := 10
|
||||
// pass := 0
|
||||
// for i := 0; i < total; i++ {
|
||||
// fmt.Println("======================================", i)
|
||||
// if statistics.SampleSelfTests(Reader) {
|
||||
// fmt.Printf("%d-th test: [O]\n", i)
|
||||
// pass += 1
|
||||
// } else {
|
||||
// fmt.Printf("%d-th test: [X]\n", i)
|
||||
// }
|
||||
// }
|
||||
|
||||
// fmt.Printf("Pass Rate: %d/%d\n", pass, total)
|
||||
// }
|
||||
@@ -0,0 +1,100 @@
|
||||
// package drng implements the SM3Rng and SM4Rng which specified in GM/T 0115.
|
||||
//
|
||||
// ctrrng and hashrng implements the low-level Drng interface.
|
||||
// Combine entropy(pool) and xxxrng we got the Drng as a ReadWriter.
|
||||
// The Read method generates random bits, and the Write method
|
||||
// add outter entropy into the Drng.
|
||||
//
|
||||
// Use drng.SM3Rng as an ordinary io.Reader to generate random bits.
|
||||
// If necessary, it could add extra entropy into the Drng.
|
||||
// For example, add user's scratch points on mobile, get random bits
|
||||
// from the cryptography server, etc..
|
||||
package drng
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/ctrrng"
|
||||
"xdx.jelly/xgcl/grand/drng/entropy"
|
||||
"xdx.jelly/xgcl/grand/drng/hashrng"
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
"xdx.jelly/xgcl/sm"
|
||||
)
|
||||
|
||||
// PERSONALIZATIONSTRING = "粤信签协同签名软件Android客户端密码模块YXQXTQM-Y"
|
||||
var PERSONALIZATIONSTRING = []byte("xdx")
|
||||
|
||||
// SM3Rng implements the io.ReadWriter interface.
|
||||
//
|
||||
// SM3Rng.Read reads random bytes from the DRNG.
|
||||
// SM3rng.Write add extra entropy to the DRNG.
|
||||
var SM3Rng io.ReadWriter
|
||||
var SM4Rng io.ReadWriter
|
||||
|
||||
func init() {
|
||||
raw, _ := hashrng.NewHashDrng(sm.SM3, hashrng.Config(internal.SecureLevel2))
|
||||
rng, _ := raw.Instantiate(PERSONALIZATIONSTRING, internal.Nonce(), entropy.GetEntropyPool())
|
||||
SM3Rng = &DrngUtil{
|
||||
Drng: rng,
|
||||
ReseedSource: entropy.GetEntropyPool(),
|
||||
}
|
||||
|
||||
raw, _ = ctrrng.NewCtrDrng(ctrrng.SM4BlockNewer{}, ctrrng.Config(internal.SecureLevel2))
|
||||
rng, _ = raw.Instantiate(PERSONALIZATIONSTRING, internal.Nonce(), entropy.GetEntropyPool())
|
||||
SM4Rng = &DrngUtil{
|
||||
Drng: rng,
|
||||
ReseedSource: entropy.GetEntropyPool(),
|
||||
}
|
||||
}
|
||||
|
||||
// DrngUtil implements the io.Reader interface
|
||||
type DrngUtil struct {
|
||||
mu sync.Mutex
|
||||
internal.Drng
|
||||
ReseedSource io.Reader // 重播种熵源
|
||||
|
||||
additionalInput io.Reader // TODO: 额外输入, 可为空
|
||||
}
|
||||
|
||||
// Write implements io.ReadWriter.
|
||||
// The returns can be ignored.
|
||||
func (du *DrngUtil) Write(p []byte) (n int, err error) {
|
||||
du.mu.Lock()
|
||||
defer du.mu.Unlock()
|
||||
|
||||
du.Drng.Reseed(p, nil)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (du *DrngUtil) Read(p []byte) (n int, err error) {
|
||||
du.mu.Lock()
|
||||
defer du.mu.Unlock()
|
||||
|
||||
outlen := du.OutLen()
|
||||
for len(p) > 0 {
|
||||
generateLen := len(p)
|
||||
if generateLen > outlen {
|
||||
generateLen = outlen
|
||||
}
|
||||
err := du.Generate(p[:generateLen], nil)
|
||||
if err != nil {
|
||||
// 需要重播种
|
||||
config := du.Config()
|
||||
entropyInput := make([]byte, config.MinEntropy)
|
||||
n, err := du.ReseedSource.Read(entropyInput)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
du.Drng.Reseed(entropyInput[:n], nil)
|
||||
err = du.Generate(p[:generateLen], nil)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
n += generateLen
|
||||
p = p[generateLen:]
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package drng_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xdx.jelly/xgcl/grand/drng"
|
||||
)
|
||||
|
||||
// 0.03GBps, 主要是internal.Outlen太小
|
||||
func TestSM3RngSpeed(t *testing.T) {
|
||||
data := make([]byte, 10*1024*1024)
|
||||
|
||||
start := time.Now()
|
||||
times := 10
|
||||
for i := 0; i < times; i++ {
|
||||
_, err := drng.SM3Rng.Read(data)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
end := time.Now()
|
||||
elapsed := end.Sub(start)
|
||||
t.Logf("Generate Random speed: %.2f MBps\n", float64(times*len(data))/float64(1024*1024)/elapsed.Seconds())
|
||||
}
|
||||
|
||||
func TestSM4RngSpeed(t *testing.T) {
|
||||
data := make([]byte, 10*1024*1024)
|
||||
|
||||
start := time.Now()
|
||||
times := 10
|
||||
for i := 0; i < times; i++ {
|
||||
_, err := drng.SM4Rng.Read(data)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
end := time.Now()
|
||||
elapsed := end.Sub(start)
|
||||
t.Logf("Generate Random speed: %.2f MBps\n", float64(times*len(data))/float64(1024*1024)/elapsed.Seconds())
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package cpu
|
||||
|
||||
//go:noescape
|
||||
func cpuid() uint64
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "textflag.h"
|
||||
|
||||
// __asm__("mrs %0, MIDR_EL1" : "=r"(arm_cpuid));
|
||||
TEXT ·cpuid(SB), NOSPLIT, $0
|
||||
MRS MIDR_EL1, R0
|
||||
MOVD R0, res+0(FP)
|
||||
RET
|
||||
@@ -0,0 +1,10 @@
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCPUID(t *testing.T) {
|
||||
fmt.Println(cpuid())
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package entropy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func MarkovTests(data []byte, verbose bool) float64 {
|
||||
// 将data转换为0,1比特串表示
|
||||
epsilon := make([]byte, 0, len(data)*8)
|
||||
for _, b := range data {
|
||||
epsilon = append(epsilon, (b>>7)&1)
|
||||
epsilon = append(epsilon, (b>>6)&1)
|
||||
epsilon = append(epsilon, (b>>5)&1)
|
||||
epsilon = append(epsilon, (b>>4)&1)
|
||||
epsilon = append(epsilon, (b>>3)&1)
|
||||
epsilon = append(epsilon, (b>>2)&1)
|
||||
epsilon = append(epsilon, (b>>1)&1)
|
||||
epsilon = append(epsilon, (b>>0)&1)
|
||||
}
|
||||
|
||||
// double markov_test(byte* data, long len, const int verbose, const char *label){
|
||||
var i, C_0, C_1, C_00, C_10 int64
|
||||
var H_min, tmp_min_entropy, P_0, P_1, P_00, P_01, P_10, P_11, entEst float64
|
||||
|
||||
C_0 = 0
|
||||
C_00 = 0
|
||||
C_10 = 0
|
||||
dataLen := int64(len(epsilon))
|
||||
// get counts for unconditional and transition probabilities
|
||||
for i = 0; i < dataLen-1; i++ {
|
||||
if epsilon[i] == 0 {
|
||||
C_0++
|
||||
if epsilon[i+1] == 0 {
|
||||
C_00++
|
||||
}
|
||||
} else if epsilon[i+1] == 0 {
|
||||
C_10++
|
||||
}
|
||||
}
|
||||
|
||||
//C_0 is now the number of 0 bits from S[0] to S[len-2]
|
||||
|
||||
C_1 = dataLen - 1 - C_0 //C_1 is the number of 1 bits from S[0] to S[len-2]
|
||||
|
||||
//Note that P_X1 = C_X1 / C_X = (C_X - C_X0)/C_X = 1.0 - C_X0/C_X = 1.0 - P_X0
|
||||
if C_0 > 0 {
|
||||
P_00 = float64(C_00) / float64(C_0)
|
||||
P_01 = 1.0 - P_00
|
||||
} else {
|
||||
P_00 = 0.0
|
||||
P_01 = 0.0
|
||||
}
|
||||
|
||||
if C_1 > 0 {
|
||||
P_10 = float64(C_10) / float64(C_1)
|
||||
P_11 = 1.0 - P_10
|
||||
} else {
|
||||
P_10 = 0.0
|
||||
P_11 = 0.0
|
||||
}
|
||||
|
||||
// account for the last symbol
|
||||
if epsilon[dataLen-1] == 0 {
|
||||
C_0++
|
||||
}
|
||||
//C_0 is now the number of 0 bits from S[0] to S[len-1]
|
||||
|
||||
P_0 = float64(C_0) / float64(dataLen)
|
||||
P_1 = 1.0 - P_0
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("P_0 = %.17g\n", P_0)
|
||||
fmt.Printf("P_1 = %.17g\n", P_1)
|
||||
fmt.Printf("P_{0,0} = %.17g\n", P_00)
|
||||
fmt.Printf("P_{0,1} = %.17g\n", P_01)
|
||||
fmt.Printf("P_{1,0} = %.17g\n", P_10)
|
||||
fmt.Printf("P_{1,1} = %.17g\n", P_11)
|
||||
}
|
||||
|
||||
H_min = 128.0
|
||||
|
||||
//In the next block, note that if P_0X > 0.0, then P_0 > 0.0
|
||||
//and similarly if P_1X > 0.0, then P_1 > 0.0
|
||||
|
||||
// Sequence 00...0
|
||||
if P_00 > 0.0 {
|
||||
tmp_min_entropy = -math.Log2(P_0) - 127.0*math.Log2(P_00)
|
||||
if tmp_min_entropy < H_min {
|
||||
H_min = tmp_min_entropy
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence 0101...01
|
||||
if (P_01 > 0.0) && (P_10 > 0.0) {
|
||||
tmp_min_entropy = -math.Log2(P_0) - 64.0*math.Log2(P_01) - 63.0*math.Log2(P_10)
|
||||
if tmp_min_entropy < H_min {
|
||||
H_min = tmp_min_entropy
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence 011...1
|
||||
if (P_01 > 0.0) && (P_11 > 0.0) {
|
||||
tmp_min_entropy = -math.Log2(P_0) - math.Log2(P_01) - 126.0*math.Log2(P_11)
|
||||
if tmp_min_entropy < H_min {
|
||||
H_min = tmp_min_entropy
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence 100...0
|
||||
if (P_10 > 0.0) && (P_00 > 0.0) {
|
||||
tmp_min_entropy = -math.Log2(P_1) - math.Log2(P_10) - 126.0*math.Log2(P_00)
|
||||
if tmp_min_entropy < H_min {
|
||||
H_min = tmp_min_entropy
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence 1010...10
|
||||
if (P_10 > 0.0) && (P_01 > 0.0) {
|
||||
tmp_min_entropy = -math.Log2(P_1) - 64.0*math.Log2(P_10) - 63.0*math.Log2(P_01)
|
||||
if tmp_min_entropy < H_min {
|
||||
H_min = tmp_min_entropy
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence 11...1
|
||||
if P_11 > 0.0 {
|
||||
tmp_min_entropy = -math.Log2(P_1) - 127.0*math.Log2(P_11)
|
||||
if tmp_min_entropy < H_min {
|
||||
H_min = tmp_min_entropy
|
||||
}
|
||||
}
|
||||
|
||||
entEst = math.Min(H_min/128.0, 1.0)
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("p_max = %.17g\n", math.Pow(2.0, -H_min))
|
||||
fmt.Printf("min entropy = %.17g\n", entEst)
|
||||
}
|
||||
|
||||
return entEst
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package entropy
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type OSEntropySource struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (e *OSEntropySource) GetEntropy(minEntropy int64, minEntropyInputLength int64, maxEntropyInputLength int64) ([]byte, error) {
|
||||
//使用互斥锁保证熵源的独占性
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
n := minEntropy
|
||||
if n < minEntropyInputLength {
|
||||
n = minEntropyInputLength
|
||||
}
|
||||
entropy := make([]byte, n)
|
||||
if _, err := rand.Reader.Read(entropy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entropy, nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package entropy
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/entropy/rdtsc"
|
||||
)
|
||||
|
||||
// SysTimeEntropySource 系统时间熵源
|
||||
type SysTimeEntropySource struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// GetEntropy
|
||||
func (e *SysTimeEntropySource) GetEntropy(minEntropy int64, minEntropyInputLength int64, maxEntropyInputLength int64) ([]byte, error) {
|
||||
//使用互斥锁保证熵源的独占性
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
// SysTimeEntropySource熵源的Markov测试min entropy ~ 0.58
|
||||
// 因此向熵源最少要取minEntropy / 0.58 ~ 2*minEntropy
|
||||
n := minEntropy << 1
|
||||
if n < minEntropyInputLength {
|
||||
n = minEntropyInputLength
|
||||
}
|
||||
entropy := make([]byte, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
// var b byte
|
||||
// for j := 0; j < 8; j++ {
|
||||
// t := rdtsc.GetCPUTimeStamp()
|
||||
// // xor of the the last two bits are used.
|
||||
// // for a rdtsc usually used 20~50 cycles.
|
||||
// // b |= byte((t^(t>>1)^(t>>2)^(t>>3))&1) << j
|
||||
// b |= byte(t&1) << j
|
||||
// }
|
||||
// entropy[i] = b
|
||||
|
||||
entropy[i] = byte(rdtsc.GetCPUTimeStamp())
|
||||
}
|
||||
return entropy, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package entropy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMarkovSimple(t *testing.T) {
|
||||
data := []byte{0b10001110, 0b01010101, 0b11001100, 0b01110010, 0b10101110}
|
||||
h := MarkovTests(data, false)
|
||||
if h-0.761 > 0.01 {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkov(t *testing.T) {
|
||||
// 系统熵源
|
||||
// entropy := OSEntropySource{}
|
||||
entropy := SysTimeEntropySource{}
|
||||
data, _ := entropy.GetEntropy(1000000/8, 1000000/8, 1000000/8)
|
||||
MarkovTests(data, false)
|
||||
}
|
||||
|
||||
func TestEntropyPoll(t *testing.T) {
|
||||
pool := &EntropyPool{}
|
||||
for i := 0; i < 100; i++ {
|
||||
pool.lshr(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
TEXT ·RDSeed(SB), NOSPLIT, $0
|
||||
MRS RNDR, R0
|
||||
MOVD R0, res+0(FP)
|
||||
RET
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package rdseed
|
||||
|
||||
const SupportRDSEED = true
|
||||
|
||||
//go:noescape
|
||||
func RDSeed() uint64
|
||||
@@ -0,0 +1,12 @@
|
||||
package rdseed
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRDSEED(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Printf("%x\n", RDSeed())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# In linux
|
||||
```
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static long long GetThreadCpuTimeNs() {
|
||||
struct timespec t;
|
||||
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t)) {
|
||||
perror("clock_gettime");
|
||||
return 0;
|
||||
}
|
||||
return t.tv_nsec;
|
||||
}
|
||||
```
|
||||
Makov测试熵估计0.5-0.6
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
TEXT ·GetCPUTimeStamp(SB), NOSPLIT, $0
|
||||
RDTSCP // save time stamp counter in DX:AX
|
||||
SHLQ $32, DX
|
||||
ORQ DX, AX
|
||||
MOVQ AX, res+0(FP)
|
||||
RET
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
TEXT ·GetCPUTimeStamp(SB), NOSPLIT, $0
|
||||
MRS CNTVCT_EL0, R0
|
||||
MOVD R0, res+0(FP)
|
||||
RET
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package rdtsc
|
||||
|
||||
const SupportRDTSC = true
|
||||
|
||||
//go:noescape
|
||||
func GetCPUTimeStamp() uint64
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !amd64 && !arm64
|
||||
|
||||
package rdtsc
|
||||
|
||||
const SupportRDTSC = false
|
||||
|
||||
func GetCPUTimeStamp() uint64 {
|
||||
panic("unsupported")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package rdtsc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRDTSC(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Printf("%x\n", GetCPUTimeStamp())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package hashrng
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
)
|
||||
|
||||
var defaultConfig1 = &internal.DrngConfig{
|
||||
Seedlen: 440 >> 3,
|
||||
Outlen: 256 >> 3,
|
||||
ReseedIntervalInTime: time.Second * 600,
|
||||
ReseedIntervalInCounter: 1 << 20,
|
||||
MinEntropy: 256 >> 3,
|
||||
MinEntropyInputLength: 256 >> 3,
|
||||
MaxEntropyInputLength: (1 << 35) >> 3,
|
||||
}
|
||||
|
||||
// 安全级别2的配置
|
||||
var defaultConfig2 = &internal.DrngConfig{
|
||||
Seedlen: 440 >> 3,
|
||||
Outlen: 256 >> 3,
|
||||
ReseedIntervalInTime: time.Second * 60,
|
||||
ReseedIntervalInCounter: 1 << 10,
|
||||
MinEntropy: 256 >> 3,
|
||||
MinEntropyInputLength: 256 >> 3,
|
||||
MaxEntropyInputLength: (1 << 35) >> 3,
|
||||
}
|
||||
|
||||
func Config(level internal.SecureLevel) *internal.DrngConfig {
|
||||
switch level {
|
||||
case internal.SecureLevel1:
|
||||
return defaultConfig1
|
||||
case internal.SecureLevel2:
|
||||
return defaultConfig2
|
||||
default:
|
||||
panic("unsupported secure level")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package hashrng
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
|
||||
"xdx.jelly/xgcl/sm"
|
||||
"xdx.jelly/xgcl/sm/sm3"
|
||||
)
|
||||
|
||||
var _ = sm3.BlockSize // import sm3
|
||||
|
||||
// 内部状态
|
||||
type internalState struct {
|
||||
v []byte
|
||||
c []byte
|
||||
|
||||
reseedCounter uint32
|
||||
lastReseedTime time.Time
|
||||
}
|
||||
|
||||
// zeromize 对内部状态置0
|
||||
func (s *internalState) zeromize() {
|
||||
for i := 0; i < len(s.v); i++ {
|
||||
s.v[i] = 0
|
||||
}
|
||||
|
||||
for i := 0; i < len(s.c); i++ {
|
||||
s.c[i] = 0
|
||||
}
|
||||
|
||||
s.reseedCounter = 0
|
||||
}
|
||||
|
||||
type HashDrng struct {
|
||||
// mu sync.Mutex
|
||||
config internal.DrngConfig
|
||||
hash sm.Hash
|
||||
df func(input []byte, n int) []byte
|
||||
state internalState
|
||||
personalizationString []byte //个性化字符串,来自应用的比特串
|
||||
}
|
||||
|
||||
// Read implements internal.internal.
|
||||
// func (hd *HashDrng) Read(p []byte) (n int, err error) {
|
||||
// return len(p), hd.Generate(p, nil)
|
||||
// }
|
||||
|
||||
// var SM3_df = newDF(sm.SM3)
|
||||
|
||||
// newDF 返回一个使用hash作为杂凑的df函数
|
||||
// 例:SM3_df = newDF(sm.SM3)
|
||||
func newDF(hash sm.Hash) func(input []byte, n int) []byte {
|
||||
h := hash.New()
|
||||
return func(input []byte, n int) []byte {
|
||||
out := make([]byte, 0, n+hash.Size()-1)
|
||||
len := (n + hash.Size() - 1) / hash.Size()
|
||||
counter := uint32(1)
|
||||
buf := make([]byte, 8)
|
||||
for i := 0; i < len; i++ {
|
||||
binary.BigEndian.PutUint32(buf, counter)
|
||||
binary.BigEndian.PutUint32(buf[4:], uint32(n<<3))
|
||||
h.Write(buf)
|
||||
h.Write(input)
|
||||
out = h.Sum(out)
|
||||
h.Reset()
|
||||
counter += 1
|
||||
}
|
||||
return out[:n]
|
||||
}
|
||||
}
|
||||
|
||||
// func New(hash sm.Hash, config *internal.DrngConfig) (internal.Drng, error) {
|
||||
// raw, _ := NewHashDrng(hash, config)
|
||||
// pool := entropy.GetEntropyPool()
|
||||
// d, err := raw.Instantiate([]byte(internal.PERSONALIZATIONSTRING), internal.Nonce(), pool)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return &drng.DrngUtil{
|
||||
// Drng: d,
|
||||
// ReseedSource: pool,
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
func NewHashDrng(hash sm.Hash, config *internal.DrngConfig) (internal.RawDrng, error) {
|
||||
hd := &HashDrng{
|
||||
hash: hash,
|
||||
df: newDF(hash),
|
||||
config: *config,
|
||||
}
|
||||
|
||||
// HashDrng 析构时对内部状态置0
|
||||
runtime.SetFinalizer(hd, func(hd *HashDrng) {
|
||||
hd.state.zeromize()
|
||||
})
|
||||
return hd, nil
|
||||
}
|
||||
|
||||
func (hd *HashDrng) OutLen() int {
|
||||
return hd.config.Outlen
|
||||
}
|
||||
|
||||
func (hd *HashDrng) Config() *internal.DrngConfig {
|
||||
return &hd.config
|
||||
}
|
||||
|
||||
// Instantiate 初始化HashDrng
|
||||
func (hd *HashDrng) Instantiate(personalizationString []byte, nonce []byte, entropySource io.Reader) (internal.Drng, error) {
|
||||
// hd.mu.Lock()
|
||||
// defer hd.mu.Unlock()
|
||||
|
||||
cfg := hd.config
|
||||
hd.personalizationString = personalizationString
|
||||
|
||||
seedMaterial := make([]byte, 2*cfg.MinEntropyInputLength, int(2*cfg.MinEntropyInputLength)+len(nonce)+len(personalizationString))
|
||||
var m, n int
|
||||
var err error
|
||||
var errCount int
|
||||
for n < int(cfg.MinEntropyInputLength) {
|
||||
m, err = entropySource.Read(seedMaterial[n:])
|
||||
n += m
|
||||
if err != nil {
|
||||
errCount++
|
||||
}
|
||||
if errCount > 3 {
|
||||
return hd, err
|
||||
}
|
||||
}
|
||||
seedMaterial = append(seedMaterial, nonce...)
|
||||
seedMaterial = append(seedMaterial, personalizationString...)
|
||||
seed := hd.df(seedMaterial, cfg.Seedlen)
|
||||
|
||||
//初始化内部状态变量
|
||||
hd.state.v = append(hd.state.v[:0], seed...)
|
||||
hd.state.c = append(hd.state.c[:0], hd.df(append([]byte{0}, seed...), cfg.Seedlen)...)
|
||||
hd.state.reseedCounter = 1
|
||||
hd.state.lastReseedTime = time.Now()
|
||||
|
||||
// seed和seedMaterial置0
|
||||
for i := 0; i < len(seed); i++ {
|
||||
seed[i] = 0
|
||||
}
|
||||
for i := 0; i < len(seedMaterial); i++ {
|
||||
seedMaterial[i] = 0
|
||||
}
|
||||
return hd, nil
|
||||
}
|
||||
|
||||
// Reseed 更新HashDrng的内部状态
|
||||
func (hd *HashDrng) Reseed(entropyInput, additionalInput []byte) {
|
||||
// hd.mu.Lock()
|
||||
// defer hd.mu.Unlock()
|
||||
|
||||
seedMaterial := make([]byte, 1, 1+len(entropyInput)+len(hd.state.v)+len(additionalInput))
|
||||
seedMaterial[0] = 1
|
||||
seedMaterial = append(seedMaterial, entropyInput...)
|
||||
seedMaterial = append(seedMaterial, hd.state.v...)
|
||||
seedMaterial = append(seedMaterial, additionalInput...)
|
||||
|
||||
seed := hd.df(seedMaterial, hd.config.Seedlen)
|
||||
copy(hd.state.v, seed)
|
||||
hd.state.c = append(hd.state.c[:0], hd.df(append([]byte{0}, seed...), hd.config.Seedlen)...)
|
||||
hd.state.reseedCounter = 1
|
||||
hd.state.lastReseedTime = time.Now()
|
||||
|
||||
// seed和seedMaterial置0
|
||||
for i := 0; i < len(seed); i++ {
|
||||
seed[i] = 0
|
||||
}
|
||||
for i := 0; i < len(seedMaterial); i++ {
|
||||
seedMaterial[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Generate 输出函数
|
||||
func (hd *HashDrng) Generate(b []byte, additionalInput []byte) error {
|
||||
// hd.mu.Lock()
|
||||
// defer hd.mu.Unlock()
|
||||
|
||||
if len(b) > hd.config.Outlen {
|
||||
return internal.ErrReturnedBitsTooLong
|
||||
}
|
||||
|
||||
// 需要重播种
|
||||
if hd.state.reseedCounter > uint32(hd.config.ReseedIntervalInCounter) ||
|
||||
time.Since(hd.state.lastReseedTime) > hd.config.ReseedIntervalInTime {
|
||||
return internal.ErrNeedReseed
|
||||
}
|
||||
hash := hd.hash.New()
|
||||
if len(additionalInput) > 0 {
|
||||
hash.Write([]byte{2})
|
||||
hash.Write(hd.state.v)
|
||||
hash.Write(additionalInput)
|
||||
W := hash.Sum(nil)
|
||||
internal.AddModBytes(hd.state.v, W)
|
||||
}
|
||||
hash.Reset()
|
||||
_, _ = hash.Write(hd.state.v)
|
||||
temp := hash.Sum(nil)
|
||||
copy(b, temp)
|
||||
hash.Reset()
|
||||
hash.Write([]byte{3})
|
||||
hash.Write(hd.state.v)
|
||||
H := hash.Sum(nil)
|
||||
|
||||
reseedCounterBytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(reseedCounterBytes, hd.state.reseedCounter)
|
||||
|
||||
internal.AddModBytes(hd.state.v, H[:])
|
||||
internal.AddModBytes(hd.state.v, hd.state.c)
|
||||
internal.AddModBytes(hd.state.v, reseedCounterBytes)
|
||||
hd.state.reseedCounter++
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package hashrng
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/entropy"
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
"xdx.jelly/xgcl/sm"
|
||||
)
|
||||
|
||||
func TestSysEntropySource(t *testing.T) {
|
||||
e := entropy.GetEntropyPool()
|
||||
b, err := e.GetEntropy(128, 128, 128)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%x\n", b)
|
||||
}
|
||||
|
||||
func TestSm3Df(t *testing.T) {
|
||||
input := make([]byte, 10)
|
||||
for n := 0; n < 100; n++ {
|
||||
output := newDF(sm.SM3)(input, n)
|
||||
if len(output) != n {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0.03GBps, 主要是internal.Outlen太小
|
||||
func TestDrngSpeed(t *testing.T) {
|
||||
raw, _ := NewHashDrng(sm.SM3, Config(internal.SecureLevel2))
|
||||
rng, _ := raw.Instantiate(nil, internal.Nonce(), entropy.GetEntropyPool())
|
||||
msg := make([]byte, rng.OutLen())
|
||||
|
||||
start := time.Now()
|
||||
times := 10000
|
||||
buf := make([]byte, 32)
|
||||
for i := 0; i < times; i++ {
|
||||
if err := rng.Generate(msg, nil); err == internal.ErrNeedReseed {
|
||||
entropy.GetEntropyPool().Read(buf)
|
||||
rng.Reseed(buf, nil)
|
||||
} else if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
elapsed := end.Sub(start)
|
||||
t.Logf("Generate Random speed: %.2f MBps\n", float64(times*len(msg))/float64(1024*1024)/elapsed.Seconds())
|
||||
}
|
||||
|
||||
// func TestSm3DrngRandomness(t *testing.T) {
|
||||
// // 测试Sm3Drng的随机性测试通过率
|
||||
// total := 10
|
||||
// pass := 0
|
||||
// for i := 0; i < total; i++ {
|
||||
// fmt.Println("======================================", i)
|
||||
// if statistics.SampleSelfTests(Reader) {
|
||||
// fmt.Printf("%d-th test: [O]\n", i)
|
||||
// pass += 1
|
||||
// } else {
|
||||
// fmt.Printf("%d-th test: [X]\n", i)
|
||||
// }
|
||||
// }
|
||||
|
||||
// fmt.Printf("Pass Rate: %d/%d\n", pass, total)
|
||||
// }
|
||||
@@ -0,0 +1,24 @@
|
||||
package hashrng
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
"xdx.jelly/xgcl/sm"
|
||||
)
|
||||
|
||||
// 随机数发生器的已知答案检测
|
||||
func KAT() bool {
|
||||
raw, _ := NewHashDrng(sm.SM3, Config(internal.SecureLevel2))
|
||||
nonce, _ := hex.DecodeString("0001020304050607")
|
||||
entropy, _ := hex.DecodeString("C4F7D581BEFEF25C8BBB6DAD52A6AB8234FA7DB7A988592BC592DAF2BE630647")
|
||||
rng, _ := raw.Instantiate(nil, nonce, bytes.NewReader(entropy))
|
||||
additionalInput, _ := hex.DecodeString("00010203040506")
|
||||
|
||||
rand := make([]byte, 32)
|
||||
rng.Generate(rand, additionalInput)
|
||||
|
||||
wanted, _ := hex.DecodeString("a6e3c0ad539fa0c211b23e3aa7c3b92482bfc77fcb9864690e832bcda4357046")
|
||||
return bytes.Compare(rand, wanted) == 0
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package drng
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
|
||||
"xdx.jelly/xgcl/grand/drng/entropy"
|
||||
"xdx.jelly/xgcl/grand/drng/internal"
|
||||
)
|
||||
|
||||
const ALPHA = 9.5367431640625e-07 // = 2^(-20)
|
||||
|
||||
func next(source entropy.EntropySource) []byte {
|
||||
b, err := source.GetEntropy(internal.MinEntropyInputLength, internal.MinEntropyInputLength, internal.MaxEntropyInputLength)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 连续健康测试
|
||||
func RepeatCounterHealthyTests(source entropy.EntropySource, n int, H int) bool {
|
||||
C := 1 + int64(math.Ceil(-math.Log2(ALPHA)/float64(H)))
|
||||
A := next(source)
|
||||
if A == nil {
|
||||
return false
|
||||
}
|
||||
B := int64(1)
|
||||
for i := 0; i < n; i++ {
|
||||
X := next(source)
|
||||
if X == nil {
|
||||
return false
|
||||
}
|
||||
if bytes.Equal(X, A) {
|
||||
B++
|
||||
}
|
||||
if B >= C {
|
||||
return false
|
||||
} else {
|
||||
A = X
|
||||
B = 1
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MinEntropyInputLength = 256 >> 3 // 最小熵阈值256比特
|
||||
MaxEntropyInputLength = (1 << 35) >> 3 // 最大熵阈值2^35比特
|
||||
|
||||
)
|
||||
|
||||
// 可能返回的错误
|
||||
var (
|
||||
ErrNeedReseed = errors.New("[drng] - need reseed")
|
||||
ErrReturnedBitsTooLong = errors.New("[drng] - requested number of bits too long")
|
||||
ErrEntropyLength = errors.New("[drng] - entropy length too short or too long")
|
||||
)
|
||||
|
||||
// 安全级别
|
||||
type SecureLevel uint
|
||||
|
||||
const (
|
||||
SecureLevel1 SecureLevel = 1 + iota
|
||||
SecureLevel2
|
||||
)
|
||||
|
||||
type DrngConfig struct {
|
||||
Seedlen int
|
||||
Outlen int
|
||||
ReseedIntervalInTime time.Duration // 重播种时间阈值
|
||||
ReseedIntervalInCounter int // 重播种计数器阈值
|
||||
MinEntropy int64
|
||||
MinEntropyInputLength int64
|
||||
MaxEntropyInputLength int64
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNonceCounter(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10000; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 10000; i++ {
|
||||
nonceCounter.LoadAndAdd(1)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
assert.Equal(t, nonceCounter.n, uint64(10000)*10000)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// RawDrng 未初始化状态,初始化后返回Drng状态
|
||||
// Note: 状态模式
|
||||
type RawDrng interface {
|
||||
// Instantiate 初始化,personalization_string为可选,返回成功则内部状态初始化完成。
|
||||
Instantiate(personalizationString []byte, nonce []byte, entropySource io.Reader) (Drng, error)
|
||||
}
|
||||
|
||||
// Drng is the low level interface for DRNG.
|
||||
//
|
||||
// For most users, use DrngUtil instead.
|
||||
type Drng interface {
|
||||
// Reseed 重播种,entropyInput为输入熵
|
||||
// additionalInput为来自应用的比特串,可以为nil
|
||||
Reseed(entropyInput, additionalInput []byte)
|
||||
|
||||
// Generate 将输出缓冲区b用随机数填充,additionalInputt为来自应用的比特串,可为nil
|
||||
Generate(b []byte, additionalInput []byte) error
|
||||
|
||||
// 每次调用输出的最大字节数(32)
|
||||
OutLen() int
|
||||
|
||||
Config() *DrngConfig
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"xdx.jelly/xgcl/internal"
|
||||
)
|
||||
|
||||
// var nonceCounter atomic.Int64
|
||||
|
||||
// atomic.Int64 only available in go 1.19, so we make a new one.
|
||||
type atomicUint64 struct {
|
||||
spin internal.SpinLock
|
||||
n uint64
|
||||
}
|
||||
|
||||
var nonceCounter atomicUint64
|
||||
|
||||
func (a *atomicUint64) LoadAndAdd(n uint64) uint64 {
|
||||
a.spin.Lock()
|
||||
defer a.spin.Unlock()
|
||||
v := a.n
|
||||
a.n += n
|
||||
return v
|
||||
}
|
||||
|
||||
// nonce 由时间戳和单调递增的计数器组成
|
||||
func Nonce() []byte {
|
||||
out := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(out, uint64(time.Now().UnixNano()))
|
||||
binary.BigEndian.PutUint64(out[8:], nonceCounter.LoadAndAdd(1))
|
||||
return out
|
||||
}
|
||||
|
||||
// AddModBytes a = a+b as integer, a,b are bigendian and len(a) >= len(b).
|
||||
func AddModBytes(a, b []byte) {
|
||||
var x uint
|
||||
|
||||
x = 0
|
||||
i := len(a) - 1
|
||||
j := len(b) - 1
|
||||
for j >= 0 {
|
||||
x += uint(a[i]) + uint(b[j])
|
||||
a[i] = byte(x)
|
||||
x = x >> 8
|
||||
j--
|
||||
i--
|
||||
}
|
||||
if x == 0 {
|
||||
return
|
||||
}
|
||||
for i >= 0 {
|
||||
a[i] += 1
|
||||
if a[i] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func IncBytes(a []byte) {
|
||||
i := len(a) - 1
|
||||
for i >= 0 {
|
||||
a[i]++
|
||||
if a[i] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package grand
|
||||
|
||||
import "xdx.jelly/xgcl/gerrors"
|
||||
|
||||
//go:generate stringer -type=ErrorCode -linecomment -output=errors_string.go errors.go
|
||||
type ErrorCode gerrors.ErrorCode
|
||||
|
||||
func (e ErrorCode) Error() string {
|
||||
return gerrors.Format(uint32(e), e.String())
|
||||
}
|
||||
|
||||
// error codes
|
||||
const (
|
||||
ErrGenerateRandomFailed ErrorCode = gerrors.XDX_JELLY_GCL_GRAND + iota //随机数生成失败
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
// Code generated by "stringer -type=ErrorCode -linecomment -output=errors_string.go errors.go"; DO NOT EDIT.
|
||||
|
||||
package grand
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ErrGenerateRandomFailed-16850944]
|
||||
}
|
||||
|
||||
const _ErrorCode_name = "随机数生成失败"
|
||||
|
||||
var _ErrorCode_index = [...]uint8{0, 21}
|
||||
|
||||
func (i ErrorCode) String() string {
|
||||
i -= 16850944
|
||||
if i >= ErrorCode(len(_ErrorCode_index)-1) {
|
||||
return "ErrorCode(" + strconv.FormatInt(int64(i+16850944), 10) + ")"
|
||||
}
|
||||
return _ErrorCode_name[_ErrorCode_index[i]:_ErrorCode_index[i+1]]
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
///
|
||||
/// Copyright (c) 2018 xdx. All rights reserved.
|
||||
///
|
||||
/// \file: rand.go
|
||||
///
|
||||
/// \brief: The default random number generators of grand
|
||||
///
|
||||
/// \author: xdx
|
||||
///
|
||||
|
||||
package grand
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"xdx.jelly/xgcl/gerrors"
|
||||
)
|
||||
|
||||
// Reader re-export crypto/rand.Reader. It the default Random number generator of GCL.
|
||||
var Reader = rand.Reader
|
||||
|
||||
// ReaderZero is a Reader which output 000...
|
||||
// Note: only for testing
|
||||
var ReaderZero = ConstantReader(0)
|
||||
|
||||
// ReaderOne is a Reader which output 111...
|
||||
// Note: only for testing
|
||||
var ReaderOne = ConstantReader(1)
|
||||
|
||||
var ReaderTwo = ConstantReader(2)
|
||||
|
||||
// ConstantReader is a reader which outputs constant byte squence.
|
||||
type ConstantReader byte
|
||||
|
||||
// Read implements the io.Reader interface.
|
||||
func (r ConstantReader) Read(b []byte) (int, error) {
|
||||
for i := range b {
|
||||
b[i] = byte(r)
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Read is a helper function for grand.Reader.
|
||||
func Read(b []byte) (n int, err error) {
|
||||
return Reader.Read(b)
|
||||
}
|
||||
|
||||
// GenerateRandom is the same as Read.
|
||||
var GenerateRandom = Read
|
||||
|
||||
// GetRandom tries to get a random bytes slice of length.
|
||||
// The return []byte may have a smaller length.
|
||||
func GetRandom(length int) []byte {
|
||||
buf := make([]byte, length)
|
||||
n, _ := Read(buf)
|
||||
return buf[:n]
|
||||
}
|
||||
|
||||
// GenerateRandomExact generates a random slice of length exactly n, or returns error
|
||||
func GenerateRandomExact(n int, r io.Reader) ([]byte, error) {
|
||||
res := make([]byte, n)
|
||||
m, err := r.Read(res)
|
||||
if m < n {
|
||||
return nil, gerrors.WithAnnotating(ErrGenerateRandomFailed, err.Error())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Int generate a random big.Int of bits bits
|
||||
func Int(bits int) *big.Int {
|
||||
if bits <= 0 {
|
||||
return new(big.Int)
|
||||
}
|
||||
|
||||
// n = 2^bits
|
||||
n := big.NewInt(1)
|
||||
n.Lsh(n, uint(bits))
|
||||
|
||||
m, _ := rand.Int(Reader, n)
|
||||
m.SetBit(m, bits-1, 1)
|
||||
return m
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Deprecated functions //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// NewReaderFromBytes wraps data to a reader, the return reader could
|
||||
// read at most len(data) bytes.
|
||||
// Note: when return an error, the reader may have read n bytes.
|
||||
//
|
||||
// Deprecated: use bytes.NewReader instead.
|
||||
func NewReaderFromBytes(data []byte) io.Reader {
|
||||
return bytes.NewReader(data)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package grand_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"xdx.jelly/xgcl/grand"
|
||||
)
|
||||
|
||||
func TestRandZero(t *testing.T) {
|
||||
randReader := grand.ReaderZero
|
||||
buf := make([]byte, 100)
|
||||
n, err := randReader.Read(buf)
|
||||
if err != nil || n != 100 {
|
||||
t.Log(n, err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandFromBytes(t *testing.T) {
|
||||
buf := make([]byte, 100)
|
||||
_, _ = grand.ReaderOne.Read(buf)
|
||||
fmt.Println(buf)
|
||||
randReader := bytes.NewReader(buf)
|
||||
|
||||
buf2 := make([]byte, 51)
|
||||
n, err := randReader.Read(buf2)
|
||||
fmt.Println(n, err, buf2)
|
||||
if err != nil || n != len(buf2) {
|
||||
t.Log(n, err)
|
||||
t.Fail()
|
||||
}
|
||||
for _, b := range buf2 {
|
||||
if b != 1 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
n, err = randReader.Read(buf2)
|
||||
fmt.Println(n, err, buf2)
|
||||
if err != nil || n != len(buf2) {
|
||||
t.Log(n, err)
|
||||
t.Fail()
|
||||
}
|
||||
for _, b := range buf2 {
|
||||
if b != 1 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
for bits := 0; bits < 10000; bits++ {
|
||||
n := grand.Int(bits)
|
||||
if n.BitLen() != bits {
|
||||
t.Fatalf("bits = %d, n = %x", bits, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package grand
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// MustRead tries to fill p with len(p) bytes from r. If it haven't read enough bytes, then an
|
||||
// error returns. So if nil error returns, then p must have read len(p) bytes. Otherwise, only
|
||||
// p[:n] are read from r.
|
||||
func MustRead(r io.Reader, p []byte) (int, error) {
|
||||
n := 0
|
||||
for len(p) > 0 {
|
||||
m, err := r.Read(p)
|
||||
if err != nil {
|
||||
return n + m, err
|
||||
}
|
||||
p = p[m:]
|
||||
n += m
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
Reference in New Issue
Block a user