|
|
@@ -0,0 +1,96 @@
|
|
|
+// 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 util
|
|
|
+
|
|
|
+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()
|
|
|
+}
|