Browse Source

common: Rand* warnings about cryptographic unsafety

Lesson articulated by @jaekwon on why we need 80 bits
of entropy at least before we can think of cryptographic
safety. math/rand's seed is a max of 64 bits so can never
be cryptographically secure.

Also added some benchmarks for RandBytes
pull/1842/head
Emmanuel Odeke 7 years ago
parent
commit
8638961f02
No known key found for this signature in database GPG Key ID: 1CA47A292F89DD40
2 changed files with 53 additions and 2 deletions
  1. +27
    -2
      common/random.go
  2. +26
    -0
      common/random_test.go

+ 27
- 2
common/random.go View File

@ -35,6 +35,7 @@ func init() {
} }
// Constructs an alphanumeric string of given length. // Constructs an alphanumeric string of given length.
// It is not safe for cryptographic usage.
func RandStr(length int) string { func RandStr(length int) string {
chars := []byte{} chars := []byte{}
MAIN_LOOP: MAIN_LOOP:
@ -58,10 +59,12 @@ MAIN_LOOP:
return string(chars) return string(chars)
} }
// It is not safe for cryptographic usage.
func RandUint16() uint16 { func RandUint16() uint16 {
return uint16(RandUint32() & (1<<16 - 1)) return uint16(RandUint32() & (1<<16 - 1))
} }
// It is not safe for cryptographic usage.
func RandUint32() uint32 { func RandUint32() uint32 {
prng.Lock() prng.Lock()
u32 := prng.Uint32() u32 := prng.Uint32()
@ -69,10 +72,12 @@ func RandUint32() uint32 {
return u32 return u32
} }
// It is not safe for cryptographic usage.
func RandUint64() uint64 { func RandUint64() uint64 {
return uint64(RandUint32())<<32 + uint64(RandUint32()) return uint64(RandUint32())<<32 + uint64(RandUint32())
} }
// It is not safe for cryptographic usage.
func RandUint() uint { func RandUint() uint {
prng.Lock() prng.Lock()
i := prng.Int() i := prng.Int()
@ -80,18 +85,22 @@ func RandUint() uint {
return uint(i) return uint(i)
} }
// It is not safe for cryptographic usage.
func RandInt16() int16 { func RandInt16() int16 {
return int16(RandUint32() & (1<<16 - 1)) return int16(RandUint32() & (1<<16 - 1))
} }
// It is not safe for cryptographic usage.
func RandInt32() int32 { func RandInt32() int32 {
return int32(RandUint32()) return int32(RandUint32())
} }
// It is not safe for cryptographic usage.
func RandInt64() int64 { func RandInt64() int64 {
return int64(RandUint64()) return int64(RandUint64())
} }
// It is not safe for cryptographic usage.
func RandInt() int { func RandInt() int {
prng.Lock() prng.Lock()
i := prng.Int() i := prng.Int()
@ -99,6 +108,7 @@ func RandInt() int {
return i return i
} }
// It is not safe for cryptographic usage.
func RandInt31() int32 { func RandInt31() int32 {
prng.Lock() prng.Lock()
i31 := prng.Int31() i31 := prng.Int31()
@ -106,6 +116,7 @@ func RandInt31() int32 {
return i31 return i31
} }
// It is not safe for cryptographic usage.
func RandInt63() int64 { func RandInt63() int64 {
prng.Lock() prng.Lock()
i63 := prng.Int63() i63 := prng.Int63()
@ -114,6 +125,7 @@ func RandInt63() int64 {
} }
// Distributed pseudo-exponentially to test for various cases // Distributed pseudo-exponentially to test for various cases
// It is not safe for cryptographic usage.
func RandUint16Exp() uint16 { func RandUint16Exp() uint16 {
bits := RandUint32() % 16 bits := RandUint32() % 16
if bits == 0 { if bits == 0 {
@ -125,6 +137,7 @@ func RandUint16Exp() uint16 {
} }
// Distributed pseudo-exponentially to test for various cases // Distributed pseudo-exponentially to test for various cases
// It is not safe for cryptographic usage.
func RandUint32Exp() uint32 { func RandUint32Exp() uint32 {
bits := RandUint32() % 32 bits := RandUint32() % 32
if bits == 0 { if bits == 0 {
@ -136,6 +149,7 @@ func RandUint32Exp() uint32 {
} }
// Distributed pseudo-exponentially to test for various cases // Distributed pseudo-exponentially to test for various cases
// It is not safe for cryptographic usage.
func RandUint64Exp() uint64 { func RandUint64Exp() uint64 {
bits := RandUint32() % 64 bits := RandUint32() % 64
if bits == 0 { if bits == 0 {
@ -146,6 +160,7 @@ func RandUint64Exp() uint64 {
return n return n
} }
// It is not safe for cryptographic usage.
func RandFloat32() float32 { func RandFloat32() float32 {
prng.Lock() prng.Lock()
f32 := prng.Float32() f32 := prng.Float32()
@ -153,17 +168,26 @@ func RandFloat32() float32 {
return f32 return f32
} }
// It is not safe for cryptographic usage.
func RandTime() time.Time { func RandTime() time.Time {
return time.Unix(int64(RandUint64Exp()), 0) return time.Unix(int64(RandUint64Exp()), 0)
} }
// RandBytes returns n random bytes from the OS's source of entropy ie. via crypto/rand. // RandBytes returns n random bytes from the OS's source of entropy ie. via crypto/rand.
// It is not safe for cryptographic usage.
func RandBytes(n int) []byte { func RandBytes(n int) []byte {
return cRandBytes(n)
// cRandBytes isn't guaranteed to be fast so instead
// use random bytes generated from the internal PRNG
bs := make([]byte, n)
for i := 0; i < len(bs); i++ {
bs[i] = byte(RandInt() & 0xFF)
}
return bs
} }
// RandIntn returns, as an int, a non-negative pseudo-random number in [0, n). // RandIntn returns, as an int, a non-negative pseudo-random number in [0, n).
// It panics if n <= 0
// It panics if n <= 0.
// It is not safe for cryptographic usage.
func RandIntn(n int) int { func RandIntn(n int) int {
prng.Lock() prng.Lock()
i := prng.Intn(n) i := prng.Intn(n)
@ -172,6 +196,7 @@ func RandIntn(n int) int {
} }
// RandPerm returns a pseudo-random permutation of n integers in [0, n). // RandPerm returns a pseudo-random permutation of n integers in [0, n).
// It is not safe for cryptographic usage.
func RandPerm(n int) []int { func RandPerm(n int) []int {
prng.Lock() prng.Lock()
perm := prng.Perm(n) perm := prng.Perm(n)


+ 26
- 0
common/random_test.go View File

@ -92,3 +92,29 @@ func TestRngConcurrencySafety(t *testing.T) {
} }
wg.Wait() wg.Wait()
} }
func BenchmarkRandBytes10B(b *testing.B) {
benchmarkRandBytes(b, 10)
}
func BenchmarkRandBytes100B(b *testing.B) {
benchmarkRandBytes(b, 100)
}
func BenchmarkRandBytes1KiB(b *testing.B) {
benchmarkRandBytes(b, 1024)
}
func BenchmarkRandBytes10KiB(b *testing.B) {
benchmarkRandBytes(b, 10*1024)
}
func BenchmarkRandBytes100KiB(b *testing.B) {
benchmarkRandBytes(b, 100*1024)
}
func BenchmarkRandBytes1MiB(b *testing.B) {
benchmarkRandBytes(b, 1024*1024)
}
func benchmarkRandBytes(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
_ = RandBytes(n)
}
b.ReportAllocs()
}

Loading…
Cancel
Save