github.com/emmansun/gmsm@v0.29.1/drbg/hmac_drbg.go (about)

     1  package drbg
     2  
     3  import (
     4  	"crypto/hmac"
     5  	"errors"
     6  	"hash"
     7  	"time"
     8  )
     9  
    10  // HmacDrbg hmac DRBG structure, its instance is NOT goroutine safe!!!
    11  // The instance should be used in one goroutine only.
    12  // Thera are NO hmac DRBR definition in GM/T 0105-2021 yet.
    13  type HmacDrbg struct {
    14  	BaseDrbg
    15  	newHash  func() hash.Hash
    16  	key      []byte
    17  	hashSize int
    18  }
    19  
    20  // NewHmacDrbg create one hmac DRBG instance
    21  func NewHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HmacDrbg, error) {
    22  	hd := &HmacDrbg{}
    23  
    24  	hd.gm = gm
    25  	hd.newHash = newHash
    26  	hd.setSecurityLevel(securityLevel)
    27  
    28  	md := newHash()
    29  	hd.hashSize = md.Size()
    30  
    31  	// here for the min length, we just check <=0 now
    32  	if len(entropy) == 0 || len(entropy) >= MAX_BYTES {
    33  		return nil, errors.New("drbg: invalid entropy length")
    34  	}
    35  
    36  	// here for the min length, we just check <=0 now
    37  	if len(nonce) == 0 || len(nonce) >= MAX_BYTES>>1 {
    38  		return nil, errors.New("drbg: invalid nonce length")
    39  	}
    40  
    41  	if len(personalization) >= MAX_BYTES {
    42  		return nil, errors.New("drbg: personalization is too long")
    43  	}
    44  
    45  	// HMAC_DRBG_Instantiate_process
    46  	hd.key = make([]byte, hd.hashSize)
    47  	hd.v = make([]byte, hd.hashSize)
    48  	for i := 0; i < hd.hashSize; i++ {
    49  		hd.key[i] = 0x00
    50  		hd.v[i] = 0x01
    51  	}
    52  	hd.update(entropy, nonce, personalization)
    53  	hd.reseedCounter = 1
    54  	hd.reseedTime = time.Now()
    55  
    56  	return hd, nil
    57  }
    58  
    59  // NewNISTHmacDrbg return hmac DRBG implementation which follows NIST standard
    60  func NewNISTHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HmacDrbg, error) {
    61  	return NewHmacDrbg(newHash, securityLevel, false, entropy, nonce, personalization)
    62  }
    63  
    64  // Generate generates pseudo random bytes usging HMAC_DRBG_Generate_process
    65  func (hd *HmacDrbg) Generate(output, additional []byte) error {
    66  	// Step 1. If reseed_counter > reseed_interval, then return [ErrReseedRequired] that a reseed is required
    67  	if hd.NeedReseed() {
    68  		return ErrReseedRequired
    69  	}
    70  	// Step 2. If additional_input is provided, then do update
    71  	if len(additional) > 0 {
    72  		hd.update(additional)
    73  	}
    74  	requestedBytes := len(output)
    75  	md := hmac.New(hd.newHash, hd.key)
    76  	for ; requestedBytes > 0; requestedBytes -= hd.hashSize {
    77  		// 4.1. V = HMAC (Key, V)
    78  		md.Reset()
    79  		md.Write(hd.v)
    80  		hd.v = md.Sum(hd.v[:0])
    81  		// 4.2. copy V to output
    82  		copy(output, hd.v)
    83  		if requestedBytes > hd.hashSize {
    84  			output = output[hd.hashSize:]
    85  		}
    86  	}
    87  	// Step 6. (Key, V) = HMAC_DRBG_Update (additional_input, Key, V)
    88  	hd.update(additional)
    89  	// Step 7. reseed_counter = reseed_counter + 1
    90  	hd.reseedCounter++
    91  	return nil
    92  }
    93  
    94  // Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
    95  // reference to NIST.SP.800-90Ar1.pdf section 10.1.2.4
    96  func (hd *HmacDrbg) Reseed(entropy, additional []byte) error {
    97  	// here for the min length, we just check <=0 now
    98  	if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= MAX_BYTES {
    99  		return errors.New("drbg: invalid entropy length")
   100  	}
   101  
   102  	if len(additional) >= MAX_BYTES {
   103  		return errors.New("drbg: additional input too long")
   104  	}
   105  	hd.update(entropy, additional)
   106  	hd.reseedCounter = 1
   107  	hd.reseedTime = time.Now()
   108  	return nil
   109  }
   110  
   111  func (hd *HmacDrbg) MaxBytesPerRequest() int {
   112  	return MAX_BYTES_PER_GENERATE
   113  }
   114  
   115  // The HMAC_DRBG_Update function updates the internal state of
   116  // HMAC_DRBG using the provided_data. Note that for this DRBG mechanism, the
   117  // HMAC_DRBG_Update function also serves as a derivation function for the
   118  // instantiate and reseed functions.
   119  func (hd *HmacDrbg) update(byteSlices ...[]byte) error {
   120  	// step 1. K = HMAC(K, V || 0x00 || provided_data)
   121  	md := hmac.New(hd.newHash, hd.key)
   122  	md.Write(hd.v)
   123  	md.Write([]byte{0x00})
   124  	length := 0
   125  	for _, bytes := range byteSlices {
   126  		length += len(bytes)
   127  		if len(bytes) > 0 {
   128  			md.Write(bytes)
   129  		}
   130  	}
   131  	hd.key = md.Sum(hd.key[:0])
   132  	// step 2. V = HMAC(K, V)
   133  	md = hmac.New(hd.newHash, hd.key)
   134  	md.Write(hd.v)
   135  	hd.v = md.Sum(hd.v[:0])
   136  	// step 3. If provided_data = null, then return
   137  	if length == 0 {
   138  		return nil
   139  	}
   140  	// step 4. K = HMAC(K, V || 0x01 || provided_data)
   141  	md.Reset()
   142  	md.Write(hd.v)
   143  	md.Write([]byte{0x01})
   144  	for _, bytes := range byteSlices {
   145  		if len(bytes) > 0 {
   146  			md.Write(bytes)
   147  		}
   148  	}
   149  	hd.key = md.Sum(hd.key[:0])
   150  	// step 5. V = HMAC(K, V)
   151  	md = hmac.New(hd.newHash, hd.key)
   152  	md.Write(hd.v)
   153  	hd.v = md.Sum(hd.v[:0])
   154  	return nil
   155  }