Skip to content

Commit

Permalink
drbg: support NIST HMAC-DRBG
Browse files Browse the repository at this point in the history
  • Loading branch information
emmansun authored Jun 5, 2024
1 parent b434b5f commit dfc45df
Show file tree
Hide file tree
Showing 3 changed files with 963 additions and 1 deletion.
2 changes: 1 addition & 1 deletion drbg/hash_drbg.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
}
if hd.gm { // leftmost(Hash(V))
md.Write(hd.v)
copy(b, md.Sum(nil))
md.Sum(b[:0])
md.Reset()
} else {
limit := uint64(m+md.Size()-1) / uint64(md.Size())
Expand Down
158 changes: 158 additions & 0 deletions drbg/hmac_drbg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package drbg

import (
"crypto/hmac"
"errors"
"hash"
"time"

"github.com/emmansun/gmsm/sm3"
)

// 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")
}

if hd.hashSize <= sm3.Size {
hd.seedLength = HASH_DRBG_SEED_SIZE
} else {
hd.seedLength = HASH_DRBG_MAX_SEED_SIZE
}
// 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
}

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
}
Loading

0 comments on commit dfc45df

Please sign in to comment.