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 }