~samwhited/xmpp

3a55e5d0e2f0879235e7415fcea5d93fb9306f6f — Sam Whited 5 years ago dec7476
Fix (and test) random ID generation issue
2 files changed, 49 insertions(+), 2 deletions(-)

M internal/idgen.go
M internal/idgen_test.go
M internal/idgen.go => internal/idgen.go +15 -1
@@ 7,6 7,7 @@ package internal
import (
	"crypto/rand"
	"fmt"
	"io"
)

const IDLen = 16


@@ 19,10 20,23 @@ const IDLen = 16
// entropy pool isn't initialized, or we can't generate random numbers for some
// other reason, panic.
func RandomID(n int) string {
	return randomID(n, cryptoReader{})
}

func randomID(n int, r io.Reader) string {
	b := make([]byte, (n/2)+(n&1))
	if _, err := rand.Read(b); err != nil {
	switch n, err := r.Read(b); {
	case err != nil:
		panic(err)
	case n != len(b):
		panic("Could not read enough randomness")
	}

	return fmt.Sprintf("%x", b)[:n]
}

type cryptoReader struct{}

func (cryptoReader) Read(p []byte) (int, error) {
	return rand.Read(p)
}

M internal/idgen_test.go => internal/idgen_test.go +34 -1
@@ 4,7 4,10 @@

package internal

import "testing"
import (
	"errors"
	"testing"
)

func BenchmarkRandomIDEven(b *testing.B) {
	for n := 0; n < b.N; n++ {


@@ 26,3 29,33 @@ func TestRandomIDLength(t *testing.T) {
		}
	}
}

type errorReader struct{}

func (errorReader) Read(p []byte) (int, error) {
	return 0, errors.New("Expected error from error reader")
}

type nopReader struct{}

func (nopReader) Read(p []byte) (int, error) {
	return 0, nil
}

func TestRandomPanicsIfRandReadFails(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Error("Expected randomID to panic if reading random bytes failed")
		}
	}()
	randomID(16, errorReader{})
}

func TestRandomPanicsIfRandReadWrongLen(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Error("Expected randomID to panic if no random bytes were read")
		}
	}()
	randomID(16, nopReader{})
}