gmsm/drbg/hmac_drbg.go

156 lines
4.3 KiB
Go

package drbg
import (
"crypto/hmac"
"errors"
"hash"
"time"
)
// HmacDrbg hmac DRBG structure, its instance is NOT goroutine safe!!!
// The instance should be used in one goroutine only.
// Thera are NO hmac DRBR definition in GM/T 0105-2021 yet.
type HmacDrbg struct {
BaseDrbg
newHash func() hash.Hash
key []byte
hashSize int
}
// NewHmacDrbg create one hmac DRBG instance
func NewHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HmacDrbg, error) {
hd := &HmacDrbg{}
hd.gm = gm
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 || len(entropy) >= MAX_BYTES {
return nil, errors.New("drbg: 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("drbg: invalid nonce length")
}
if len(personalization) >= MAX_BYTES {
return nil, errors.New("drbg: personalization is too long")
}
// HMAC_DRBG_Instantiate_process
hd.key = make([]byte, hd.hashSize)
hd.v = make([]byte, hd.hashSize)
for i := 0; i < hd.hashSize; i++ {
hd.key[i] = 0x00
hd.v[i] = 0x01
}
hd.update(entropy, nonce, personalization)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return hd, nil
}
// NewNISTHmacDrbg return hmac DRBG implementation which follows NIST standard
func NewNISTHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HmacDrbg, error) {
return NewHmacDrbg(newHash, securityLevel, false, entropy, nonce, personalization)
}
// Generate generates pseudo random bytes usging HMAC_DRBG_Generate_process
func (hd *HmacDrbg) Generate(output, additional []byte) error {
// Step 1. If reseed_counter > reseed_interval, then return [ErrReseedRequired] that a reseed is required
if hd.NeedReseed() {
return ErrReseedRequired
}
// Step 2. If additional_input is provided, then do update
if len(additional) > 0 {
hd.update(additional)
}
requestedBytes := len(output)
md := hmac.New(hd.newHash, hd.key)
for ; requestedBytes > 0; requestedBytes -= hd.hashSize {
// 4.1. V = HMAC (Key, V)
md.Reset()
md.Write(hd.v)
hd.v = md.Sum(hd.v[:0])
// 4.2. copy V to output
copy(output, hd.v)
if requestedBytes > hd.hashSize {
output = output[hd.hashSize:]
}
}
// Step 6. (Key, V) = HMAC_DRBG_Update (additional_input, Key, V)
hd.update(additional)
// Step 7. reseed_counter = reseed_counter + 1
hd.reseedCounter++
return nil
}
// Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
// reference to NIST.SP.800-90Ar1.pdf section 10.1.2.4
func (hd *HmacDrbg) Reseed(entropy, additional []byte) error {
// here for the min length, we just check <=0 now
if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= MAX_BYTES {
return errors.New("drbg: invalid entropy length")
}
if len(additional) >= MAX_BYTES {
return errors.New("drbg: additional input too long")
}
hd.update(entropy, additional)
hd.reseedCounter = 1
hd.reseedTime = time.Now()
return nil
}
func (hd *HmacDrbg) MaxBytesPerRequest() int {
return MAX_BYTES_PER_GENERATE
}
// The HMAC_DRBG_Update function updates the internal state of
// HMAC_DRBG using the provided_data. Note that for this DRBG mechanism, the
// HMAC_DRBG_Update function also serves as a derivation function for the
// instantiate and reseed functions.
func (hd *HmacDrbg) update(byteSlices ...[]byte) error {
// step 1. K = HMAC(K, V || 0x00 || provided_data)
md := hmac.New(hd.newHash, hd.key)
md.Write(hd.v)
md.Write([]byte{0x00})
length := 0
for _, bytes := range byteSlices {
length += len(bytes)
if len(bytes) > 0 {
md.Write(bytes)
}
}
hd.key = md.Sum(hd.key[:0])
// step 2. V = HMAC(K, V)
md = hmac.New(hd.newHash, hd.key)
md.Write(hd.v)
hd.v = md.Sum(hd.v[:0])
// step 3. If provided_data = null, then return
if length == 0 {
return nil
}
// step 4. K = HMAC(K, V || 0x01 || provided_data)
md.Reset()
md.Write(hd.v)
md.Write([]byte{0x01})
for _, bytes := range byteSlices {
if len(bytes) > 0 {
md.Write(bytes)
}
}
hd.key = md.Sum(hd.key[:0])
// step 5. V = HMAC(K, V)
md = hmac.New(hd.newHash, hd.key)
md.Write(hd.v)
hd.v = md.Sum(hd.v[:0])
return nil
}