drbg: use hash creator, but it's still NOT goroutine safe

This commit is contained in:
Sun Yimin 2023-04-13 09:32:14 +08:00 committed by GitHub
parent a4d7601bd9
commit 75fde484ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 39 deletions

View File

@ -88,7 +88,7 @@ func NewGmCtrDrbgPrng(entropySource io.Reader, securityStrength int, securityLev
}
// NewHashDrbgPrng create pseudo random number generator base on HASH DRBG
func NewHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
func NewHashDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
prng := new(DrbgPrng)
if entropySource != nil {
prng.entropySource = entropySource
@ -114,7 +114,7 @@ func NewHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int
return nil, err
}
prng.impl, err = NewHashDrbg(md, securityLevel, gm, entropyInput, nonce, personalization)
prng.impl, err = NewHashDrbg(newHash, securityLevel, gm, entropyInput, nonce, personalization)
if err != nil {
return nil, err
}
@ -123,13 +123,13 @@ func NewHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int
}
// NewNistHashDrbgPrng create pseudo random number generator base on hash DRBG which follows NIST standard
func NewNistHashDrbgPrng(md hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(md, entropySource, securityStrength, false, securityLevel, personalization)
func NewNistHashDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(newHash, entropySource, securityStrength, false, securityLevel, personalization)
}
// NewGmHashDrbgPrng create pseudo random number generator base on hash DRBG which follows GM/T 0105-2021 standard
func NewGmHashDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
return NewHashDrbgPrng(sm3.New(), entropySource, securityStrength, true, securityLevel, personalization)
return NewHashDrbgPrng(sm3.New, entropySource, securityStrength, true, securityLevel, personalization)
}
func (prng *DrbgPrng) getEntropy(entropyInput []byte) error {

View File

@ -56,7 +56,7 @@ func TestGmHashDrbgPrng(t *testing.T) {
}
func TestNistHashDrbgPrng(t *testing.T) {
prng, err := NewNistHashDrbgPrng(sha256.New(), nil, 32, SECURITY_LEVEL_TEST, nil)
prng, err := NewNistHashDrbgPrng(sha256.New, nil, 32, SECURITY_LEVEL_TEST, nil)
if err != nil {
t.Fatal(err)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/emmansun/gmsm/sm4"
)
// CtrDrbg CTR DRBG structure, its instance is NOT goroutine safe!!!
type CtrDrbg struct {
BaseDrbg
cipherProvider func(key []byte) (cipher.Block, error)

View File

@ -12,27 +12,32 @@ import (
const HASH_DRBG_SEED_SIZE = 55
const HASH_DRBG_MAX_SEED_SIZE = 111
// HashDrbg hash DRBG structure, its instance is NOT goroutine safe!!!
type HashDrbg struct {
BaseDrbg
md hash.Hash
c []byte
newHash func() hash.Hash
c []byte
hashSize int
}
// NewHashDrbg create one hash DRBG instance
func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HashDrbg, error) {
func NewHashDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HashDrbg, error) {
hd := &HashDrbg{}
hd.gm = gm
hd.md = md
hd.newHash = newHash
hd.setSecurityLevel(securityLevel)
md := newHash()
hd.hashSize = md.Size()
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.md.Size()) || len(entropy) >= MAX_BYTES {
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || 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 || (hd.gm && len(nonce) < hd.md.Size()/2) || len(nonce) >= MAX_BYTES>>1 {
if len(nonce) == 0 || (hd.gm && len(nonce) < hd.hashSize/2) || len(nonce) >= MAX_BYTES>>1 {
return nil, errors.New("invalid nonce length")
}
@ -40,7 +45,7 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
return nil, errors.New("personalization is too long")
}
if md.Size() <= sm3.Size {
if hd.hashSize <= sm3.Size {
hd.v = make([]byte, HASH_DRBG_SEED_SIZE)
hd.c = make([]byte, HASH_DRBG_SEED_SIZE)
hd.seedLength = HASH_DRBG_SEED_SIZE
@ -74,19 +79,19 @@ func NewHashDrbg(md hash.Hash, securityLevel SecurityLevel, gm bool, entropy, no
}
// 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)
func NewNISTHashDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
return NewHashDrbg(newHash, securityLevel, false, 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)
return NewHashDrbg(sm3.New, securityLevel, true, entropy, nonce, personalization)
}
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.md.Size()) || len(entropy) >= MAX_BYTES {
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= MAX_BYTES {
return errors.New("invalid entropy length")
}
@ -133,10 +138,10 @@ func (hd *HashDrbg) addC() {
}
func (hd *HashDrbg) addH() {
hd.md.Write([]byte{0x03})
hd.md.Write(hd.v)
hd.addW(hd.md.Sum(nil))
hd.md.Reset()
md := hd.newHash()
md.Write([]byte{0x03})
md.Write(hd.v)
hd.addW(md.Sum(nil))
}
func (hd *HashDrbg) addReseedCounter() {
@ -147,7 +152,7 @@ func (hd *HashDrbg) addReseedCounter() {
func (hd *HashDrbg) MaxBytesPerRequest() int {
if hd.gm {
return hd.md.Size()
return hd.hashSize
}
return MAX_BYTES_PER_GENERATE
}
@ -158,10 +163,10 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
if hd.NeedReseed() {
return ErrReseedRequired
}
if (hd.gm && len(b) > hd.md.Size()) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
if (hd.gm && len(b) > hd.hashSize) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
return errors.New("too many bytes requested")
}
md := hd.md
md := hd.newHash()
m := len(b)
// if len(additional_input) > 0, then
@ -200,8 +205,8 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
// derive Hash_df
func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
md := hd.md
limit := uint64(len+md.Size()-1) / uint64(md.Size())
md := hd.newHash()
limit := uint64(len+hd.hashSize-1) / uint64(hd.hashSize)
var requireBytes [4]byte
binary.BigEndian.PutUint32(requireBytes[:], uint32(len<<3))
var ct byte = 1

View File

@ -14,7 +14,7 @@ import (
var tests = []struct {
gm bool
md hash.Hash
newHash func() hash.Hash
entropyInput string
nonce string
personalizationString string
@ -32,7 +32,7 @@ var tests = []struct {
}{
{
false,
sha1.New(),
sha1.New,
"1610b828ccd27de08ceea032a20e9208",
"492cf1709242f6b5",
"",
@ -50,7 +50,7 @@ var tests = []struct {
},
{
false,
sha1.New(),
sha1.New,
"d9bab5cedca96f6178d64509a0dfdc5e",
"dad8989414450e01",
"",
@ -68,7 +68,7 @@ var tests = []struct {
},
{
false,
sha256.New(),
sha256.New,
"63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569",
"808aa38f2a72a62359915a9f8a04ca68",
"",
@ -86,7 +86,7 @@ var tests = []struct {
},
{
false,
sha256.New(),
sha256.New,
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
@ -104,7 +104,7 @@ var tests = []struct {
},
{
false,
sha512.New(),
sha512.New,
"3144e17a10c856129764f58fd8e4231020546996c0bf6cff8e91c24ee09be333",
"b16fcb1cf0c010f31feab733588b8e04",
"",
@ -122,7 +122,7 @@ var tests = []struct {
},
{
false,
sha512.New(),
sha512.New,
"c73a7820f0f53e8bbfc3b7b71d994143cf6e98642e9ea6d8df5dccbc43db8720",
"20cc9834b588adcb1bbde64f0d2a34cb",
"",
@ -140,7 +140,7 @@ var tests = []struct {
},
{
true,
sm3.New(),
sm3.New,
"63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569",
"808aa38f2a72a62359915a9f8a04ca68",
"",
@ -158,7 +158,7 @@ var tests = []struct {
},
{
true,
sm3.New(),
sm3.New,
"9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c",
"a20765538e8db31295747ec922c13a69",
"",
@ -183,7 +183,7 @@ func TestHashDRBG(t *testing.T) {
personalizationString, _ := hex.DecodeString(test.personalizationString)
v0, _ := hex.DecodeString(test.v0)
c0, _ := hex.DecodeString(test.c0)
hd, err := NewHashDrbg(test.md, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
hd, err := NewHashDrbg(test.newHash, SECURITY_LEVEL_ONE, test.gm, entropyInput, nonce, personalizationString)
if err != nil {
t.Fatal(err)
}
@ -232,15 +232,15 @@ func TestHashDRBG(t *testing.T) {
func TestGmHashDRBG_Validation(t *testing.T) {
entropyInput := make([]byte, 64)
_, err := NewHashDrbg(sm3.New(), SECURITY_LEVEL_ONE, true, entropyInput[:16], entropyInput[16:24], nil)
_, err := NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:16], entropyInput[16:24], nil)
if err == nil {
t.Fatalf("expected error here")
}
_, err = NewHashDrbg(sm3.New(), SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:40], nil)
_, err = NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:40], nil)
if err == nil {
t.Fatalf("expected error here")
}
hd, err := NewHashDrbg(sm3.New(), SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil)
hd, err := NewHashDrbg(sm3.New, SECURITY_LEVEL_ONE, true, entropyInput[:32], entropyInput[32:48], nil)
if err != nil {
t.Fatal(err)
}