syncthing/lib/rand/securesource_test.go
Jakob Borg e6b78e5d56 lib/rand: Break out random functions into separate package
The intention for this package is to provide a combination of the
security of crypto/rand and the convenience of math/rand. It should be
the first choice of random data unless ultimate performance is required
and the usage is provably irrelevant from a security standpoint.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3186
2016-05-26 07:02:56 +00:00

97 lines
2.4 KiB
Go

// Copyright (C) 2016 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
package rand
import "testing"
func TestSecureSource(t *testing.T) {
// This is not a test to verify that the random numbers are secure,
// merely that the numbers look random at all and that we've haven't
// broken the implementation by masking off half the bits or always
// returning "4" (chosen by fair dice roll),
const nsamples = 10000
// Create a new source and sample values from it.
s := newSecureSource()
res0 := make([]int64, nsamples)
for i := range res0 {
res0[i] = s.Int63()
}
// Do it again
s = newSecureSource()
res1 := make([]int64, nsamples)
for i := range res1 {
res1[i] = s.Int63()
}
// There should (statistically speaking) be no repetition of the values,
// neither within the samples from a source nor between sources.
for _, v0 := range res0 {
for _, v1 := range res1 {
if v0 == v1 {
t.Errorf("Suspicious coincidence, %d repeated between res0/res1", v0)
}
}
}
for i, v0 := range res0 {
for _, v1 := range res0[i+1:] {
if v0 == v1 {
t.Errorf("Suspicious coincidence, %d repeated within res0", v0)
}
}
}
for i, v0 := range res1 {
for _, v1 := range res1[i+1:] {
if v0 == v1 {
t.Errorf("Suspicious coincidence, %d repeated within res1", v0)
}
}
}
// Count how many times each bit was set. On average each bit ought to
// be set in half of the samples, except the topmost bit which must
// never be set (int63). We raise an alarm if a single bit is set in
// fewer than 1/3 of the samples or more often than 2/3 of the samples.
var bits [64]int
for _, v := range res0 {
for i := range bits {
if v&1 == 1 {
bits[i]++
}
v >>= 1
}
}
for bit, count := range bits {
switch bit {
case 63:
// The topmost bit is never set
if count != 0 {
t.Errorf("The topmost bit was set %d times in %d samples (should be 0)", count, nsamples)
}
default:
if count < nsamples/3 {
t.Errorf("Bit %d was set only %d times out of %d", bit, count, nsamples)
}
if count > nsamples/3*2 {
t.Errorf("Bit %d was set fully %d times out of %d", bit, count, nsamples)
}
}
}
}
var sink int64
func BenchmarkSecureSource(b *testing.B) {
s := newSecureSource()
for i := 0; i < b.N; i++ {
sink = s.Int63()
}
b.ReportAllocs()
}