github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/prng/prng_test.go (about) 1 /* 2 * Copyright (c) 2018, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package prng 21 22 import ( 23 "bytes" 24 crypto_rand "crypto/rand" 25 "fmt" 26 "math" 27 "math/big" 28 "sort" 29 "strings" 30 "testing" 31 "time" 32 ) 33 34 func TestSeed(t *testing.T) { 35 36 seed, err := NewSeed() 37 if err != nil { 38 t.Fatalf("NewSeed failed: %s", err) 39 } 40 41 prng1 := NewPRNGWithSeed(seed) 42 prng2 := NewPRNGWithSeed(seed) 43 44 for i := 1; i < 10000; i++ { 45 46 bytes1 := make([]byte, i) 47 prng1.Read(bytes1) 48 49 bytes2 := make([]byte, i) 50 prng2.Read(bytes2) 51 52 zeroes := make([]byte, i) 53 if bytes.Equal(zeroes, bytes1) { 54 t.Fatalf("unexpected zero bytes") 55 } 56 57 if !bytes.Equal(bytes1, bytes2) { 58 t.Fatalf("unexpected different bytes") 59 } 60 } 61 62 prng1 = NewPRNGWithSeed(seed) 63 64 prng3, err := NewPRNGWithSaltedSeed(seed, "3") 65 if err != nil { 66 t.Fatalf("NewPRNGWithSaltedSeed failed: %s", err) 67 } 68 69 prng4, err := NewPRNGWithSaltedSeed(seed, "4") 70 if err != nil { 71 t.Fatalf("NewPRNGWithSaltedSeed failed: %s", err) 72 } 73 74 for i := 1; i < 10000; i++ { 75 76 bytes1 := make([]byte, i) 77 prng1.Read(bytes1) 78 79 bytes3 := make([]byte, i) 80 prng3.Read(bytes3) 81 82 bytes4 := make([]byte, i) 83 prng4.Read(bytes4) 84 85 if bytes.Equal(bytes1, bytes3) { 86 t.Fatalf("unexpected identical bytes") 87 } 88 89 if bytes.Equal(bytes3, bytes4) { 90 t.Fatalf("unexpected identical bytes") 91 } 92 } 93 } 94 95 func TestFlipWeightedCoin(t *testing.T) { 96 97 runs := 100000 98 tolerance := 1000 99 100 testCases := []struct { 101 weight float64 102 expectedTrues int 103 }{ 104 {0.333, runs / 3}, 105 {0.5, runs / 2}, 106 {1.0, runs}, 107 {0.0, 0}, 108 } 109 110 for _, testCase := range testCases { 111 t.Run(fmt.Sprintf("%f", testCase.weight), func(t *testing.T) { 112 113 p, err := NewPRNG() 114 if err != nil { 115 t.Fatalf("NewPRNG failed: %s", err) 116 } 117 118 trues := 0 119 for i := 0; i < runs; i++ { 120 if p.FlipWeightedCoin(testCase.weight) { 121 trues++ 122 } 123 } 124 125 min := testCase.expectedTrues - tolerance 126 if min < 0 { 127 min = 0 128 } 129 max := testCase.expectedTrues + tolerance 130 131 if trues < min || trues > max { 132 t.Errorf("unexpected coin flip outcome: %f %d (+/-%d) %d", 133 testCase.weight, testCase.expectedTrues, tolerance, trues) 134 } 135 }) 136 } 137 } 138 139 func TestPerm(t *testing.T) { 140 141 p, err := NewPRNG() 142 if err != nil { 143 t.Fatalf("NewPRNG failed: %s", err) 144 } 145 146 for n := 0; n < 1000; n++ { 147 148 perm := p.Perm(n) 149 if len(perm) != n { 150 t.Error("unexpected permutation size") 151 } 152 153 sum := 0 154 for i := 0; i < n; i++ { 155 sum += perm[i] 156 } 157 158 expectedSum := (n * (n - 1)) / 2 159 if sum != expectedSum { 160 t.Error("unexpected permutation") 161 } 162 } 163 } 164 165 func TestRange(t *testing.T) { 166 167 p, err := NewPRNG() 168 if err != nil { 169 t.Fatalf("NewPRNG failed: %s", err) 170 } 171 172 min := 1 173 max := 19 174 var gotMin, gotMax bool 175 for n := 0; n < 1000; n++ { 176 177 i := p.Range(min, max) 178 179 if i < min || i > max { 180 t.Error("out of range") 181 } 182 183 if i == min { 184 gotMin = true 185 } 186 if i == max { 187 gotMax = true 188 } 189 } 190 191 if !gotMin { 192 t.Error("missing min") 193 } 194 if !gotMax { 195 t.Error("missing max") 196 } 197 } 198 199 func TestPeriod(t *testing.T) { 200 201 p, err := NewPRNG() 202 if err != nil { 203 t.Fatalf("NewPRNG failed: %s", err) 204 } 205 206 min := 1 * time.Nanosecond 207 max := 10000 * time.Nanosecond 208 209 different := 0 210 211 for n := 0; n < 1000; n++ { 212 213 res1 := p.Period(min, max) 214 215 if res1 < min { 216 t.Error("duration should not be less than min") 217 } 218 219 if res1 > max { 220 t.Error("duration should not be more than max") 221 } 222 223 res2 := p.Period(min, max) 224 225 if res1 != res2 { 226 different += 1 227 } 228 } 229 230 // res1 and res2 should be different most of the time, but it's possible 231 // to get the same result twice in a row. 232 if different < 900 { 233 t.Error("duration insufficiently random") 234 } 235 } 236 237 func TestJitter(t *testing.T) { 238 239 testCases := []struct { 240 n int64 241 factor float64 242 expectedMin int64 243 expectedMax int64 244 }{ 245 {100, 0.1, 90, 110}, 246 {1000, 0.3, 700, 1300}, 247 } 248 249 for _, testCase := range testCases { 250 t.Run(fmt.Sprintf("Jitter case: %+v", testCase), func(t *testing.T) { 251 252 p, err := NewPRNG() 253 if err != nil { 254 t.Fatalf("NewPRNG failed: %s", err) 255 } 256 257 min := int64(math.MaxInt64) 258 max := int64(0) 259 260 for i := 0; i < 100000; i++ { 261 262 x := p.Jitter(testCase.n, testCase.factor) 263 if x < min { 264 min = x 265 } 266 if x > max { 267 max = x 268 } 269 } 270 271 if min != testCase.expectedMin { 272 t.Errorf("unexpected minimum jittered value: %d", min) 273 } 274 275 if max != testCase.expectedMax { 276 t.Errorf("unexpected maximum jittered value: %d", max) 277 } 278 }) 279 } 280 } 281 282 func TestIntn(t *testing.T) { 283 284 p, err := NewPRNG() 285 if err != nil { 286 t.Fatalf("NewPRNG failed: %s", err) 287 } 288 289 for max := 0; max <= 255; max++ { 290 291 counts := make(map[int]int) 292 repeats := 200000 293 294 for r := 0; r < repeats; r++ { 295 value := p.Intn(max) 296 if value < 0 || value > max { 297 t.Fatalf("unexpected value: max = %d, value = %d", max, value) 298 } 299 counts[value] += 1 300 } 301 302 expected := repeats / (max + 1) 303 304 for i := 0; i < max; i++ { 305 if counts[i] < (expected/10)*8 { 306 t.Logf("max = %d, counts = %+v", max, counts) 307 t.Fatalf("unexpected low count: max = %d, i = %d, count = %d", max, i, counts[i]) 308 } 309 } 310 } 311 } 312 313 func TestExpFloat64Range(t *testing.T) { 314 315 testCases := []struct { 316 min, max, lambda float64 317 factor int 318 }{ 319 {1.0, 3.0, 2.0, 5}, 320 {0.0, 1.0, 2.0, 5}, 321 {-2.0, -1.0, 2.0, 5}, 322 } 323 324 for _, testCase := range testCases { 325 t.Run(fmt.Sprintf("ExpFloat64Range case: %+v", testCase), func(t *testing.T) { 326 327 p, err := NewPRNG() 328 if err != nil { 329 t.Fatalf("NewPRNG failed: %s", err) 330 } 331 332 buckets := make(map[float64]int) 333 334 for i := 0; i < 100000; i++ { 335 336 value := p.ExpFloat64Range(testCase.min, testCase.max, testCase.lambda) 337 338 if value < testCase.min || value > testCase.max { 339 t.Fatalf( 340 "unexpected value: %f [%f, %f]", value, testCase.min, testCase.max) 341 } 342 343 buckets[float64(int(10.0*(value)))/10.0] += 1 344 } 345 346 keys := make([]float64, 0) 347 for k := range buckets { 348 keys = append(keys, k) 349 } 350 351 sort.Float64s(keys) 352 353 strs := make([]string, 0) 354 for _, k := range keys { 355 strs = append(strs, fmt.Sprintf("%0.2f: %d", k, buckets[k])) 356 } 357 358 t.Logf(strings.Join(strs, ",")) 359 360 for i := 0; i < len(keys)-1; i++ { 361 if buckets[keys[i]] <= buckets[keys[i+1]] { 362 t.Fatalf("unexpected distribution") 363 } 364 } 365 366 // First bucket should have at least "factor" times more items than last 367 // bucket. 368 if buckets[keys[0]]/buckets[keys[len(keys)-1]] < testCase.factor { 369 t.Fatalf("unexpected distribution") 370 } 371 372 }) 373 } 374 } 375 376 //lint:ignore U1000 intentionally unused 377 func Disabled_TestRandomStreamLimit(t *testing.T) { 378 379 // This test takes up to ~2 minute to complete, so it's disabled by default. 380 381 p, err := NewPRNG() 382 if err != nil { 383 t.Fatalf("NewPRNG failed: %s", err) 384 } 385 386 // Use large blocks to get close to the key stream limit. 387 388 var b [2 * 1024 * 1024 * 1024]byte 389 n := int64(0) 390 391 for i := 0; i < 127; i++ { 392 p.Read(b[:]) 393 n += int64(len(b)) 394 } 395 396 // Stop using large blocks 64 bytes short of the limit, 2^38-64. 397 398 p.Read(b[0 : len(b)-128]) 399 n += int64(len(b) - 128) 400 401 // Invoke byte at a time across the limit boundary to ensure we 402 // don't jump over the limit case. 403 404 for i := 0; i < 192; i++ { 405 p.Read(b[0:1]) 406 n += int64(1) 407 } 408 } 409 410 func BenchmarkIntn(b *testing.B) { 411 412 p, err := NewPRNG() 413 if err != nil { 414 b.Fatalf("NewPRNG failed: %s", err) 415 } 416 417 max := 255 418 419 b.Run("PRNG", func(b *testing.B) { 420 for n := 0; n < b.N; n++ { 421 _ = p.Intn(n % max) 422 } 423 }) 424 425 b.Run("getrandom()", func(b *testing.B) { 426 for n := 0; n < b.N; n++ { 427 428 _, err := crypto_rand.Int(crypto_rand.Reader, big.NewInt(int64(max))) 429 if err != nil { 430 b.Fatalf("crypto_rand.Int failed: %s", err) 431 } 432 } 433 }) 434 }