mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-22 10:16:18 +08:00
implement ctr drbg
This commit is contained in:
parent
f723aa8c66
commit
643df78c07
61
drbg/common.go
Normal file
61
drbg/common.go
Normal 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
201
drbg/ctr_drbg.go
Normal 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
285
drbg/ctr_drbg_test.go
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user