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

     1  // Package drbg implements Random Number Generation Using Deterministic Random Bit Generators.
     2  package drbg
     3  
     4  import (
     5  	"crypto/cipher"
     6  	"crypto/rand"
     7  	"errors"
     8  	"hash"
     9  	"io"
    10  	"time"
    11  
    12  	"github.com/emmansun/gmsm/sm3"
    13  	"github.com/emmansun/gmsm/sm4"
    14  )
    15  
    16  const DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST uint64 = 8
    17  const DRBG_RESEED_COUNTER_INTERVAL_LEVEL2 uint64 = 1 << 10
    18  const DRBG_RESEED_COUNTER_INTERVAL_LEVEL1 uint64 = 1 << 20
    19  
    20  const DRBG_RESEED_TIME_INTERVAL_LEVEL_TEST = time.Duration(6) * time.Second
    21  const DRBG_RESEED_TIME_INTERVAL_LEVEL2 = time.Duration(60) * time.Second
    22  const DRBG_RESEED_TIME_INTERVAL_LEVEL1 = time.Duration(600) * time.Second
    23  
    24  const MAX_BYTES = 1 << 27
    25  const MAX_BYTES_PER_GENERATE = 1 << 11
    26  
    27  var ErrReseedRequired = errors.New("drbg: reseed reuqired")
    28  
    29  type SecurityLevel byte
    30  
    31  const (
    32  	SECURITY_LEVEL_ONE  SecurityLevel = 0x01
    33  	SECURITY_LEVEL_TWO  SecurityLevel = 0x02
    34  	SECURITY_LEVEL_TEST SecurityLevel = 0x99
    35  )
    36  
    37  // DrbgPrng sample pseudo random number generator base on DRBG
    38  type DrbgPrng struct {
    39  	entropySource    io.Reader
    40  	securityStrength int
    41  	impl             DRBG
    42  }
    43  
    44  // NewCtrDrbgPrng create pseudo random number generator base on CTR DRBG
    45  func NewCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
    46  	prng := new(DrbgPrng)
    47  	if entropySource != nil {
    48  		prng.entropySource = entropySource
    49  	} else {
    50  		prng.entropySource = rand.Reader
    51  	}
    52  
    53  	prng.securityStrength = selectSecurityStrength(securityStrength)
    54  	if gm && securityStrength < 32 {
    55  		return nil, errors.New("drbg: invalid security strength")
    56  	}
    57  
    58  	// Get entropy input
    59  	entropyInput := make([]byte, prng.securityStrength)
    60  	err := prng.getEntropy(entropyInput)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	// Get nonce, reference to NIST SP 800-90A, 8.6.7
    66  	nonce := make([]byte, prng.securityStrength/2)
    67  	err = prng.getEntropy(nonce)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	// inital working state
    73  	prng.impl, err = NewCtrDrbg(cipherProvider, keyLen, securityLevel, gm, entropyInput, nonce, personalization)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	return prng, nil
    79  }
    80  
    81  // NewNistCtrDrbgPrng create pseudo random number generator base on CTR DRBG which follows NIST standard
    82  func NewNistCtrDrbgPrng(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
    83  	return NewCtrDrbgPrng(cipherProvider, keyLen, entropySource, securityStrength, false, securityLevel, personalization)
    84  }
    85  
    86  // NewNistCtrDrbgPrng create pseudo random number generator base on CTR DRBG which follows GM/T 0105-2021 standard
    87  func NewGmCtrDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
    88  	return NewCtrDrbgPrng(sm4.NewCipher, 16, entropySource, securityStrength, true, securityLevel, personalization)
    89  }
    90  
    91  // NewHashDrbgPrng create pseudo random number generator base on HASH DRBG
    92  func NewHashDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
    93  	prng := new(DrbgPrng)
    94  	if entropySource != nil {
    95  		prng.entropySource = entropySource
    96  	} else {
    97  		prng.entropySource = rand.Reader
    98  	}
    99  	prng.securityStrength = selectSecurityStrength(securityStrength)
   100  	if gm && securityStrength < 32 {
   101  		return nil, errors.New("drbg: invalid security strength")
   102  	}
   103  
   104  	// Get entropy input
   105  	entropyInput := make([]byte, prng.securityStrength)
   106  	err := prng.getEntropy(entropyInput)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	// Get nonce, reference to NIST SP 800-90A, 8.6.7
   112  	nonce := make([]byte, prng.securityStrength/2)
   113  	err = prng.getEntropy(nonce)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	// inital working state
   119  	prng.impl, err = NewHashDrbg(newHash, securityLevel, gm, entropyInput, nonce, personalization)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	return prng, nil
   125  }
   126  
   127  // NewNistHashDrbgPrng create pseudo random number generator base on hash DRBG which follows NIST standard
   128  func NewNistHashDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
   129  	return NewHashDrbgPrng(newHash, entropySource, securityStrength, false, securityLevel, personalization)
   130  }
   131  
   132  // NewGmHashDrbgPrng create pseudo random number generator base on hash DRBG which follows GM/T 0105-2021 standard
   133  func NewGmHashDrbgPrng(entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
   134  	return NewHashDrbgPrng(sm3.New, entropySource, securityStrength, true, securityLevel, personalization)
   135  }
   136  
   137  // NewHmacDrbgPrng create pseudo random number generator base on hash mac DRBG
   138  func NewHmacDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, gm bool, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
   139  	prng := new(DrbgPrng)
   140  	if entropySource != nil {
   141  		prng.entropySource = entropySource
   142  	} else {
   143  		prng.entropySource = rand.Reader
   144  	}
   145  	prng.securityStrength = selectSecurityStrength(securityStrength)
   146  
   147  	// Get entropy input
   148  	entropyInput := make([]byte, prng.securityStrength)
   149  	err := prng.getEntropy(entropyInput)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	// Get nonce, reference to NIST SP 800-90A, 8.6.7
   155  	nonce := make([]byte, prng.securityStrength/2)
   156  	err = prng.getEntropy(nonce)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	// inital working state
   162  	prng.impl, err = NewHmacDrbg(newHash, securityLevel, gm, entropyInput, nonce, personalization)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	return prng, nil
   168  }
   169  
   170  // NewNistHmacDrbgPrng create pseudo random number generator base on hash mac DRBG which follows NIST standard
   171  func NewNistHmacDrbgPrng(newHash func() hash.Hash, entropySource io.Reader, securityStrength int, securityLevel SecurityLevel, personalization []byte) (*DrbgPrng, error) {
   172  	return NewHmacDrbgPrng(newHash, entropySource, securityStrength, false, securityLevel, personalization)
   173  }
   174  
   175  func (prng *DrbgPrng) getEntropy(entropyInput []byte) error {
   176  	n, err := prng.entropySource.Read(entropyInput)
   177  	if err != nil {
   178  		return err
   179  	}
   180  	if n != len(entropyInput) {
   181  		return errors.New("drbg: fail to read enough entropy input")
   182  	}
   183  	return nil
   184  }
   185  
   186  func (prng *DrbgPrng) Read(data []byte) (int, error) {
   187  	maxBytesPerRequest := prng.impl.MaxBytesPerRequest()
   188  	total := 0
   189  
   190  	for len(data) > 0 {
   191  		b := data
   192  		if len(data) > maxBytesPerRequest {
   193  			b = data[:maxBytesPerRequest]
   194  		}
   195  
   196  		err := prng.impl.Generate(b, nil)
   197  		if err == ErrReseedRequired {
   198  			entropyInput := make([]byte, prng.securityStrength)
   199  			err := prng.getEntropy(entropyInput)
   200  			if err != nil {
   201  				return 0, err
   202  			}
   203  			err = prng.impl.Reseed(entropyInput, nil)
   204  			if err != nil {
   205  				return 0, err
   206  			}
   207  		} else if err != nil {
   208  			return 0, err
   209  		} else {
   210  			total += len(b)
   211  			data = data[len(b):]
   212  		}
   213  	}
   214  	return total, nil
   215  }
   216  
   217  // DRBG interface for both hash and ctr drbg implementations
   218  type DRBG interface {
   219  	// check internal state, return if reseed required
   220  	NeedReseed() bool
   221  	// reseed process
   222  	Reseed(entropy, additional []byte) error
   223  	// generate requrested bytes to b
   224  	Generate(b, additional []byte) error
   225  	// MaxBytesPerRequest return max bytes per request
   226  	MaxBytesPerRequest() int
   227  }
   228  
   229  type BaseDrbg struct {
   230  	v                       []byte
   231  	seedLength              int
   232  	reseedTime              time.Time
   233  	reseedIntervalInTime    time.Duration
   234  	reseedCounter           uint64
   235  	reseedIntervalInCounter uint64
   236  	securityLevel           SecurityLevel
   237  	gm                      bool
   238  }
   239  
   240  func (hd *BaseDrbg) NeedReseed() bool {
   241  	return (hd.reseedCounter > hd.reseedIntervalInCounter) || (hd.gm && time.Since(hd.reseedTime) > hd.reseedIntervalInTime)
   242  }
   243  
   244  func (hd *BaseDrbg) setSecurityLevel(securityLevel SecurityLevel) {
   245  	hd.securityLevel = securityLevel
   246  	switch securityLevel {
   247  	case SECURITY_LEVEL_TWO:
   248  		hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL2
   249  		hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL2
   250  	case SECURITY_LEVEL_TEST:
   251  		hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL_TEST
   252  		hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL_TEST
   253  	default:
   254  		hd.reseedIntervalInCounter = DRBG_RESEED_COUNTER_INTERVAL_LEVEL1
   255  		hd.reseedIntervalInTime = DRBG_RESEED_TIME_INTERVAL_LEVEL1
   256  	}
   257  }
   258  
   259  // Set security_strength to the lowest security strength greater than or equal to 
   260  // requested_instantiation_security_strength from the set {112, 128, 192, 256}.
   261  func selectSecurityStrength(requested int) int {
   262  	switch {
   263  	case requested <= 14:
   264  		return 14
   265  	case requested <= 16:
   266  		return 16
   267  	case requested <= 24:
   268  		return 24
   269  	case requested <= 32:
   270  		return 32
   271  	default:
   272  		return requested
   273  	}
   274  }
   275  
   276  func add(left, right []byte, len int) {
   277  	var temp uint16 = 0
   278  	for i := len - 1; i >= 0; i-- {
   279  		temp += uint16(left[i]) + uint16(right[i])
   280  		right[i] = byte(temp & 0xff)
   281  		temp >>= 8
   282  	}
   283  }
   284  
   285  func addOne(data []byte, len int) {
   286  	var temp uint16 = 1
   287  	for i := len - 1; i >= 0; i-- {
   288  		temp += uint16(data[i])
   289  		data[i] = byte(temp & 0xff)
   290  		temp >>= 8
   291  	}
   292  }