mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 23:28:48 +00:00
Merge pull request #3462 from Yawning/feature/math-sys-rand
core:math/rand: Use `crypto.rand_bytes()` for the system RNG
This commit is contained in:
@@ -27,6 +27,8 @@ tests/documentation/all.odin-doc
|
|||||||
tests/internal/test_map
|
tests/internal/test_map
|
||||||
tests/internal/test_pow
|
tests/internal/test_pow
|
||||||
tests/internal/test_rtti
|
tests/internal/test_rtti
|
||||||
|
tests/core/test_base64
|
||||||
|
tests/core/test_cbor
|
||||||
tests/core/test_core_compress
|
tests/core/test_core_compress
|
||||||
tests/core/test_core_container
|
tests/core/test_core_container
|
||||||
tests/core/test_core_filepath
|
tests/core/test_core_filepath
|
||||||
@@ -40,8 +42,10 @@ tests/core/test_core_net
|
|||||||
tests/core/test_core_os_exit
|
tests/core/test_core_os_exit
|
||||||
tests/core/test_core_reflect
|
tests/core/test_core_reflect
|
||||||
tests/core/test_core_strings
|
tests/core/test_core_strings
|
||||||
|
tests/core/test_core_time
|
||||||
tests/core/test_crypto
|
tests/core/test_crypto
|
||||||
tests/core/test_hash
|
tests/core/test_hash
|
||||||
|
tests/core/test_hex
|
||||||
tests/core/test_hxa
|
tests/core/test_hxa
|
||||||
tests/core/test_json
|
tests/core/test_json
|
||||||
tests/core/test_linalg_glsl_math
|
tests/core/test_linalg_glsl_math
|
||||||
|
|||||||
@@ -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
|
// the system entropy source. This routine will block if the system entropy
|
||||||
// source is not ready yet. All system entropy source failures are treated
|
// source is not ready yet. All system entropy source failures are treated
|
||||||
// as catastrophic, resulting in a panic.
|
// 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) {
|
rand_bytes :: proc (dst: []byte) {
|
||||||
// zero-fill the buffer first
|
// zero-fill the buffer first
|
||||||
mem.zero_explicit(raw_data(dst), len(dst))
|
mem.zero_explicit(raw_data(dst), len(dst))
|
||||||
|
|
||||||
_rand_bytes(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,14 +3,13 @@ package crypto
|
|||||||
|
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
arc4random_buf(raw_data(dst), len(dst))
|
arc4random_buf(raw_data(dst), len(dst))
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc() -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import "core:fmt"
|
|||||||
import CF "core:sys/darwin/CoreFoundation"
|
import CF "core:sys/darwin/CoreFoundation"
|
||||||
import Sec "core:sys/darwin/Security"
|
import Sec "core:sys/darwin/Security"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
||||||
if err != .Success {
|
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))
|
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc() -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@
|
|||||||
//+build !js
|
//+build !js
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: false
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc() -> bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,12 @@ foreign odin_env {
|
|||||||
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_MAX_PER_CALL_BYTES :: 65536 // 64kiB
|
_MAX_PER_CALL_BYTES :: 65536 // 64kiB
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
dst := dst
|
dst := dst
|
||||||
|
|
||||||
@@ -18,7 +22,3 @@ _rand_bytes :: proc(dst: []byte) {
|
|||||||
dst = dst[to_read:]
|
dst = dst[to_read:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc() -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import "core:fmt"
|
|||||||
|
|
||||||
import "core:sys/linux"
|
import "core:sys/linux"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc (dst: []byte) {
|
_rand_bytes :: proc (dst: []byte) {
|
||||||
dst := dst
|
dst := dst
|
||||||
l := len(dst)
|
l := len(dst)
|
||||||
@@ -34,7 +38,3 @@ _rand_bytes :: proc (dst: []byte) {
|
|||||||
dst = dst[n_read:]
|
dst = dst[n_read:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc() -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import win32 "core:sys/windows"
|
|||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||||
if ret != os.ERROR_NONE {
|
if ret != os.ERROR_NONE {
|
||||||
@@ -21,7 +24,3 @@ _rand_bytes :: proc(dst: []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_has_rand_bytes :: proc() -> bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
+21
-18
@@ -5,6 +5,7 @@ Package core:math/rand implements various random number generators
|
|||||||
package rand
|
package rand
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
import "core:crypto"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:mem"
|
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.
|
Initialises a random number generator to use the system random number generator.
|
||||||
The system random number generator is platform specific.
|
The system random number generator is platform specific, and not supported
|
||||||
On `linux` refer to the `getrandom` syscall.
|
on all targets.
|
||||||
On `darwin` refer to `getentropy`.
|
|
||||||
On `windows` refer to `BCryptGenRandom`.
|
|
||||||
|
|
||||||
All other platforms are not supported
|
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
- r: The random number generator to use the system random number generator
|
- 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:
|
Example:
|
||||||
|
import "core:crypto"
|
||||||
import "core:math/rand"
|
import "core:math/rand"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
init_as_system_example :: proc() {
|
init_as_system_example :: proc() {
|
||||||
my_rand: rand.Rand
|
my_rand: rand.Rand
|
||||||
rand.init_as_system(&my_rand)
|
switch crypto.HAS_RAND_BYTES {
|
||||||
fmt.println(rand.uint64(&my_rand))
|
case true:
|
||||||
|
rand.init_as_system(&my_rand)
|
||||||
|
fmt.println(rand.uint64(&my_rand))
|
||||||
|
case false:
|
||||||
|
fmt.println("system random not supported!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Possible Output:
|
Possible Output:
|
||||||
@@ -133,7 +137,7 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
init_as_system :: proc(r: ^Rand) {
|
init_as_system :: proc(r: ^Rand) {
|
||||||
if !#defined(_system_random) {
|
if !crypto.HAS_RAND_BYTES {
|
||||||
panic(#procedure + " is not supported on this platform yet")
|
panic(#procedure + " is not supported on this platform yet")
|
||||||
}
|
}
|
||||||
r.state = 0
|
r.state = 0
|
||||||
@@ -144,15 +148,14 @@ init_as_system :: proc(r: ^Rand) {
|
|||||||
@(private)
|
@(private)
|
||||||
_random_u64 :: proc(r: ^Rand) -> u64 {
|
_random_u64 :: proc(r: ^Rand) -> u64 {
|
||||||
r := r
|
r := r
|
||||||
if r == nil {
|
switch {
|
||||||
|
case r == nil:
|
||||||
r = &global_rand
|
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
|
old_state := r.state
|
||||||
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -277,7 +277,7 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
|
|||||||
test_rand_bytes :: proc(t: ^testing.T) {
|
test_rand_bytes :: proc(t: ^testing.T) {
|
||||||
tc.log(t, "Testing rand_bytes")
|
tc.log(t, "Testing rand_bytes")
|
||||||
|
|
||||||
if !crypto.has_rand_bytes() {
|
if !crypto.HAS_RAND_BYTES {
|
||||||
tc.log(t, "rand_bytes not supported - skipping")
|
tc.log(t, "rand_bytes not supported - skipping")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user