mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-21 17:56:19 +08:00
156 lines
4.3 KiB
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
|
|
}
|