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 }