246 lines
5.7 KiB
Go
246 lines
5.7 KiB
Go
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
|
|
}
|