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

     1  package drbg
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"hash"
     7  	"time"
     8  
     9  	"github.com/emmansun/gmsm/sm3"
    10  )
    11  
    12  const HASH_DRBG_SEED_SIZE = 55
    13  const HASH_DRBG_MAX_SEED_SIZE = 111
    14  
    15  // HashDrbg hash DRBG structure, its instance is NOT goroutine safe!!!
    16  type HashDrbg struct {
    17  	BaseDrbg
    18  	newHash func() hash.Hash
    19  	c       []byte
    20  	hashSize int
    21  }
    22  
    23  // NewHashDrbg create one hash DRBG instance
    24  func NewHashDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*HashDrbg, error) {
    25  	hd := &HashDrbg{}
    26  
    27  	hd.gm = gm
    28  	hd.newHash = newHash
    29  	hd.setSecurityLevel(securityLevel)
    30  
    31  	md := newHash()
    32  	hd.hashSize = md.Size()
    33  
    34  	// here for the min length, we just check <=0 now
    35  	if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= MAX_BYTES {
    36  		return nil, errors.New("drbg: invalid entropy length")
    37  	}
    38  
    39  	// here for the min length, we just check <=0 now
    40  	if len(nonce) == 0 || (hd.gm && len(nonce) < hd.hashSize/2) || len(nonce) >= MAX_BYTES>>1 {
    41  		return nil, errors.New("drbg: invalid nonce length")
    42  	}
    43  
    44  	if len(personalization) >= MAX_BYTES {
    45  		return nil, errors.New("drbg: personalization is too long")
    46  	}
    47  
    48  	if hd.hashSize <= sm3.Size {
    49  		hd.v = make([]byte, HASH_DRBG_SEED_SIZE)
    50  		hd.c = make([]byte, HASH_DRBG_SEED_SIZE)
    51  		hd.seedLength = HASH_DRBG_SEED_SIZE
    52  	} else {
    53  		hd.v = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
    54  		hd.c = make([]byte, HASH_DRBG_MAX_SEED_SIZE)
    55  		hd.seedLength = HASH_DRBG_MAX_SEED_SIZE
    56  	}
    57  	// seed_material = entropy_input || instantiation_nonce || personalization_string
    58  	seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
    59  	copy(seedMaterial, entropy)
    60  	copy(seedMaterial[len(entropy):], nonce)
    61  	copy(seedMaterial[len(entropy)+len(nonce):], personalization)
    62  
    63  	// seed = Hash_df(seed_material, seed_length)
    64  	seed := hd.derive(seedMaterial, hd.seedLength)
    65  	// V = seed
    66  	copy(hd.v, seed)
    67  
    68  	// C = Hash_df(0x00 || V, seed_length)
    69  	temp := make([]byte, hd.seedLength+1)
    70  	temp[0] = 0
    71  	copy(temp[1:], seed)
    72  	seed = hd.derive(temp, hd.seedLength)
    73  	copy(hd.c, seed)
    74  
    75  	hd.reseedCounter = 1
    76  	hd.reseedTime = time.Now()
    77  
    78  	return hd, nil
    79  }
    80  
    81  // NewNISTHashDrbg return hash DRBG implementation which follows NIST standard
    82  func NewNISTHashDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
    83  	return NewHashDrbg(newHash, securityLevel, false, entropy, nonce, personalization)
    84  }
    85  
    86  // NewGMHashDrbg return hash DRBG implementation which follows GM/T 0105-2021 standard
    87  func NewGMHashDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*HashDrbg, error) {
    88  	return NewHashDrbg(sm3.New, securityLevel, true, entropy, nonce, personalization)
    89  }
    90  
    91  // Reseed hash DRBG reseed process. GM/T 0105-2021 has a little different with NIST.
    92  func (hd *HashDrbg) Reseed(entropy, additional []byte) error {
    93  	// here for the min length, we just check <=0 now
    94  	if len(entropy) == 0 || (hd.gm && len(entropy) < hd.hashSize) || len(entropy) >= MAX_BYTES {
    95  		return errors.New("drbg: invalid entropy length")
    96  	}
    97  
    98  	if len(additional) >= MAX_BYTES {
    99  		return errors.New("drbg: additional input too long")
   100  	}
   101  	seedMaterial := make([]byte, len(entropy)+hd.seedLength+len(additional)+1)
   102  	seedMaterial[0] = 1
   103  	if hd.gm { // seed_material = 0x01 || entropy_input || V || additional_input
   104  		copy(seedMaterial[1:], entropy)
   105  		copy(seedMaterial[len(entropy)+1:], hd.v)
   106  	} else { // seed_material = 0x01 || V || entropy_input || additional_input
   107  		copy(seedMaterial[1:], hd.v)
   108  		copy(seedMaterial[hd.seedLength+1:], entropy)
   109  	}
   110  	copy(seedMaterial[len(entropy)+hd.seedLength+1:], additional)
   111  
   112  	// seed = Hash_df(seed_material, seed_length)
   113  	seed := hd.derive(seedMaterial, hd.seedLength)
   114  
   115  	// V = seed
   116  	copy(hd.v, seed)
   117  	temp := make([]byte, hd.seedLength+1)
   118  
   119  	// C = Hash_df(0x01 || V, seed_length)
   120  	temp[0] = 0
   121  	copy(temp[1:], seed)
   122  	seed = hd.derive(temp, hd.seedLength)
   123  	copy(hd.c, seed)
   124  
   125  	hd.reseedCounter = 1
   126  	hd.reseedTime = time.Now()
   127  	return nil
   128  }
   129  
   130  func (hd *HashDrbg) addW(w []byte) {
   131  	t := make([]byte, hd.seedLength)
   132  	copy(t[hd.seedLength-len(w):], w)
   133  	add(t, hd.v, hd.seedLength)
   134  }
   135  
   136  func (hd *HashDrbg) addC() {
   137  	add(hd.c, hd.v, hd.seedLength)
   138  }
   139  
   140  func (hd *HashDrbg) addH() {
   141  	md := hd.newHash()
   142  	md.Write([]byte{0x03})
   143  	md.Write(hd.v)
   144  	hd.addW(md.Sum(nil))
   145  }
   146  
   147  func (hd *HashDrbg) addReseedCounter() {
   148  	t := make([]byte, hd.seedLength)
   149  	binary.BigEndian.PutUint64(t[hd.seedLength-8:], hd.reseedCounter)
   150  	add(t, hd.v, hd.seedLength)
   151  }
   152  
   153  func (hd *HashDrbg) MaxBytesPerRequest() int {
   154  	if hd.gm {
   155  		return hd.hashSize
   156  	}
   157  	return MAX_BYTES_PER_GENERATE
   158  }
   159  
   160  // Generate hash DRBG pseudorandom bits process. GM/T 0105-2021 has a little different with NIST.
   161  // GM/T 0105-2021 can only generate no more than hash.Size bytes once.
   162  func (hd *HashDrbg) Generate(b, additional []byte) error {
   163  	if hd.NeedReseed() {
   164  		return ErrReseedRequired
   165  	}
   166  	if (hd.gm && len(b) > hd.hashSize) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
   167  		return errors.New("drbg: too many bytes requested")
   168  	}
   169  	md := hd.newHash()
   170  	m := len(b)
   171  
   172  	// if len(additional_input) > 0, then
   173  	// w = Hash(0x02 || V || additional_input)
   174  	if len(additional) > 0 {
   175  		md.Write([]byte{0x02})
   176  		md.Write(hd.v)
   177  		md.Write(additional)
   178  		w := md.Sum(nil)
   179  		md.Reset()
   180  		hd.addW(w)
   181  	}
   182  	if hd.gm { // leftmost(Hash(V))
   183  		md.Write(hd.v)
   184  		copy(b, md.Sum(nil))
   185  		md.Reset()
   186  	} else {
   187  		limit := uint64(m+md.Size()-1) / uint64(md.Size())
   188  		data := make([]byte, hd.seedLength)
   189  		copy(data, hd.v)
   190  		for i := 0; i < int(limit); i++ {
   191  			md.Write(data)
   192  			copy(b[i*md.Size():], md.Sum(nil))
   193  			addOne(data, hd.seedLength)
   194  			md.Reset()
   195  		}
   196  	}
   197  	// V = (V + H + C + reseed_counter) mode 2^seed_length
   198  	hd.addH()
   199  	hd.addC()
   200  	hd.addReseedCounter()
   201  
   202  	hd.reseedCounter++
   203  	return nil
   204  }
   205  
   206  // derive Hash_df
   207  func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
   208  	md := hd.newHash()
   209  	limit := uint64(len+hd.hashSize-1) / uint64(hd.hashSize)
   210  	var requireBytes [4]byte
   211  	binary.BigEndian.PutUint32(requireBytes[:], uint32(len<<3))
   212  	var ct byte = 1
   213  	k := make([]byte, len)
   214  	for i := 0; i < int(limit); i++ {
   215  		// Hash( counter_byte || return_bits || seed_material )
   216  		md.Write([]byte{ct})
   217  		md.Write(requireBytes[:])
   218  		md.Write(seedMaterial)
   219  		copy(k[i*md.Size():], md.Sum(nil))
   220  		ct++
   221  		md.Reset()
   222  	}
   223  	return k
   224  }