init: v1.0.0

This commit is contained in:
yaole
2026-05-27 23:03:00 +08:00
commit 8d97f750eb
466 changed files with 80067 additions and 0 deletions
+38
View File
@@ -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")
}
}
+245
View File
@@ -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
}
+28
View File
@@ -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)
// }
+100
View File
@@ -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
}
+38
View File
@@ -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())
}
+4
View File
@@ -0,0 +1,4 @@
package cpu
//go:noescape
func cpuid() uint64
+7
View File
@@ -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
+10
View File
@@ -0,0 +1,10 @@
package cpu
import (
"fmt"
"testing"
)
func TestCPUID(t *testing.T) {
fmt.Println(cpuid())
}
+142
View File
@@ -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
}
+179
View File
@@ -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
}
+27
View File
@@ -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
}
+41
View File
@@ -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
}
+28
View File
@@ -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)
}
}
+7
View File
@@ -0,0 +1,7 @@
#include "textflag.h"
TEXT ·RDSeed(SB), NOSPLIT, $0
MRS RNDR, R0
MOVD R0, res+0(FP)
RET
+9
View File
@@ -0,0 +1,9 @@
//go:build amd64 || arm64
// +build amd64 arm64
package rdseed
const SupportRDSEED = true
//go:noescape
func RDSeed() uint64
+12
View File
@@ -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())
}
}
+17
View File
@@ -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
+10
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
#include "textflag.h"
TEXT ·GetCPUTimeStamp(SB), NOSPLIT, $0
MRS CNTVCT_EL0, R0
MOVD R0, res+0(FP)
RET
+9
View File
@@ -0,0 +1,9 @@
//go:build amd64 || arm64
// +build amd64 arm64
package rdtsc
const SupportRDTSC = true
//go:noescape
func GetCPUTimeStamp() uint64
+9
View File
@@ -0,0 +1,9 @@
//go:build !amd64 && !arm64
package rdtsc
const SupportRDTSC = false
func GetCPUTimeStamp() uint64 {
panic("unsupported")
}
+12
View File
@@ -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())
}
}
+39
View File
@@ -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")
}
}
+219
View File
@@ -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
}
+71
View File
@@ -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)
// }
+24
View File
@@ -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
}
+45
View File
@@ -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
}
+37
View File
@@ -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
}
+23
View File
@@ -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)
}
+29
View File
@@ -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
}
+69
View File
@@ -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
}
}
}