Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 50 additions & 42 deletions randombytes/randombytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
57 changes: 57 additions & 0 deletions randombytes/randombytes_deprecated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package randombytes

// #cgo pkg-config: libsodium
// #include <stdlib.h>
// #include <sodium.h>
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
115 changes: 115 additions & 0 deletions randombytes/randombytes_test.go
Original file line number Diff line number Diff line change
@@ -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()
}
}
10 changes: 9 additions & 1 deletion support/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
}
}

Expand Down