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

     1  package drbg
     2  
     3  import (
     4  	"crypto/cipher"
     5  	"encoding/binary"
     6  	"errors"
     7  	"time"
     8  
     9  	"github.com/emmansun/gmsm/internal/subtle"
    10  	"github.com/emmansun/gmsm/sm4"
    11  )
    12  
    13  // CtrDrbg CTR DRBG structure, its instance is NOT goroutine safe!!!
    14  type CtrDrbg struct {
    15  	BaseDrbg
    16  	cipherProvider func(key []byte) (cipher.Block, error)
    17  	key            []byte
    18  	keyLen         int
    19  }
    20  
    21  // NewCtrDrbg create one CTR DRBG instance
    22  func NewCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, gm bool, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
    23  	hd := &CtrDrbg{}
    24  
    25  	hd.gm = gm
    26  	hd.setSecurityLevel(securityLevel)
    27  
    28  	// here for the min length, we just check <=0 now
    29  	if len(entropy) == 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= MAX_BYTES {
    30  		return nil, errors.New("drbg: invalid entropy length")
    31  	}
    32  
    33  	// here for the min length, we just check <=0 now
    34  	if len(nonce) == 0 || (hd.gm && len(nonce) < 16) || len(nonce) >= MAX_BYTES>>1 {
    35  		return nil, errors.New("drbg: invalid nonce length")
    36  	}
    37  
    38  	if len(personalization) >= MAX_BYTES {
    39  		return nil, errors.New("drbg: personalization is too long")
    40  	}
    41  
    42  	hd.cipherProvider = cipherProvider
    43  	hd.keyLen = keyLen
    44  	temp := make([]byte, hd.keyLen)
    45  	block, err := cipherProvider(temp)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	hd.seedLength = block.BlockSize() + keyLen
    50  	hd.v = make([]byte, block.BlockSize())
    51  	hd.key = make([]byte, hd.keyLen)
    52  
    53  	// seed_material = entropy_input || instantiation_nonce || personalization_string
    54  	seedMaterial := make([]byte, len(entropy)+len(nonce)+len(personalization))
    55  	copy(seedMaterial, entropy)
    56  	copy(seedMaterial[len(entropy):], nonce)
    57  	copy(seedMaterial[len(entropy)+len(nonce):], personalization)
    58  	// seed_material = Block_Cipher_df(seed_material, seed_length)
    59  	seedMaterial = hd.derive(seedMaterial, hd.seedLength)
    60  	// CTR_DRBG_Updae(seed_material, Key, V)
    61  	hd.update(seedMaterial)
    62  
    63  	hd.reseedCounter = 1
    64  	hd.reseedTime = time.Now()
    65  	return hd, nil
    66  }
    67  
    68  // NewNISTCtrDrbg create one CTR DRBG implementation which follows NIST standard
    69  func NewNISTCtrDrbg(cipherProvider func(key []byte) (cipher.Block, error), keyLen int, securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
    70  	return NewCtrDrbg(cipherProvider, keyLen, securityLevel, false, entropy, nonce, personalization)
    71  }
    72  
    73  // NewGMCtrDrbg create one CTR DRBG implementation which follows GM/T 0105-2021 standard
    74  func NewGMCtrDrbg(securityLevel SecurityLevel, entropy, nonce, personalization []byte) (*CtrDrbg, error) {
    75  	return NewCtrDrbg(sm4.NewCipher, 16, securityLevel, true, entropy, nonce, personalization)
    76  }
    77  
    78  func (hd *CtrDrbg) Reseed(entropy, additional []byte) error {
    79  	// here for the min length, we just check <=0 now
    80  	if len(entropy) <= 0 || (hd.gm && len(entropy) < 32) || len(entropy) >= MAX_BYTES {
    81  		return errors.New("drbg: invalid entropy length")
    82  	}
    83  
    84  	if len(additional) >= MAX_BYTES {
    85  		return errors.New("drbg: additional input too long")
    86  	}
    87  
    88  	// seed_material = entropy_input || additional_input
    89  	var seedMaterial []byte
    90  	if len(additional) == 0 {
    91  		seedMaterial = entropy
    92  	} else {
    93  		seedMaterial = make([]byte, len(entropy)+len(additional))
    94  		copy(seedMaterial, entropy)
    95  		copy(seedMaterial[len(entropy):], additional)
    96  	}
    97  	// seed_material = Block_Cipher_df(seed_material, seed_length)
    98  	seedMaterial = hd.derive(seedMaterial, hd.seedLength)
    99  	// CTR_DRBG_Updae(seed_material, Key, V)
   100  	hd.update(seedMaterial)
   101  
   102  	hd.reseedCounter = 1
   103  	hd.reseedTime = time.Now()
   104  	return nil
   105  }
   106  
   107  func (hd *CtrDrbg) newBlockCipher(key []byte) cipher.Block {
   108  	block, err := hd.cipherProvider(key)
   109  	if err != nil {
   110  		panic(err)
   111  	}
   112  	return block
   113  }
   114  
   115  func (hd *CtrDrbg) MaxBytesPerRequest() int {
   116  	if hd.gm {
   117  		return len(hd.v)
   118  	}
   119  	return MAX_BYTES_PER_GENERATE
   120  }
   121  
   122  // Generate CTR DRBG pseudorandom bits generate process.
   123  func (hd *CtrDrbg) Generate(b, additional []byte) error {
   124  	if hd.NeedReseed() {
   125  		return ErrReseedRequired
   126  	}
   127  	outlen := len(hd.v)
   128  	if (hd.gm && len(b) > outlen) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) {
   129  		return errors.New("drbg: too many bytes requested")
   130  	}
   131  
   132  	// If len(additional_input) > 0, then
   133  	// additional_input = Block_Cipher_df(additional_input, seed_length)
   134  	// CTR_DRBG_Update(additional_input, Key, V)
   135  	if len(additional) > 0 {
   136  		additional = hd.derive(additional, hd.seedLength)
   137  		hd.update(additional)
   138  	}
   139  
   140  	block := hd.newBlockCipher(hd.key)
   141  	temp := make([]byte, outlen)
   142  
   143  	m := len(b)
   144  	limit := uint64(m+outlen-1) / uint64(outlen)
   145  	for i := 0; i < int(limit); i++ {
   146  		// V = (V + 1) mod 2^outlen)
   147  		addOne(hd.v, outlen)
   148  		// output_block = Encrypt(Key, V)
   149  		block.Encrypt(temp, hd.v)
   150  		copy(b[i*outlen:], temp)
   151  	}
   152  	hd.update(additional)
   153  	hd.reseedCounter++
   154  	return nil
   155  }
   156  
   157  func (cd *CtrDrbg) update(seedMaterial []byte) {
   158  	temp := make([]byte, cd.seedLength)
   159  	block := cd.newBlockCipher(cd.key)
   160  
   161  	outlen := block.BlockSize()
   162  	v := make([]byte, outlen)
   163  	output := make([]byte, outlen)
   164  	copy(v, cd.v)
   165  	for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ {
   166  		// V = (V + 1) mod 2^outlen
   167  		addOne(v, outlen)
   168  		// output_block = Encrypt(Key, V)
   169  		block.Encrypt(output, v)
   170  		copy(temp[i*outlen:], output)
   171  	}
   172  	// temp = temp XOR seed_material
   173  	subtle.XORBytes(temp, temp, seedMaterial)
   174  	// Key = leftmost(temp, key_length)
   175  	copy(cd.key, temp)
   176  	// V = rightmost(temp, outlen)
   177  	copy(cd.v, temp[cd.keyLen:])
   178  }
   179  
   180  // derive Block_Cipher_df
   181  func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
   182  	outlen := cd.seedLength - cd.keyLen
   183  	lenS := ((4 + 4 + len(seedMaterial) + outlen) / outlen) * outlen
   184  	S := make([]byte, lenS+outlen)
   185  
   186  	// S = counter || len(seed_material) || len(return_bytes) || seed_material || 0x80
   187  	// len(S) = ((outlen + 4 + 4 + len(seed_material) + 1 + outlen - 1) / outlen) * outlen
   188  	binary.BigEndian.PutUint32(S[outlen:], uint32(len(seedMaterial)))
   189  	binary.BigEndian.PutUint32(S[outlen+4:], uint32(returnBytes))
   190  	copy(S[outlen+8:], seedMaterial)
   191  	S[outlen+8+len(seedMaterial)] = 0x80
   192  
   193  	key := make([]byte, cd.keyLen)
   194  	for i := 0; i < cd.keyLen; i++ {
   195  		key[i] = byte(i)
   196  	}
   197  	blocks := (cd.seedLength + outlen - 1) / outlen
   198  	temp := make([]byte, blocks*outlen)
   199  	block := cd.newBlockCipher(key)
   200  
   201  	for i := 0; i < blocks; i++ {
   202  		binary.BigEndian.PutUint32(S, uint32(i))
   203  		copy(temp[i*outlen:], cd.bcc(block, S))
   204  	}
   205  
   206  	key = temp[:cd.keyLen]
   207  	X := temp[cd.keyLen:cd.seedLength]
   208  	temp = make([]byte, returnBytes)
   209  	block = cd.newBlockCipher(key)
   210  	for i := 0; i < (returnBytes+outlen-1)/outlen; i++ {
   211  		block.Encrypt(X, X)
   212  		copy(temp[i*outlen:], X)
   213  	}
   214  	return temp
   215  }
   216  
   217  func (cd *CtrDrbg) bcc(block cipher.Block, data []byte) []byte {
   218  	chainingValue := make([]byte, block.BlockSize())
   219  	for i := 0; i < len(data)/block.BlockSize(); i++ {
   220  		subtle.XORBytes(chainingValue, chainingValue, data[i*block.BlockSize():])
   221  		block.Encrypt(chainingValue, chainingValue)
   222  	}
   223  	return chainingValue
   224  }