Browse Source

Merge pull request #3462 from Yawning/feature/math-sys-rand

core:math/rand: Use `crypto.rand_bytes()` for the system RNG
gingerBill 1 year ago
parent
commit
9d3f835e31

+ 4 - 0
.gitignore

@@ -27,6 +27,8 @@ tests/documentation/all.odin-doc
 tests/internal/test_map
 tests/internal/test_pow
 tests/internal/test_rtti
+tests/core/test_base64
+tests/core/test_cbor
 tests/core/test_core_compress
 tests/core/test_core_container
 tests/core/test_core_filepath
@@ -40,8 +42,10 @@ tests/core/test_core_net
 tests/core/test_core_os_exit
 tests/core/test_core_reflect
 tests/core/test_core_strings
+tests/core/test_core_time
 tests/core/test_crypto
 tests/core/test_hash
+tests/core/test_hex
 tests/core/test_hxa
 tests/core/test_json
 tests/core/test_linalg_glsl_math

+ 3 - 6
core/crypto/crypto.odin

@@ -49,15 +49,12 @@ compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> i
 // the system entropy source.  This routine will block if the system entropy
 // source is not ready yet.  All system entropy source failures are treated
 // as catastrophic, resulting in a panic.
+//
+// Support for the system entropy source can be checked with the
+// `HAS_RAND_BYTES` boolean constant.
 rand_bytes :: proc (dst: []byte) {
 	// zero-fill the buffer first
 	mem.zero_explicit(raw_data(dst), len(dst))
 
 	_rand_bytes(dst)
 }
-
-// has_rand_bytes returns true iff the target has support for accessing the
-// system entropty source.
-has_rand_bytes :: proc () -> bool {
-	return _has_rand_bytes()
-}

+ 3 - 4
core/crypto/rand_bsd.odin

@@ -3,14 +3,13 @@ package crypto
 
 foreign import libc "system:c"
 
+HAS_RAND_BYTES :: true
+
 foreign libc {
 	arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
 }
 
+@(private)
 _rand_bytes :: proc(dst: []byte) {
 	arc4random_buf(raw_data(dst), len(dst))
 }
-
-_has_rand_bytes :: proc() -> bool {
-	return true
-}

+ 3 - 4
core/crypto/rand_darwin.odin

@@ -5,6 +5,9 @@ import "core:fmt"
 import CF "core:sys/darwin/CoreFoundation"
 import Sec "core:sys/darwin/Security"
 
+HAS_RAND_BYTES :: true
+
+@(private)
 _rand_bytes :: proc(dst: []byte) {
 	err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
 	if err != .Success {
@@ -12,7 +15,3 @@ _rand_bytes :: proc(dst: []byte) {
         panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg))
 	}
 }
-
-_has_rand_bytes :: proc() -> bool {
-	return true
-}

+ 3 - 4
core/crypto/rand_generic.odin

@@ -6,10 +6,9 @@
 //+build !js
 package crypto
 
+HAS_RAND_BYTES :: false
+
+@(private)
 _rand_bytes :: proc(dst: []byte) {
 	unimplemented("crypto: rand_bytes not supported on this OS")
 }
-
-_has_rand_bytes :: proc() -> bool {
-	return false
-}

+ 4 - 4
core/crypto/rand_js.odin

@@ -6,8 +6,12 @@ foreign odin_env {
 	env_rand_bytes :: proc "contextless" (buf: []byte) ---
 }
 
+HAS_RAND_BYTES :: true
+
+@(private)
 _MAX_PER_CALL_BYTES :: 65536 // 64kiB
 
+@(private)
 _rand_bytes :: proc(dst: []byte) {
 	dst := dst
 
@@ -18,7 +22,3 @@ _rand_bytes :: proc(dst: []byte) {
 		dst = dst[to_read:]
 	}
 }
-
-_has_rand_bytes :: proc() -> bool {
-	return true
-}

+ 4 - 4
core/crypto/rand_linux.odin

@@ -4,8 +4,12 @@ import "core:fmt"
 
 import "core:sys/linux"
 
+HAS_RAND_BYTES :: true
+
+@(private)
 _MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
 
+@(private)
 _rand_bytes :: proc (dst: []byte) {
 	dst := dst
 	l := len(dst)
@@ -34,7 +38,3 @@ _rand_bytes :: proc (dst: []byte) {
 		dst = dst[n_read:]
 	}
 }
-
-_has_rand_bytes :: proc() -> bool {
-	return true
-}

+ 3 - 4
core/crypto/rand_windows.odin

@@ -4,6 +4,9 @@ import win32 "core:sys/windows"
 import "core:os"
 import "core:fmt"
 
+HAS_RAND_BYTES :: true
+
+@(private)
 _rand_bytes :: proc(dst: []byte) {
 	ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
 	if ret != os.ERROR_NONE {
@@ -21,7 +24,3 @@ _rand_bytes :: proc(dst: []byte) {
 		}
 	}
 }
-
-_has_rand_bytes :: proc() -> bool {
-	return true
-}

+ 21 - 18
core/math/rand/rand.odin

@@ -5,6 +5,7 @@ Package core:math/rand implements various random number generators
 package rand
 
 import "base:intrinsics"
+import "core:crypto"
 import "core:math"
 import "core:mem"
 
@@ -104,27 +105,30 @@ init :: proc(r: ^Rand, seed: u64) {
 }
 
 /*
-Initialises a random number generator to use the system random number generator.  
-The system random number generator is platform specific.  
-On `linux` refer to the `getrandom` syscall.  
-On `darwin` refer to `getentropy`.  
-On `windows` refer to `BCryptGenRandom`.
-
-All other platforms are not supported
+Initialises a random number generator to use the system random number generator.
+The system random number generator is platform specific, and not supported
+on all targets.
 
 Inputs:
 - r: The random number generator to use the system random number generator
 
-WARNING: Panics if the system is not either `windows`, `darwin` or `linux`
+WARNING: Panics if the system random number generator is not supported.
+Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant.
 
 Example:
+	import "core:crypto"
 	import "core:math/rand"
 	import "core:fmt"
 
 	init_as_system_example :: proc() {
 		my_rand: rand.Rand
-		rand.init_as_system(&my_rand)
-		fmt.println(rand.uint64(&my_rand))
+		switch crypto.HAS_RAND_BYTES {
+		case true:
+			rand.init_as_system(&my_rand)
+			fmt.println(rand.uint64(&my_rand))
+		case false:
+			fmt.println("system random not supported!")
+		}
 	}
 
 Possible Output:
@@ -133,7 +137,7 @@ Possible Output:
 
 */
 init_as_system :: proc(r: ^Rand) {
-	if !#defined(_system_random) {
+	if !crypto.HAS_RAND_BYTES {
 		panic(#procedure + " is not supported on this platform yet")
 	}
 	r.state = 0
@@ -144,15 +148,14 @@ init_as_system :: proc(r: ^Rand) {
 @(private)
 _random_u64 :: proc(r: ^Rand) -> u64 {
 	r := r
-	if r == nil {
+	switch {
+	case r == nil:
 		r = &global_rand
+	case r.is_system:
+		value: u64
+		crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)])
+		return value
 	}
-	when #defined(_system_random) {
-		if r.is_system {
-			return _system_random()
-		}
-	}
-
 
 	old_state := r.state
 	r.state = old_state * 6364136223846793005 + (r.inc|1)

+ 0 - 22
core/math/rand/system_darwin.odin

@@ -1,22 +0,0 @@
-package rand
-
-import "core:sys/darwin"
-
-@(require_results)
-_system_random :: proc() -> u64 {
-	for {
-		value: u64
-		ret := darwin.syscall_getentropy(([^]u8)(&value), size_of(value))
-		if ret < 0 {
-			switch ret {
-			case -4: // EINTR
-				continue
-			case -78: // ENOSYS
-				panic("getentropy not available in kernel")
-			case:
-				panic("getentropy failed")
-			}
-		}
-		return value
-	}
-}

+ 0 - 14
core/math/rand/system_js.odin

@@ -1,14 +0,0 @@
-package rand
-
-foreign import "odin_env"
-foreign odin_env {
-	@(link_name = "rand_bytes")
-	env_rand_bytes :: proc "contextless" (buf: []byte) ---
-}
-
-@(require_results)
-_system_random :: proc() -> u64 {
-	buf: [8]u8
-	env_rand_bytes(buf[:])
-	return transmute(u64)buf
-}

+ 0 - 29
core/math/rand/system_linux.odin

@@ -1,29 +0,0 @@
-package rand
-
-import "core:sys/linux"
-
-@(require_results)
-_system_random :: proc() -> u64 {
-	for {
-		value: u64
-		value_buf := (cast([^]u8)&value)[:size_of(u64)]
-		_, errno := linux.getrandom(value_buf, {})
-		#partial switch errno {
-		case .NONE:
-			// Do nothing
-		case .EINTR: 
-			// Call interupted by a signal handler, just retry the request.
-			continue
-		case .ENOSYS: 
-			// The kernel is apparently prehistoric (< 3.17 circa 2014)
-			// and does not support getrandom.
-			panic("getrandom not available in kernel")
-		case:
-			// All other failures are things that should NEVER happen
-			// unless the kernel interface changes (ie: the Linux
-			// developers break userland).
-			panic("getrandom failed")
-		}
-		return value
-	}
-}

+ 0 - 13
core/math/rand/system_windows.odin

@@ -1,13 +0,0 @@
-package rand
-
-import win32 "core:sys/windows"
-
-@(require_results)
-_system_random :: proc() -> u64 {
-	value: u64
-	status := win32.BCryptGenRandom(nil, ([^]u8)(&value), size_of(value), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
-	if status < 0 {
-		panic("BCryptGenRandom failed")
-	}
-	return value
-}

+ 1 - 1
tests/core/crypto/test_core_crypto.odin

@@ -277,7 +277,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
 test_rand_bytes :: proc(t: ^testing.T) {
 	tc.log(t, "Testing rand_bytes")
 
-	if !crypto.has_rand_bytes() {
+	if !crypto.HAS_RAND_BYTES {
 		tc.log(t, "rand_bytes not supported - skipping")
 		return
 	}