diff --git a/randombytes/randombytes.go b/randombytes/randombytes.go index 1adc104..0a6609f 100644 --- a/randombytes/randombytes.go +++ b/randombytes/randombytes.go @@ -7,77 +7,85 @@ import "C" import "github.com/GoKillers/libsodium-go/support" import "unsafe" -// RandomBytesSeedBytes returns the number of bytes required +// SeedBytes represents the number of bytes required // for seeding RandomBytesBufDeterministic. -func RandomBytesSeedBytes() int { - return int(C.randombytes_seedbytes()) -} +const SeedBytes = C.randombytes_SEEDBYTES + +// BytesMax represents the maximum number of random bytes returned +const BytesMax = C.randombytes_BYTES_MAX -// RandomBytes returns a specified number of random bytes. -// It is essentially a wrapper around RandomBytesBuf for convenience. +// Bytes returns a specified number of random bytes. +// It is essentially a wrapper around Read for convenience. // Note that this behaviour is different than in NaCl and libsodium, -// where this function behaves the same as RandomBytesBuf. -func RandomBytes(size int) []byte { +// where this function behaves the same as Read. +func Bytes(size int) []byte { buf := make([]byte, size) - RandomBytesBuf(buf) + Read(buf) return buf } -// RandomBytesBuf fills a buffer with random bytes. -func RandomBytesBuf(buf []byte) { - if len(buf) > 0 { - C.randombytes_buf(unsafe.Pointer(&buf[0]), C.size_t(len(buf))) - } +// Read fills a buffer with random bytes. +func Read(buf []byte) { + support.CheckSizeMax(buf, BytesMax, "buffer") + C.randombytes_buf( + unsafe.Pointer(support.BytePointer(buf)), + C.size_t(len(buf))) } -// RandomBytesBufDeterministic fills a buffer with bytes that are +// ReadDeterministic fills a buffer with bytes that are // indistinguishable from random bytes without knowing seed. -func RandomBytesBufDeterministic(buf []byte, seed []byte) { - support.CheckSize(seed, RandomBytesSeedBytes(), "seed") - if len(buf) > 0 { - C.randombytes_buf_deterministic( - unsafe.Pointer(&buf[0]), - C.size_t(len(buf)), - (*C.uchar)(&seed[0])) - } +func ReadDeterministic(buf, seed []byte) { + support.CheckSizeMax(buf, BytesMax, "buffer") + support.CheckSize(seed, SeedBytes, "seed") + + C.randombytes_buf_deterministic( + unsafe.Pointer(support.BytePointer(buf)), + C.size_t(len(buf)), + (*C.uchar)(&seed[0])) } -// RandomBytesRandom returns a random 32 bit unsigned integer. -func RandomBytesRandom() uint32 { +// Random returns a random 32 bit unsigned integer. +func Random() uint32 { return uint32(C.randombytes_random()) } -// RandomBytesUniform returns a random number between 0 and an upper bound. +// Uniform returns a random number between 0 and an upper bound. // The generated bytes have a uniform distribution between 0 and the upper bound. -func RandomBytesUniform(upperBound uint32) uint32 { +func Uniform(upperBound uint32) uint32 { return uint32(C.randombytes_uniform(C.uint32_t(upperBound))) } -// RandomBytesStir reseeds the random number generator. -func RandomBytesStir() { +// Stir reseeds the random number generator. +func Stir() { C.randombytes_stir() } -// RandomBytesClose deallocates the resources used by the random number generator. -func RandomBytesClose() { +// Close de-allocates the resources used by the random number generator. +func Close() { C.randombytes_close() } -// RandomBytesSetImplementation sets the implementation of the random number generator. -func RandomBytesSetImplementation(impl *C.struct_randombytes_implementation) int { - return int(C.randombytes_set_implementation(impl)) +// SetImplementation sets the implementation of the random number generator. +func SetImplementation(impl Implementation) int { + return int(C.randombytes_set_implementation( + (*C.struct_randombytes_implementation)(impl))) } -// RandomBytesImplementationName returns the name of the random number +// ImplementationName returns the name of the random number // generator that is being used. -func RandomBytesImplementationName() string { +func ImplementationName() string { return C.GoString(C.randombytes_implementation_name()) } -// RandomBytesSalsa20Implementation contains a pointer to C.randombytes_salsa20_implementation -// This means that it can be used as an argument to RandomBytesSetImplementation -var RandomBytesSalsa20Implementation *C.struct_randombytes_implementation = &C.randombytes_salsa20_implementation +// Implementation represents a pointer to a randombytes implementation +type Implementation *C.struct_randombytes_implementation -// RandomBytesSysRandomImplementation contains a pointer to C.randombytes_sysrandom_implementation -// This means that it can be used as an argument to RandomBytesSetImplementation -var RandomBytesSysRandomImplementation *C.struct_randombytes_implementation = &C.randombytes_sysrandom_implementation +// Salsa20Implementation returns the Salsa20 implementation. +func Salsa20Implementation() Implementation { + return Implementation(&C.randombytes_salsa20_implementation) +} + +// SysRandomImplementation returns the SysRandom implementation. +func SysRandomImplementation() Implementation { + return Implementation(&C.randombytes_sysrandom_implementation) +} diff --git a/randombytes/randombytes_deprecated.go b/randombytes/randombytes_deprecated.go new file mode 100644 index 0000000..ea197b4 --- /dev/null +++ b/randombytes/randombytes_deprecated.go @@ -0,0 +1,57 @@ +package randombytes + +// #cgo pkg-config: libsodium +// #include +// #include +import "C" + +// Deprecated: use SeedBytes instead +func RandomBytesSeedBytes() int { + return SeedBytes +} + +// Deprecated: use Bytes() instead +func RandomBytes(size int) []byte { + return Bytes(size) +} + +// Deprecated: use Read() instead +func RandomBytesBuf(buf []byte) { + Read(buf) +} + +// Deprecated: use ReadDeterministic() instead +func RandomBytesBufDeterministic(buf, seed []byte) { + ReadDeterministic(buf, seed) +} + +// Deprecated: use Random() instead +func RandomBytesRandom() uint32 { + return Random() +} + +// Deprecated: use Uniform() instead +func RandomBytesUniform(upperBound uint32) uint32 { + return Uniform(upperBound) +} + +// Deprecated: use Stir() instead +func RandomBytesStir() { + Stir() +} + +// Deprecated: use Close() instead +func RandomBytesClose() { + Close() +} + +// Deprecated: use SetImplementation() instead +func RandomBytesSetImplementation(impl *C.struct_randombytes_implementation) int { + return SetImplementation(Implementation(impl)) +} + +// Deprecated: use Salsa20Implementation() instead +var RandomBytesSalsa20Implementation *C.struct_randombytes_implementation = &C.randombytes_salsa20_implementation + +// Deprecated: use SysRandomImplementation() instead +var RandomBytesSysRandomImplementation *C.struct_randombytes_implementation = &C.randombytes_sysrandom_implementation diff --git a/randombytes/randombytes_test.go b/randombytes/randombytes_test.go new file mode 100644 index 0000000..5ef5555 --- /dev/null +++ b/randombytes/randombytes_test.go @@ -0,0 +1,115 @@ +package randombytes + +import ( + "bytes" + "github.com/google/gofuzz" + "testing" +) + +// TestRandomBytes tests if two generated byte slices are the same +func TestRandomBytesBuf(t *testing.T) { + var a, b []byte + + for i := 0; i < 10000; i++ { + // Give a a random size + f := fuzz.New().NumElements(4, 20) + f.Fuzz(&a) + + // Fill a and b with random bytes + a = Bytes(len(a)) + b = make([]byte, len(a)) + Read(b) + + if bytes.Equal(a, b) && len(a) > 0 { + t.Errorf("Two random byte slices are the same: %v", a) + } + } +} + +// TestRandomBytesRandom checks if two generated integers are the same +func TestRandomBytesRandom(t *testing.T) { + var a, b uint32 + + a = Random() + b = Random() + if a == b { + t.Errorf("Two random integers are the same: %v", a) + } +} + +// TestRandomBytesUniform checks if the generated bytes are below the upper bound. +func TestRandomBytesUniform(t *testing.T) { + var upper uint32 + f := fuzz.New() + + for i := 0; i < 10000; i++ { + // Create a random upper bound + f.Fuzz(&upper) + + // Generate bytes + v := Uniform(upper) + + // Check + if v >= upper && v > 0 { + t.Errorf("Random value %v is greater than upper boudn %v", v, upper) + } + } +} + +// TestRandomBytesBuf checks if bytes are indeed deterministic. +// The bytes should be different with a different seed. +func TestRandomBytesBufDeterministic(t *testing.T) { + var a, b, c []byte + + f := fuzz.New().NumElements(4, 100) + + for i := 0; i < 10000; i++ { + seed := Bytes(SeedBytes) + + // Give a & b the same random length. + f.Fuzz(&a) + b = a + + ReadDeterministic(a, seed) + Stir() + ReadDeterministic(b, seed) + + if !bytes.Equal(a, b) { + t.Error("Deterministic bytes are random.") + } + + if bytes.Equal(a, c) && len(a) > 0 { + t.Error("Deterministic bytes are the same with a different seed") + } + + c = a + } +} + +// TestRandomBytesSetImplementation tests if the implementation switches, +// and if all functions work with the other implementation. +func TestRandomBytesSetImplementation(t *testing.T) { + implementations := make(map[string](Implementation)) + implementations["salsa20"] = Salsa20Implementation() + implementations["sysrandom"] = SysRandomImplementation() + + // Loop through all implementations + for n, i := range implementations { + SetImplementation(i) + if ImplementationName() != n { + t.Errorf( + "Incorrect implementation %v should be %v", + ImplementationName(), + n, + ) + } + + // Run tests for this implementation + Stir() + TestRandomBytesBuf(t) + TestRandomBytesRandom(t) + TestRandomBytesUniform(t) + TestRandomBytesBufDeterministic(t) + Close() + } +} diff --git a/support/support.go b/support/support.go index a3eeb90..886ab2c 100644 --- a/support/support.go +++ b/support/support.go @@ -18,7 +18,15 @@ func CheckSize(buf []byte, expected int, descrip string) { // and panics when this is not the case. func CheckSizeMin(buf []byte, min int, descrip string) { if len(buf) < min { - panic(fmt.Sprintf("Incorrect %s buffer size, expected (>%d), got (%d).", descrip, min, len(buf))) + panic(fmt.Sprintf("Incorrect %s buffer size, expected (>=%d), got (%d).", descrip, min, len(buf))) + } +} + +// CheckSizeMax checks if the length of a byte slice is less or equal than a minimum length, +// and panics when this is not the case. +func CheckSizeMax(buf []byte, max uint64, descrip string) { + if uint64(len(buf)) > max { + panic(fmt.Sprintf("Incorrect %s buffer size, expected (<=%d), got (%d).", descrip, max, len(buf))) } }