diff --git a/element_unchecked.go b/element_unchecked.go
index b887b6e9f98bd9cadefeaf35ccba2e1fe1671e95..1d4297199c348b331db74b40e9f39371409bc8d9 100644
--- a/element_unchecked.go
+++ b/element_unchecked.go
@@ -75,7 +75,7 @@ func (el *elementImpl) SetString(s string, base int) (Element, bool) {
 }
 
 func (el *elementImpl) BigInt() *big.Int {
-	mpz := newmpz()
+	mpz := newMpz()
 	C.element_to_mpz(&mpz[0], el.data)
 	return mpz2big(mpz)
 }
diff --git a/errors.go b/errors.go
index bc5006fdc8789cb32dd04dd0527d5fa23ac19864..5ae343dc835d6304732fda60384d5175cf40e81f 100644
--- a/errors.go
+++ b/errors.go
@@ -11,6 +11,8 @@ var (
 	ErrBadPairList        = errors.New("pairing product list is in an invalid format")
 	ErrBadInput           = errors.New("invalid element format during scan")
 	ErrBadVerb            = errors.New("invalid verb specified for scan")
+	ErrIllegalNil         = errors.New("received nil when non-nil was expected")
 	ErrOutOfRange         = errors.New("index out of range")
+	ErrEntropyFailure     = errors.New("error while reading from entropy source")
 	ErrInternal           = errors.New("a severe internal error has lead to possible memory corruption")
 )
diff --git a/gmp_big.go b/gmp_big.go
index 7b5561770d45ce54b54449a47fadc8c3db0817da..0668c77304693e4359f7d53c3424cc0a8e9b0dd2 100644
--- a/gmp_big.go
+++ b/gmp_big.go
@@ -14,34 +14,42 @@ import (
 var wordSize C.size_t
 var bitsPerWord C.size_t
 
-func clearmpz(x *C.mpz_t) {
+func clearMpz(x *C.mpz_t) {
 	println("clearmpz")
 	C.mpz_clear(&x[0])
 }
 
-func newmpz() *C.mpz_t {
+func newMpz() *C.mpz_t {
 	out := &C.mpz_t{}
 	C.mpz_init(&out[0])
-	runtime.SetFinalizer(out, clearmpz)
+	runtime.SetFinalizer(out, clearMpz)
 	return out
 }
 
-func big2mpz(num *big.Int) *C.mpz_t {
+func big2thisMpz(num *big.Int, out *C.mpz_t) {
 	words := num.Bits()
-	out := newmpz()
 	if len(words) > 0 {
 		C.mpz_import(&out[0], C.size_t(len(words)), -1, wordSize, 0, 0, unsafe.Pointer(&words[0]))
 	}
+}
+
+func big2mpz(num *big.Int) *C.mpz_t {
+	out := newMpz()
+	big2thisMpz(num, out)
 	return out
 }
 
-func mpz2big(num *C.mpz_t) (out *big.Int) {
+func mpz2thisBig(num *C.mpz_t, out *big.Int) {
 	wordsNeeded := (C.mpz_sizeinbase(&num[0], 2) + (bitsPerWord - 1)) / bitsPerWord
 	words := make([]big.Word, wordsNeeded)
 	var wordsWritten C.size_t
 	C.mpz_export(unsafe.Pointer(&words[0]), &wordsWritten, -1, wordSize, 0, 0, &num[0])
-	out = &big.Int{}
 	out.SetBits(words)
+}
+
+func mpz2big(num *C.mpz_t) (out *big.Int) {
+	out = &big.Int{}
+	mpz2thisBig(num, out)
 	return
 }
 
diff --git a/rand_hook.c b/rand_hook.c
new file mode 100644
index 0000000000000000000000000000000000000000..809c39477721ab850a5eeffdaae2bffce0f007f0
--- /dev/null
+++ b/rand_hook.c
@@ -0,0 +1,17 @@
+#include "_cgo_export.h"
+#include <pbc/pbc.h>
+
+void pbc_init_random();
+
+void goRandomHook(mpz_t out, mpz_t limit, void* data) {
+	UNUSED_VAR(data);
+	goGenerateRandom(out, limit);
+}
+
+void installRandomHook() {
+	pbc_random_set_function(goRandomHook, NULL);
+}
+
+void uninstallRandomHook() {
+	pbc_init_random();
+}
\ No newline at end of file
diff --git a/utils.go b/utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..656a5e17bfab9f68b812223c8a433a69597b6ceb
--- /dev/null
+++ b/utils.go
@@ -0,0 +1,95 @@
+package pbc
+
+/*
+#include <pbc/pbc.h>
+
+void installRandomHook();
+void uninstallRandomHook();
+*/
+import "C"
+
+import (
+	cryptorand "crypto/rand"
+	"io"
+	"math/big"
+	"math/rand"
+	"unsafe"
+)
+
+var logging bool
+
+func Logging() bool { return logging }
+
+func SetLogging(log bool) {
+	logging = log
+	if log {
+		C.pbc_set_msg_to_stderr(C.int(1))
+	} else {
+		C.pbc_set_msg_to_stderr(C.int(0))
+	}
+}
+
+type RandomSource interface {
+	Rand(limit *big.Int) *big.Int
+}
+
+var randomProvider RandomSource
+
+func RandomProvider() RandomSource { return randomProvider }
+
+func SetRandomProvider(provider RandomSource) {
+	randomProvider = provider
+	if provider == nil {
+		C.uninstallRandomHook()
+	} else {
+		C.installRandomHook()
+	}
+}
+
+//export goGenerateRandom
+func goGenerateRandom(out, limit unsafe.Pointer) {
+	outPtr := (*C.mpz_t)(out)
+	limitPtr := (*C.mpz_t)(limit)
+	r := randomProvider.Rand(mpz2big(limitPtr))
+	big2thisMpz(r, outPtr)
+}
+
+type readerProvider struct {
+	source io.Reader
+}
+
+func (provider readerProvider) Rand(limit *big.Int) (result *big.Int) {
+	result, err := cryptorand.Int(provider.source, limit)
+	if err != nil {
+		panic(ErrEntropyFailure)
+	}
+	return
+}
+
+type randProvider struct {
+	source *rand.Rand
+}
+
+func (provider randProvider) Rand(limit *big.Int) (result *big.Int) {
+	result = &big.Int{}
+	result.Rand(provider.source, limit)
+	return
+}
+
+func SetCryptoRandom() { SetReaderRandom(cryptorand.Reader) }
+
+func SetReaderRandom(reader io.Reader) {
+	if reader == nil {
+		panic(ErrIllegalNil)
+	}
+	SetRandomProvider(&readerProvider{reader})
+}
+
+func SetRandRandom(rand *rand.Rand) { SetRandomProvider(&randProvider{rand}) }
+
+func SetDefaultRandom() { SetRandomProvider(nil) }
+
+func init() {
+	SetLogging(false)
+	SetDefaultRandom()
+}