implement ctr drbg

This commit is contained in:
Sun Yimin 2022-10-20 11:30:20 +08:00 committed by GitHub
parent f723aa8c66
commit 643df78c07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 585 additions and 57 deletions

61
drbg/common.go Normal file
View File

@ -0,0 +1,61 @@
package drbg
import (
"time"
)
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10
const DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20
const DRBG_RESEED_TIME_INTERVAL_LEVEL2 = time.Duration(60) * time.Second
const DRBG_RESEED_TIME_INTERVAL_LEVEL1 = time.Duration(600) * time.Second
const MAX_BYTES = 1 << 27
type SecurityLevel byte
const (
SECURITY_LEVEL_ONE SecurityLevel = 0x01
SECURITY_LEVEL_TWO SecurityLevel = 0x02
)
// DRBG interface for both hash and ctr drbg implementations
type DRBG interface {
// check internal state, return if reseed required
NeedReseed() bool
// reseed process
Reseed(entropy, additional []byte) error
// generate requrested bytes to b
Generate(b, additional []byte) error
}
type BaseDrbg struct {
v []byte
seedLength int
reseedTime time.Time
reseedIntervalInTime time.Duration
reseedCounter uint64
reseedIntervalInCounter uint64
securityLevel SecurityLevel
gm bool
}
func (hd *BaseDrbg) NeedReseed() bool {
return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime)
}
func add(left, right []byte, len int) {
var temp uint16 = 0
for i := len - 1; i >= 0; i-- {
temp += uint16(left[i]) + uint16(right[i])
right[i] = byte(temp & 0xff)
temp >>= 8
}
}
func addOne(data []byte, len int) {
var temp uint16 = 1
for i := len - 1; i >= 0; i-- {
temp += uint16(data[i])
data[i] = byte(temp & 0xff)
temp >>= 8
}
}

201
drbg/ctr_drbg.go Normal file
View File

@ -0,0 +1,201 @@
package drbg
import (
"crypto/cipher"
"encoding/binary"
"errors"
"time"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/sm4"
)
type CtrDrbg struct {
BaseDrbg
cipherProvider func(key []byte) (cipher.Block, error)
key []byte
keyLen int
}
// NewCtrDrbg create one CTR DRBG instance
func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
hd := &CtrDrbg{}
hd.gm = gm
hd.securityLevel = securityLevel
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL1
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL1
if hd.securityLevel == SECURITY_LEVEL_TWO {
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL2
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL2
}
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || len(entropy) >= MAX_BYTES {
return nil, errors.New("invalid entropy length")
}
// here for the min length, we just check <=0 now
if len(nonce) <= 0 || len(nonce) >= MAX_BYTES>>1 {
return nil, errors.New("invalid nonce length")
}
if len(personalization) >= MAX_BYTES {
return nil, errors.New("personalization is too long")
}
hd.cipherProvider = cipherProvider
hd.keyLen = keyLen
temp := make([]byte, hd.keyLen)
block, err := cipherProvider(temp)
if err != nil {
return nil, err
}
hd.seedLength = block.BlockSize() + keyLen
hd.v = make([]byte, block.BlockSize())
hd.key = make([]byte, hd.keyLen)
seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], nonce)
copy(seedMaterial[len(entropy)+len(nonce):], personalization)
seed := hd.derive(seedMaterial, hd.seedLength)
hd.update(seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTCtrDrbg create one CTR DRBG implementation which follows NIST standard
func NewNISTCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
return NewCtrDrbg(cipherProvider, keyLen, securityLevel, false, entropy, nonce, personalization)
}
// NewGMCtrDrbg create one CTR DRBG implementation which follows GM/T 0105-2021 standard
func NewGMCtrDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
return NewCtrDrbg(sm4.NewCipher, 16, securityLevel, true, entropy, nonce, personalization)
}
func (hd *CtrDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) <= 0 || len(entropy) >= MAX_BYTES {
return errors.New("invalid entropy length")
}
if len(additional) >= MAX_BYTES {
return errors.New("additional input too long")
}
var seedMaterial []byte
if len(additional) == 0 {
seedMaterial = entropy
} else {
seedMaterial = make([]byte, len(entropy)+len(additional))
copy(seedMaterial, entropy)
copy(seedMaterial[len(entropy):], additional)
}
seed := hd.derive(seedMaterial, hd.seedLength)
hd.update(seed)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
}
func (hd *CtrDrbg) newBlockCipher(key []byte) cipher.Block {
block, err := hd.cipherProvider(key)
if err != nil {
panic(err)
}
return block
}
// Generate CTR DRBG generate process.
func (hd *CtrDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
return errors.New("reseed reuqired")
}
outlen := len(hd.v)
if (hd.gm && len(b) > outlen) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
return errors.New("too many bytes requested")
}
if len(additional) > 0 {
additional = hd.derive(additional, hd.seedLength)
hd.update(additional)
}
block := hd.newBlockCipher(hd.key)
temp := make([]byte, outlen)
m := len(b)
limit := uint64(m+outlen-1) / uint64(outlen)
for i := 0; i < int(limit); i++ {
addOne(hd.v, outlen)
block.Encrypt(temp, hd.v)
copy(b[i*outlen:], temp)
}
hd.update(additional)
hd.reseedCounter++
return nil
}
func (cd *CtrDrbg) update(seedMaterial []byte) {
temp := make([]byte, cd.seedLength)
block := cd.newBlockCipher(cd.key)
outlen := block.BlockSize()
v := make([]byte, outlen)
output := make([]byte, outlen)
copy(v, cd.v)
for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ {
addOne(v, outlen)
block.Encrypt(output, v)
copy(temp[i*outlen:], output)
}
subtle.XORBytes(temp, temp, seedMaterial)
copy(cd.key, temp)
copy(cd.v, temp[cd.keyLen:])
}
func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
outlen := cd.seedLength - cd.keyLen
lenS := ((4 + 4 + len(seedMaterial) + outlen) / outlen) * outlen
S := make([]byte, lenS+outlen)
binary.BigEndian.PutUint32(S[outlen:], uint32(len(seedMaterial)))
binary.BigEndian.PutUint32(S[outlen+4:], uint32(returnBytes))
copy(S[outlen+8:], seedMaterial)
S[outlen+8+len(seedMaterial)] = 0x80
key := make([]byte, cd.keyLen)
for i := 0; i < cd.keyLen; i++ {
key[i] = byte(i)
}
blocks := (cd.seedLength + outlen - 1) / outlen
temp := make([]byte, blocks*outlen)
block := cd.newBlockCipher(key)
for i := 0; i < blocks; i++ {
binary.BigEndian.PutUint32(S, uint32(i))
copy(temp[i*outlen:], cd.bcc(block, S))
}
key = temp[:cd.keyLen]
X := temp[cd.keyLen:cd.seedLength]
temp = make([]byte, returnBytes)
block = cd.newBlockCipher(key)
for i := 0; i < (returnBytes+outlen-1)/outlen; i++ {
block.Encrypt(X, X)
copy(temp[i*outlen:], X)
}
return temp
}
func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte {
chainingValue := make([]byte, block.BlockSize())
for i := 0; i < len(data)/block.BlockSize(); i++ {
subtle.XORBytes(chainingValue, chainingValue, data[i*block.BlockSize():])
block.Encrypt(chainingValue, chainingValue)
}
return chainingValue
}

285
drbg/ctr_drbg_test.go Normal file
View File

@ -0,0 +1,285 @@
package drbg
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"testing"
"github.com/emmansun/gmsm/sm4"
)
var ctrtests = []struct {
gm bool
cipherProvider func(key []byte) (cipher.Block, error)
keyLen int
entropyInput string
nonce string
personalizationString string
v0 string
key0 string
entropyInputReseed string
additionalInputReseed string
v1 string
key1 string
additionalInput1 string
v2 string
key2 string
additionalInput2 string
returnbits1 string
v3 string
key3 string
}{
{ // AES-128, without additional input
false,
aes.NewCipher,
16,
"0f65da13dca407999d4773c2b4a11d85",
"5209e5b4ed82a234",
"",
"80941680713df715056fb2a3d2e998b2",
"0c42ea6804303954deb197a07e6dbdd2",
"1dea0a12c52bf64339dd291c80d8ca89",
"",
"f2bacbb233252fba35fb0582f9286179", // v1
"32fbfd0109f364ed21ef21a6e5c763e7", //key1
"",
"99003d630bba500fe17c37f8c7331bf6", // v2
"757c8eb766f9aaa4650d6500b58624a3", //key2
"",
"2859cc468a76b08661ffd23b28547ffd0997ad526a0f51261b99ed3a37bd407bf418dbe6c6c3e26ed0ddefcb7474d899bd99f3655427519fc5b4057bcaf306d4",
"5907ab447a88e5106753507cc97e0fd5",
"e421ff2445e04992faf36cf9a5eaf1f9",
},
{ // AES-128, without additional input
false,
aes.NewCipher,
16,
"c9b8d7eb0afa5889e7f9b78a50ed453c",
"3058ba347ecd11b1",
"",
"b4e0180e3af0d99592249db33a29cc4e",
"1621bebef7e9215078459ecc74baffbc",
"643686b86266d9111f29eb389e1184b4",
"",
"7574911eeb85d56d385f0c8c99965c4a", // v1
"a4c515266cb5986825a503b39d5f398c", //key1
"",
"abca3ea049e405d3826f43e54e08c8f7", // v2
"edcdf23f60d3988a4d235798aa0d33a2", //key2
"",
"0a8ccadc1c5cbd20b8ce32f942505e654b91a4e9410e0ea627c961d632d3be71d6a7dfd64b8f70d28ff91869b92ced908b454936b6d18fcddd7fb77216ccc404",
"1d57b4e09fd920d91877a0737559ee29",
"e83e07722d26779d0b76a52a629b211b",
},
{ // AES-128, with additional input
false,
aes.NewCipher,
16,
"285da6cf762552634636bfee3400b156",
"8f8bada74820cb43",
"",
"ad2af7e4c84337cfc3116d59f02c54a8", // v0
"c92780982442d348cc7363dfc96a999d", // key0
"b4699b33354a83bfed115f770f32db0b", // EntropyInputReseed
"38bfec9a10e6e40c106841dae48dc3b8", // AdditionalInputReseed
"923f37427a8e10bf945249a5b790769a", // v1
"57004c8a776f5c702e83ff56acc32dcc", // key1
"629ead5bacfac8235711ffeb22f57558", // AdditionalInput1
"7ade619ed91092987d8a1d244605f85f", // v2
"3b5f92f511c10fef2f640de2cd8c9049", // key2
"dd8a02ee668ca3e03949b38cb6e6b4df", // AdditionalInput2
"e555aa4432bde04dcf0f0b03ead187b31df06653d444234b5c1bfc11b224285f2fb2b6cdd5a9ae6f13d99bd02c3c9fe9c3c1be46a600f5f757ab4574af893501",
"f5dac2375e820f797c6f1258147d8ea7", // v3
"6bc01c1518fe9f9dfbbb08d97c34db1e", // key3
},
{ // AES-192, without additional input
false,
aes.NewCipher,
24,
"b11d8b104a7ced9b9f37e5d92ad3dfcbb817552b1ae88f6a",
"017510f270c66586a51313eadc32b07e",
"",
"9e5767ab537fe663c71e4054ba618c8d", // v0
"b9b3d73bc0c784a7d78db344109707c73abbff7dc2dfa864", // key0
"6d14cfb36f30c9c1a1ba0e0a32c2f99d1b47f219a3a8ac14", // EntropyInputReseed
"", // AdditionalInputReseed
"c8563c5a4adc3b579f79f898c4b69854", // v1
"3e18d4984d454e5f986e49bfa7a569dab3667ece8130cba1", // key1
"", // AdditionalInput1
"087a3112e191f60619acae2a556f333b", // v2
"b42a24cbb9e8c014bb65350afa28a67b273a41e599bde5b8", // key2
"", // AdditionalInput2
"53fbba563ae014ebc080767aab8452a9f36ce40bbf68f1a12dc0a6388c870c8dfa4250526cbc8c983fee6449903c6bd7c2c02e327680a66b464267edbc4e6797",
"84f344f8277841e920464ca475b10276", // v3
"1f5e987ac2259b7072867e4ae59167094d0162111062f6f8", // key3
},
{ // AES-192, with additional input
false,
aes.NewCipher,
24,
"3a09c9cc5e01f152ea2ed3021d49b4d6386aa6f04521ebde",
"490bd4ee628cf9615035543e70fce4e2",
"",
"59a45ccbc3864f79b896c30d4a231d46", // v0
"a4283dc9450ac97bf22c387082e3816728243473cedaa2af", // key0
"df06e5668d41a6fa7660aef477eff7a0ffc0542c1cd406d5", // EntropyInputReseed
"59b8c26626aab69e462752722f19450d12e2c0e959882d4d06ef4177e396855d", // AdditionalInputReseed
"5857d49a1552923931926dca1682fbc2", // v1
"9c4d7784fe341619e21f2535d404866df3b75e9a7940d471", // key1
"28e57a9128e479985cce391e98127fd126f37ad0f317fd5f97b8c18e762f360b", // AdditionalInput1
"bb8ed7bcbe1203be861b8e6570fe116b", // v2
"6a8fddde995255f89ea3c9454cc481045ff0e16ce5a34693", // key2
"d488672b52e867816178369f542190685bbe8672720c1943d8a4378cc9b9dd0c", // AdditionalInput2
"5c233e2850e4981bab0f6513a76ca2c9f9f97b89b7fedd3d9aaffecf305d89fd5306cf24715895ad9ba7dac8c389fd87f95b4973003150871fa281e962f270cb",
"1cf82a0638c421bb43401943498d0f88", // v3
"5dec9ad1f5f3d0e7bb59ae581097a3f616e443e4f5bd804a", // key3
},
{ // AES-256, without additional input
false,
aes.NewCipher,
32,
"2d4c9f46b981c6a0b2b5d8c69391e569ff13851437ebc0fc00d616340252fed5",
"0bf814b411f65ec4866be1abb59d3c32",
"",
"446ce986bd722ad1a514ebb7d274ec99", // v0
"d64160c3e965f377caef625c7eb21dd37728bcf84bfc23b92e267611feaffda8", // key0
"93500fae4fa32b86033b7a7bac9d37e710dcc67ca266bc8607d665937766d207", // EntropyInputReseed
"", // AdditionalInputReseed
"0b8e38a54036f1ba80a2880d4f17bb09", // v1
"50d9feb33fc77303b83232b7deded04f1bfa4afaa937712f88458d6b64c046c5", // key1
"", // AdditionalInput1
"84b0a849c5459e27fe7f8c5db26fa13d", // v2
"a2203a6f082ecdc0cd38f0b3b19f1a8cd6a5f110a13bb488c1e70f9f95a93024", // key2
"", // AdditionalInput2
"322dd28670e75c0ea638f3cb68d6a9d6e50ddfd052b772a7b1d78263a7b8978b6740c2b65a9550c3a76325866fa97e16d74006bc96f26249b9f0a90d076f08e5",
"de67dd5f9a431fc46dd1825cd1a2bff3", // v3
"de721178a341a85eb54a2f7e2b3cd4bcc201417e739eb183fa958f9af8535b2c", // key3
},
{ // AES-256, with additional input
false,
aes.NewCipher,
32,
"6f60f0f9d486bc23e1223b934e61c0c78ae9232fa2e9a87c6dacd447c3f10e9e",
"401e3f87762fa8a14ab232ccb8480a2f",
"",
"ee534dcfd9d2be3a3f9c65a6c5f599b0", // v0
"6d9aa2e029466438d3e4c22530bd071dbe57b549b87370957b28da8ae083f8d6", // key0
"350be52552a65a804a106543ebb7dd046cffae104e4e8b2f18936d564d3c1950", // EntropyInputReseed
"7a3688adb1cfb6c03264e2762ece96bfe4daf9558fabf74d7fff203c08b4dd9f", // AdditionalInputReseed
"433725f6c4b8c662c3b2db4b75f38d86", // v1
"b5953178a900b2fcf052b5cbc1d882ea944da2965e84fef59c4919bb4d5c892d", // key1
"67cf4a56d081c53670f257c25557014cd5e8b0e919aa58f23d6861b10b00ea80", // AdditionalInput1
"2c342b2ab12bd3484e4660b8dd5f85eb", // v2
"b2b9e9f1ffcfd84c050445f93dfad90d6ca240494bbed5d44a0deb38fbaeb751", // key2
"648d4a229198b43f33dd7dd8426650be11c5656adcdf913bb3ee5eb49a2a3892", // AdditionalInput2
"2d819fb9fee38bfc3f15a07ef0e183ff36db5d3184cea1d24e796ba103687415abe6d9f2c59a11931439a3d14f45fc3f4345f331a0675a3477eaf7cd89107e37",
"a9729f842063b9464e74018c0ab30df3", // v3
"770600434fe0af64e045f5530e2b9732da9e3b4c3af342994a4f1f7ee5c4144e", // key3
},
{ // SM4-128, without additional input
true,
sm4.NewCipher,
16,
"0f65da13dca407999d4773c2b4a11d85",
"5209e5b4ed82a234",
"",
"3dee0b770815026b88a86a1637c4c9f6",
"40ef052e0dffc441fd644f6dce7430c0",
"1dea0a12c52bf64339dd291c80d8ca89",
"",
"1c5c3a1369b7ab6ea6f5631b0a8e4f2d", // v1
"ac5783e385d6d4c6a2c5a184e7cebecb", //key1
"",
"22b16dfe04896ef1ddf69f12e5d9a1dd", // v2
"3f7b6539fc274b97565b2a1b26d021e0", //key2
"",
"58129d515c9bb6d32e1ab1206fe6c618",
"69bfff2ab5f3dbcc5eaca7eb5a3cf8e4",
"c2b4a693df1f768e7d45d0926a40f527",
},
{ // SM4-128, with additional input
false,
sm4.NewCipher,
16,
"285da6cf762552634636bfee3400b156",
"8f8bada74820cb43",
"",
"f5cb0d67b1784b97b1d90fff63e67d32", // v0
"f55a8ee6f9414feac90fe15a43c9bc11", // key0
"b4699b33354a83bfed115f770f32db0b", // EntropyInputReseed
"38bfec9a10e6e40c106841dae48dc3b8", // AdditionalInputReseed
"7398bf677878ae483b0e560b9c5bf666", // v1
"402b8f4743e52ba2d7dc88541f5d23b1", // key1
"629ead5bacfac8235711ffeb22f57558", // AdditionalInput1
"095162afa9927d43e1bb531ff9719a32", // v2
"c8a759bc36a5b86ede53122a89041a1c", // key2
"dd8a02ee668ca3e03949b38cb6e6b4df", // AdditionalInput2
"71f9fbd6bc78149061eabfbd248f54eb",
"eddc1b09ff7ce31f19a0922249d78345", // v3
"8c10825d503632f8cabed20bc62d6a08", // key3
},
}
func TestCtrDRBG(t *testing.T) {
for i, test := range ctrtests {
entropyInput, _ := hex.DecodeString(test.entropyInput)
nonce, _ := hex.DecodeString(test.nonce)
personalizationString, _ := hex.DecodeString(test.personalizationString)
v0, _ := hex.DecodeString(test.v0)
key0, _ := hex.DecodeString(test.key0)
hd, err := NewCtrDrbg(test.cipherProvider, test.keyLen, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Error(err)
}
if !bytes.Equal(hd.v[:len(v0)], v0) {
t.Errorf("case %v, not same v0 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key[:len(key0)], key0) {
t.Errorf("case %v, not same key0 %s", i+1, hex.EncodeToString(hd.key))
}
// Reseed
entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed)
additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed)
v1, _ := hex.DecodeString(test.v1)
key1, _ := hex.DecodeString(test.key1)
err = hd.Reseed(entropyInputReseed, additionalInputReseed)
if err != nil {
t.Error(err)
}
if !bytes.Equal(hd.v, v1) {
t.Errorf("case %v, not same v1 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key1) {
t.Errorf("case %v, not same key1 %s", i+1, hex.EncodeToString(hd.key))
}
// Generate 1
returnbits1, _ := hex.DecodeString(test.returnbits1)
v2, _ := hex.DecodeString(test.v2)
key2, _ := hex.DecodeString(test.key2)
output := make([]byte, len(returnbits1))
additionalInput1, _ := hex.DecodeString(test.additionalInput1)
hd.Generate(output, additionalInput1)
if !bytes.Equal(hd.v, v2) {
t.Errorf("case %v, not same v2 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key2) {
t.Errorf("case %v, not same key2 %s", i+1, hex.EncodeToString(hd.key))
}
// Generate 2
v3, _ := hex.DecodeString(test.v3)
key3, _ := hex.DecodeString(test.key3)
additionalInput2, _ := hex.DecodeString(test.additionalInput2)
hd.Generate(output, additionalInput2)
if !bytes.Equal(hd.v[:len(v0)], v3) {
t.Errorf("case %v, not same v3 %s", i+1, hex.EncodeToString(hd.v))
}
if !bytes.Equal(hd.key, key3) {
t.Errorf("case %v, not same key3 %s", i+1, hex.EncodeToString(hd.key))
}
if !bytes.Equal(returnbits1, output) {
t.Errorf("case %v, not expected return bits %s", i+1, hex.EncodeToString(output))
}
}
}

View File

@ -1,40 +1,22 @@
package drbg
import (
"crypto/sha256"
"encoding/binary"
"errors"
"hash"
"time"
"github.com/emmansun/gmsm/sm3"
)
const HASH_DRBG_SEED_SIZE = 55
const HASH_DRBG_MAX_SEED_SIZE = 111
const HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10
const HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20
const HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL2 = time.Duration(60) * time.Second
const HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL1 = time.Duration(600) * time.Second
const MAX_BYTES = 1 << 27
const MAX_BYTES_PER_GENERATE = 1 << 11
type SecurityLevel byte
const (
SECURITY_LEVEL_ONE SecurityLevel = 0x01
SECURITY_LEVEL_TWO SecurityLevel = 0x02
)
type HashDrbg struct {
md hash.Hash
v []byte
c []byte
seedLength int
reseedTime time.Time
reseedIntervalInTime time.Duration
reseedCounter uint64
reseedIntervalInCounter uint64
securityLevel SecurityLevel
gm bool
BaseDrbg
md hash.Hash
c []byte
}
// NewHashDrbg create one hash DRBG instance
@ -43,11 +25,11 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
hd.gm = gm
hd.securityLevel = securityLevel
hd.reseedIntervalInCounter = HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL1
hd.reseedIntervalInTime = HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL1
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL1
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL1
if hd.securityLevel == SECURITY_LEVEL_TWO {
hd.reseedIntervalInCounter = HASH_DRBG_RESEED_COUNTER_INTERVAL_LEVEL2
hd.reseedIntervalInTime = HASH_DRBG_RESEED_TIME_INTERVAL_LEVEL2
hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL2
hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL2
}
// here for the min length, we just check <=0 now
@ -65,7 +47,7 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
}
hd.md = md
if md.Size() <= sha256.Size {
if md.Size() <= sm3.Size {
hd.v = make([]byte, HASH_DRBG_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_SEED_SIZE)
hd.seedLength = HASH_DRBG_SEED_SIZE
@ -91,14 +73,14 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
return hd, nil
}
// NewNISTHashDrbg return hash DRBG implementation which follow NIST standard
// NewNISTHashDrbg return hash DRBG implementation which follows NIST standard
func NewNISTHashDrbg(md hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(md, securityLevel, false, entropy, nonce, personalization)
}
// NewGMHashDrbg return hash DRBG implementation which follow GM/T 0105-2021 standard
func NewGMHashDrbg(md hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(md, securityLevel, true, entropy, nonce, personalization)
// NewGMHashDrbg return hash DRBG implementation which follows GM/T 0105-2021 standard
func NewGMHashDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(sm3.New(), securityLevel, true, entropy, nonce, personalization)
}
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
@ -135,19 +117,6 @@ func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
return nil
}
func (hd *HashDrbg) NeedReseed() bool {
return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime)
}
func add(left, right []byte, len int) {
var temp uint16 = 0
for i := len - 1; i >= 0; i-- {
temp += uint16(left[i]) + uint16(right[i])
right[i] = byte(temp & 0xff)
temp >>= 8
}
}
func (hd *HashDrbg) addW(w []byte) {
t := make([]byte, hd.seedLength)
copy(t[hd.seedLength-len(w):], w)
@ -171,15 +140,6 @@ func (hd *HashDrbg) addReseedCounter() {
add(t, hd.v, hd.seedLength)
}
func (hd *HashDrbg) addOne(data []byte) {
var temp uint16 = 1
for i := hd.seedLength - 1; i >= 0; i-- {
temp += uint16(data[i])
data[i] = byte(temp & 0xff)
temp >>= 8
}
}
// Generate hash DRBG generate process. GM/T 0105-2021 has a little different with NIST.
// GM/T 0105-2021 can only generate no more than hash.Size bytes once.
func (hd *HashDrbg) Generate(b, additional []byte) error {
@ -210,7 +170,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
for i := 0; i < int(limit); i++ {
md.Write(data)
copy(b[i*md.Size():], md.Sum(nil))
hd.addOne(data)
addOne(data, hd.seedLength)
md.Reset()
}
}
@ -222,7 +182,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
return nil
}
func (hd *HashDrbg) derive(seedMaternial []byte, len int) []byte {
func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
md := hd.md
limit := uint64(len+md.Size()-1) / uint64(md.Size())
var requireBytes [4]byte
@ -232,7 +192,7 @@ func (hd *HashDrbg) derive(seedMaternial []byte, len int) []byte {
for i := 0; i < int(limit); i++ {
md.Write([]byte{ct})
md.Write(requireBytes[:])
md.Write(seedMaternial)
md.Write(seedMaterial)
copy(k[i*md.Size():], md.Sum(nil))
ct++
md.Reset()

View File

@ -156,6 +156,24 @@ var tests = []struct {
"00d98d35a2fab8df23e9e1fb9aad143d62c0759eb79e15c37e8f2bc5064e68da",
"299d084f049dd9b7f62ee712b5b2c1c602f078980f4d9816d8f2baf38765be984b6c493497af30f68a56072404f27e45af419d04eb9e35",
},
{
true,
sm3.New(),
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
"997cd31a7032c8643ca56de1d34ff4f930b13192e17c8947bcaf9b9d010cf79805511255c7ea18b41cde77e491ca943861ec29780f3f36", // v0
"4c5b167d27fa9d40cbc45d0d9f3c52504a1cb5aa2f37a3fa812037bd1e458412ecff0641dd5cb2785d0f8044151b42842777211547b457", // c0
"96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc",
"6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d",
"e3d804eda66df62f425c41047c5812fca471c8236395c92d4bd834c2e52d606be6ad3da8973df16e8567bcb16e45f2842ace91bf6dfeb3", // v1
"1a1b2fffca26953626e0fa3afd377e14e63c5e81275b39f1436b707efb2c3059e6ced8fdb238a45bf05aae9f2417dbf5c4f89d3772f324", // c1
"d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6",
"fdf334ed70948b65693d3b3f798f91118aae26a48af1045be386984b75695902870479c3c53593d332d195ee7f1bed45fc5069cc4f9948", // v2
"5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b",
"48709db5509a03d6131775fbbfe74fe52611e760d22fde61e274a295f4354d67",
"180e64ed3abb209b901e357a76c70f2670ea8525b24c401d146b70b178e33d8fd10f7680c50bbe1f9773ba664dd14cd25d329c380bf399", // v3
},
}
func TestHashDRBG(t *testing.T) {
@ -175,6 +193,7 @@ func TestHashDRBG(t *testing.T) {
if !bytes.Equal(hd.c[:len(c0)], c0) {
t.Errorf("not same c0 %s", hex.EncodeToString(hd.c[:len(c0)]))
}
// Reseed
entropyInputReseed, _ := hex.DecodeString(test.entropyInputReseed)
additionalInputReseed, _ := hex.DecodeString(test.additionalInputReseed)
v1, _ := hex.DecodeString(test.v1)
@ -189,6 +208,7 @@ func TestHashDRBG(t *testing.T) {
if !bytes.Equal(hd.c[:len(c0)], c1) {
t.Errorf("not same c1 %s", hex.EncodeToString(hd.c[:len(c0)]))
}
// Generate 1
returnbits1, _ := hex.DecodeString(test.returnbits1)
v2, _ := hex.DecodeString(test.v2)
output := make([]byte, len(returnbits1))
@ -197,6 +217,7 @@ func TestHashDRBG(t *testing.T) {
if !bytes.Equal(hd.v[:len(v0)], v2) {
t.Errorf("not same v2 %s", hex.EncodeToString(hd.v[:len(v0)]))
}
// Generate 2
v3, _ := hex.DecodeString(test.v3)
additionalInput2, _ := hex.DecodeString(test.additionalInput2)
hd.Generate(output, additionalInput2)