mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
library/random.system_rng.m:
As above (this code is only used where no system RNG is available,
which is why this hasn't been noticed).
951 lines
27 KiB
Mathematica
951 lines
27 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2021-2022, 2024-2026 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: random.system_rng.m.
|
|
% Main author: Julien Fischer
|
|
% Stability: medium.
|
|
%
|
|
% This module provides access to the system random number generator.
|
|
% This is a platform specific cryptographically secure random number generator
|
|
% that is seeded from the OS entropy pool.
|
|
%
|
|
% The intended use of this generator is to provide small amounts of
|
|
% high-quality random material suitable, for example, for seeding other
|
|
% random number generators. It is not intended for generating large amounts
|
|
% of random material.
|
|
%
|
|
% On the C backends, the system RNG depends on the operating system.
|
|
% For macOS, Cygwin, OpenBSD, NetBSD and versions of FreeBSD from 12 onwards,
|
|
% we use the arc4random() family of functions.
|
|
% For Windows, we use the rand_s() function.
|
|
% For Linux, AIX, Solaris and versions of FreeBSD before 12, we read randomness
|
|
% from /dev/urandom; on these system each open system RNG handle will require
|
|
% an open file descriptor.
|
|
% On other operating systems the system RNG is not available; on these systems
|
|
% attempting to open a system RNG handle will throw an exception.
|
|
%
|
|
% On the C# backend, the system RNG is an instance of the
|
|
% System.Security.Cryptography.RandomNumberGenerator class that uses the
|
|
% default cryptographic random number generator implementation.
|
|
%
|
|
% On the Java backend, the system RNG is an instance of the
|
|
% java.security.SecureRandom class using the default random number algorithm.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module random.system_rng.
|
|
:- interface.
|
|
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% A handle through which the system RNG can be accessed.
|
|
%
|
|
% In general, it is *not* safe to share system RNG handles across
|
|
% multiple threads. In circumstances where multiple threads require access
|
|
% to the system RNG, each thread should open its own handle.
|
|
%
|
|
:- type system_rng.
|
|
|
|
:- instance urandom(system_rng, io).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% True if this platform provides a system RNG.
|
|
%
|
|
:- pred have_system_rng is semidet.
|
|
|
|
% open_system_rng(MaybeHandle, !IO):
|
|
%
|
|
% Returns a handle through which the system RNG can be accessed.
|
|
%
|
|
:- pred open_system_rng(maybe_error(system_rng)::out, io::di, io::uo)
|
|
is det.
|
|
|
|
% close_system_rng(Handle, !IO):
|
|
%
|
|
% Release the system RNG handle along with any OS resources associated
|
|
% with it. Throws an exception if an error occurs.
|
|
%
|
|
:- pred close_system_rng(system_rng::in, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a uniformly distributed unsigned integer of 8, 16, 32 or
|
|
% 64 bits respectively using the system RNG.
|
|
%
|
|
:- pred generate_uint8(system_rng::in, uint8::out, io::di, io::uo) is det.
|
|
:- pred generate_uint16(system_rng::in, uint16::out, io::di, io::uo) is det.
|
|
:- pred generate_uint32(system_rng::in, uint32::out, io::di, io::uo) is det.
|
|
:- pred generate_uint64(system_rng::in, uint64::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module bool.
|
|
:- import_module exception.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_decl("C", "
|
|
|
|
#include \"mercury_conf_param.h\"
|
|
|
|
#if defined(MR_HAVE_SYS_PARAM_H)
|
|
#include <sys/param.h>
|
|
#endif
|
|
|
|
#if defined(MR_MAC_OSX)
|
|
#include <AvailabilityMacros.h>
|
|
#endif
|
|
|
|
// The following macros define if the system random number exists on this
|
|
// system and, if so, how it is accessed.
|
|
//
|
|
// Only one of the following must be defined.
|
|
//
|
|
// ML_SYSRAND_IMPL_ARC4RANDOM
|
|
// the system RNG is implemented by calling the arc4random() family of
|
|
// functions. Note: this for when arc4random() is provided by libc (as on
|
|
// macOS and the BSDs), not for when it is provided as a separate library
|
|
// (e.g. libbsd on Linux).
|
|
//
|
|
// This should only be enabled on systems where arc4random() uses a secure
|
|
// PRNG, such as ChaCha20; it should _not_ be enabled on systems where
|
|
// arc4random() still uses RC4.
|
|
//
|
|
// ML_SYSRAND_IMPL_RAND_S
|
|
// the system RNG is implemented by calling the rand_s() function
|
|
// (Windows only).
|
|
//
|
|
// ML_SYSRAND_IMPL_GETRANDOM (NYI)
|
|
// the system RNG is implemented by calling getrandom() (sufficiently
|
|
// recent Linux kernels only).
|
|
//
|
|
// ML_SYSRAND_IMPL_URANDOM
|
|
// the system RNG is implemented by reading from /dev/urandom.
|
|
//
|
|
// ML_SYSRAND_IMPL_NONE
|
|
// there is no system RNG available on this platform.
|
|
|
|
#if defined(__linux__) || defined(MR_SOLARIS) || defined(_AIX)
|
|
#define ML_SYSRAND_IMPL_URANDOM
|
|
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(MR_CYGWIN)
|
|
#define ML_SYSRAND_IMPL_ARC4RANDOM
|
|
#elif __FreeBSD__ >= 12
|
|
// arc4random() on FreeBSD used RC4 until version 12.
|
|
#define ML_SYSRAND_IMPL_ARC4RANDOM
|
|
#elif defined(__FreeBSD__)
|
|
#define ML_SYSRAND_IMPL_URANDOM
|
|
#elif defined(MR_MAC_OSX)
|
|
// arc4random() on macOS used RC4 until version 10.12.
|
|
// XXX this will be unnecessary when we stop supporting versions
|
|
// of macOS before 10.12.
|
|
#if defined(MAC_OS_X_VERSION_10_12) && \\
|
|
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
|
|
#define ML_SYSRAND_IMPL_ARC4RANDOM
|
|
#else
|
|
#define ML_SYSRAND_IMPL_URANDOM
|
|
#endif
|
|
#elif defined(MR_WIN32)
|
|
#define ML_SYSRAND_IMPL_RAND_S
|
|
#else
|
|
#define ML_SYSRAND_IMPL_NONE
|
|
#endif
|
|
|
|
struct ML_SystemRandomHandle_Struct {
|
|
#if defined(ML_SYSRAND_IMPL_URANDOM)
|
|
int ML_srh_fd;
|
|
#else
|
|
MR_Bool ML_srh_is_open;
|
|
#endif
|
|
};
|
|
typedef struct ML_SystemRandomHandle_Struct *ML_SystemRandomHandle;
|
|
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_type("C", system_rng,
|
|
"ML_SystemRandomHandle", [can_pass_as_mercury_type]).
|
|
:- pragma foreign_type("C#", system_rng,
|
|
"random__system_rng.ML_SystemRandomHandle").
|
|
:- pragma foreign_type("Java", system_rng,
|
|
"random__system_rng.ML_SystemRandomHandle").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- instance urandom(system_rng, io) where [
|
|
pred(generate_uint8/4) is system_rng.generate_uint8,
|
|
pred(generate_uint16/4) is system_rng.generate_uint16,
|
|
pred(generate_uint32/4) is system_rng.generate_uint32,
|
|
pred(generate_uint64/4) is system_rng.generate_uint64
|
|
].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
have_system_rng,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
#if defined(ML_SYSRAND_IMPL_NONE)
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
#else
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
have_system_rng,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
have_system_rng,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
open_system_rng(Result, !IO) :-
|
|
do_open_system_rng(Handle, IsOk, ErrorMsg, !IO),
|
|
(
|
|
IsOk = yes,
|
|
Result = ok(Handle)
|
|
;
|
|
IsOk = no,
|
|
Result = error(ErrorMsg)
|
|
).
|
|
|
|
:- pred do_open_system_rng(system_rng::out, bool::out, string::out,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_open_system_rng(Handle::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
Handle = ML_random_open(&IsOk, &ErrorMsg);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
do_open_system_rng(Handle::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Handle = new random__system_rng.ML_SystemRandomHandle();
|
|
IsOk = mr_bool.YES;
|
|
ErrorMsg = \"\";
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
do_open_system_rng(Handle::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Handle = new random__system_rng.ML_SystemRandomHandle();
|
|
IsOk = bool.YES;
|
|
ErrorMsg = \"\";
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
close_system_rng(Handle, !IO) :-
|
|
do_close_system_rng(Handle, IsOk, ErrorMsg, !IO),
|
|
(
|
|
IsOk = yes
|
|
;
|
|
IsOk = no,
|
|
throw_system_rng_error($pred, ErrorMsg)
|
|
).
|
|
|
|
:- pred do_close_system_rng(system_rng::in, bool::out, string::out,
|
|
io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_close_system_rng(Handle::in, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe,
|
|
may_not_duplicate],
|
|
"
|
|
IsOk = ML_random_close(Handle, &ErrorMsg);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
do_close_system_rng(Handle::in, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
IsOk = mr_bool.NO;
|
|
ErrorMsg = \"system RNG handle already closed\";
|
|
} else {
|
|
Handle.close();
|
|
IsOk = mr_bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
do_close_system_rng(Handle::in, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
IsOk = bool.NO;
|
|
ErrorMsg = \"system RNG handle already closed\";
|
|
} else {
|
|
Handle.close();
|
|
IsOk = bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_uint8(Handle, U8, !IO) :-
|
|
do_generate_uint8(Handle, U8, IsOk, ErrorMsg, !IO),
|
|
(
|
|
IsOk = yes
|
|
;
|
|
IsOk = no,
|
|
throw_system_rng_error($pred, ErrorMsg)
|
|
).
|
|
|
|
:- pred do_generate_uint8(system_rng::in, uint8::out, bool::out,
|
|
string::out, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_generate_uint8(Handle::in, U8::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
union {
|
|
uint8_t n;
|
|
unsigned char buffer[1];
|
|
} u;
|
|
IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
|
|
&ErrorMsg);
|
|
U8 = u.n;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
do_generate_uint8(Handle::in, U8::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U8 = 0;
|
|
IsOk = mr_bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U8 = Handle.getByte();
|
|
IsOk = mr_bool.NO;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
do_generate_uint8(Handle::in, U8::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U8 = 0;
|
|
IsOk = bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U8 = Handle.getByte();
|
|
IsOk = bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_uint16(Handle, U16, !IO) :-
|
|
do_generate_uint16(Handle, U16, IsOk, ErrorMsg, !IO),
|
|
(
|
|
IsOk = yes
|
|
;
|
|
IsOk = no,
|
|
throw_system_rng_error($pred, ErrorMsg)
|
|
).
|
|
|
|
:- pred do_generate_uint16(system_rng::in, uint16::out, bool::out,
|
|
string::out, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_generate_uint16(Handle::in, U16::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
union {
|
|
uint16_t n;
|
|
unsigned char buffer[2];
|
|
} u;
|
|
IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
|
|
&ErrorMsg);
|
|
U16 = u.n;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
do_generate_uint16(Handle::in, U16::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, not_thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U16 = 0;
|
|
IsOk = mr_bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U16 = Handle.getUShort();
|
|
IsOk = mr_bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
do_generate_uint16(Handle::in, U16::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U16 = 0;
|
|
IsOk = bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U16 = Handle.getShort();
|
|
IsOk = bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_uint32(Handle, U32, !IO) :-
|
|
do_generate_uint32(Handle, U32, IsOk, ErrorMsg, !IO),
|
|
(
|
|
IsOk = yes
|
|
;
|
|
IsOk = no,
|
|
throw_system_rng_error($pred, ErrorMsg)
|
|
).
|
|
|
|
:- pred do_generate_uint32(system_rng::in, uint32::out, bool::out,
|
|
string::out, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_generate_uint32(Handle::in, U32::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
union {
|
|
uint32_t n;
|
|
unsigned char buffer[4];
|
|
} u;
|
|
IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
|
|
&ErrorMsg);
|
|
U32 = u.n;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
do_generate_uint32(Handle::in, U32::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, not_thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U32 = 0;
|
|
IsOk = mr_bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U32 = Handle.getUInt();
|
|
IsOk = mr_bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
do_generate_uint32(Handle::in, U32::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U32 = 0;
|
|
IsOk = bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U32 = Handle.getInt();
|
|
IsOk = bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_uint64(Handle, U64, !IO) :-
|
|
do_generate_uint64(Handle, U64, IsOk, ErrorMsg, !IO),
|
|
(
|
|
IsOk = yes
|
|
;
|
|
IsOk = no,
|
|
throw_system_rng_error($pred, ErrorMsg)
|
|
).
|
|
|
|
:- pred do_generate_uint64(system_rng::in, uint64::out, bool::out,
|
|
string::out, io::di, io::uo) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
do_generate_uint64(Handle::in, U64::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
|
|
may_not_duplicate],
|
|
"
|
|
union {
|
|
uint64_t n;
|
|
unsigned char buffer[8];
|
|
} u;
|
|
IsOk = ML_random_generate_bytes(Handle, u.buffer, sizeof(u.buffer),
|
|
&ErrorMsg);
|
|
U64 = u.n;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
do_generate_uint64(Handle::in, U64::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, not_thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U64 = 0;
|
|
IsOk = mr_bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U64 = Handle.getULong();
|
|
IsOk = mr_bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
do_generate_uint64(Handle::in, U64::out, IsOk::out, ErrorMsg::out,
|
|
_IO0::di, _IO::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (Handle.isClosed()) {
|
|
U64 = 0;
|
|
IsOk = bool.NO;
|
|
ErrorMsg = \"system RNG handle is closed\";
|
|
} else {
|
|
U64 = Handle.getLong();
|
|
IsOk = bool.YES;
|
|
ErrorMsg = \"\";
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred throw_system_rng_error(string::in, string::in) is erroneous.
|
|
|
|
throw_system_rng_error(Pred, Msg) :-
|
|
string.format("%s: %s", [s(Pred), s(Msg)], Error),
|
|
throw(software_error(Error)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_decl("C", local, "
|
|
|
|
#include \"mercury_memory.h\" // For MR_GC_NEW.
|
|
#include \"mercury_misc.h\" // For MR_fatal_error.
|
|
#include \"mercury_runtime_util.h\" // For MR_strerror.
|
|
#include \"mercury_regs.h\" // For MR_{save,restore}_transient_hp
|
|
#include \"mercury_reg_workarounds.h\" // For MR_memcpy.
|
|
#include \"mercury_std.h\" // For MR_TRUE, MR_FALSE etc.
|
|
#include \"mercury_string.h\" // For MR_make_aligned_string_copy etc.
|
|
#include \"mercury_types.h\" // For MR_Bool.
|
|
#include \"mercury_windows.h\"
|
|
|
|
#if defined(ML_SYSRAND_IMPL_URANDOM)
|
|
#if defined(MR_HAVE_UNISTD_H)
|
|
#include <unistd.h>
|
|
#endif
|
|
#if defined(MR_HAVE_FCNTL_H)
|
|
#include <fcntl.h>
|
|
#endif
|
|
#if defined(MR_HAVE_SYS_STAT_H)
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
|
|
// On Windows we need to ensure that _CRT_RAND_S is defined before stdlib.h
|
|
// is included but this must be done in runtime/mercury_std.h.
|
|
#include <stdlib.h>
|
|
|
|
// When succeeded is MR_TRUE, returns a handle through which the system
|
|
// RNG can be accessed; err_msg will point to the empty string in this case.
|
|
// When succeeded is MR_FALSE, the value return is not a valid handle and
|
|
// err_msg will point to a string (on the Mercury heap) describing why a handle
|
|
// for the system RNG could not be acquired.
|
|
//
|
|
static ML_SystemRandomHandle ML_random_open(MR_Bool *succeeded,
|
|
MR_String *err_msg);
|
|
|
|
// Attempt to close the handle to the system RNG.
|
|
// Returns MR_TRUE on success with err_msg pointing to the empty string.
|
|
// Returns MR_FALSE on failure with err_msg pointing to a string (on the
|
|
// Mercury heap) that describes why the handle could not be closed.
|
|
//
|
|
static MR_Bool ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg);
|
|
|
|
// Fill buffer with len random bytes generated by the system RNG.
|
|
// Returns MR_TRUE if len bytes were generated; err_msg will point to the empty
|
|
// string.
|
|
// Returns MR_FALSE if the system RNG has been unable to generate len bytes.
|
|
// In this case the contents of buffer are indeterminate and err_msg will point
|
|
// to a string (on the Mercury heap) that describes the problem.
|
|
//
|
|
static MR_Bool ML_random_generate_bytes(ML_SystemRandomHandle handle,
|
|
unsigned char *buffer, size_t len, MR_String *err_msg);
|
|
").
|
|
|
|
:- pragma foreign_code("C", "
|
|
|
|
#if defined(ML_SYSRAND_IMPL_URANDOM)
|
|
|
|
static ML_SystemRandomHandle
|
|
ML_random_open(MR_Bool *succeeded, MR_String *err_msg)
|
|
{
|
|
int fd;
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
const char *errno_msg;
|
|
ML_SystemRandomHandle handle;
|
|
|
|
do {
|
|
fd = open(\"/dev/urandom\", O_RDONLY);
|
|
} while (fd == -1 && MR_is_eintr(errno));
|
|
|
|
if (fd == -1) {
|
|
errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
|
|
MR_save_transient_hp();
|
|
MR_make_aligned_string_copy(*err_msg, errno_msg);
|
|
MR_restore_transient_hp();
|
|
*succeeded = MR_NO;
|
|
handle = NULL;
|
|
} else {
|
|
handle = MR_GC_NEW(struct ML_SystemRandomHandle_Struct);
|
|
handle->ML_srh_fd = fd;
|
|
*err_msg = MR_make_string_const(\"\");
|
|
*succeeded = MR_YES;
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg)
|
|
{
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
const char *errno_msg;
|
|
|
|
if (close(handle->ML_srh_fd) == -1) {
|
|
errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
|
|
MR_save_transient_hp();
|
|
MR_make_aligned_string_copy(*err_msg, errno_msg);
|
|
MR_restore_transient_hp();
|
|
return MR_NO;
|
|
} else {
|
|
handle->ML_srh_fd = -1;
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return MR_YES;
|
|
}
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_generate_bytes(ML_SystemRandomHandle handle,
|
|
unsigned char *buffer, size_t len, MR_String *err_msg)
|
|
{
|
|
int i;
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
const char *errno_msg;
|
|
|
|
for (i = 0; i < len; ) {
|
|
size_t to_read = len - i;
|
|
ssize_t bytes_read = read(handle->ML_srh_fd, buffer, to_read);
|
|
if (bytes_read == -1) {
|
|
if (MR_is_eintr(errno)) {
|
|
continue;
|
|
} else {
|
|
errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
|
|
MR_save_transient_hp();
|
|
MR_make_aligned_string_copy(*err_msg, errno_msg);
|
|
MR_restore_transient_hp();
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
i += bytes_read;
|
|
}
|
|
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return MR_TRUE;
|
|
}
|
|
|
|
#elif defined(ML_SYSRAND_IMPL_ARC4RANDOM)
|
|
|
|
static ML_SystemRandomHandle
|
|
ML_random_open(MR_Bool *succeeded, MR_String *err_msg)
|
|
{
|
|
ML_SystemRandomHandle handle =
|
|
MR_GC_NEW(struct ML_SystemRandomHandle_Struct);
|
|
handle->ML_srh_is_open = MR_TRUE;
|
|
*succeeded = MR_TRUE;
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return handle;
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg)
|
|
{
|
|
if (handle->ML_srh_is_open) {
|
|
handle->ML_srh_is_open = MR_FALSE;
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return MR_TRUE;
|
|
} else {
|
|
*err_msg =
|
|
MR_make_string_const(\"system RNG handle is already closed\");
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_generate_bytes(ML_SystemRandomHandle handle,
|
|
unsigned char *buffer, size_t len, MR_String *err_msg)
|
|
{
|
|
if (handle->ML_srh_is_open) {
|
|
arc4random_buf(buffer, len);
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return MR_TRUE;
|
|
} else {
|
|
*err_msg = MR_make_string_const(\"system RNG handle is closed\");
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
#elif defined(ML_SYSRAND_IMPL_RAND_S)
|
|
|
|
static ML_SystemRandomHandle
|
|
ML_random_open(MR_Bool *succeeded, MR_String *err_msg)
|
|
{
|
|
ML_SystemRandomHandle handle =
|
|
MR_GC_NEW(struct ML_SystemRandomHandle_Struct);
|
|
handle->ML_srh_is_open = MR_TRUE;
|
|
*succeeded = MR_TRUE;
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return handle;
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg)
|
|
{
|
|
if (handle->ML_srh_is_open) {
|
|
handle->ML_srh_is_open = MR_FALSE;
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return MR_TRUE;
|
|
} else {
|
|
*err_msg =
|
|
MR_make_string_const(\"system RNG handle is already closed\");
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_generate_bytes(ML_SystemRandomHandle handle,
|
|
unsigned char *buffer, size_t len, MR_String *err_msg)
|
|
{
|
|
int err;
|
|
unsigned int n;
|
|
size_t num_to_read = len;
|
|
|
|
if (!handle->ML_srh_is_open) {
|
|
*err_msg = MR_make_string_const(\"system RNG handle is closed\");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
while (num_to_read > 0) {
|
|
if (num_to_read < 4) {
|
|
err = rand_s(&n);
|
|
if (err != 0) {
|
|
goto rand_s_failure_handler;
|
|
}
|
|
MR_memcpy(buffer, (unsigned char *) &n, num_to_read);
|
|
break;
|
|
} else {
|
|
err = rand_s((unsigned int *) buffer);
|
|
if (err != 0) {
|
|
goto rand_s_failure_handler;
|
|
}
|
|
num_to_read -= 4;
|
|
buffer += 4;
|
|
}
|
|
}
|
|
|
|
*err_msg = MR_make_string_const(\"\");
|
|
return MR_TRUE;
|
|
|
|
rand_s_failure_handler:
|
|
|
|
*err_msg = MR_make_string_const(\"rand_s failed\");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
#else // ML_SYSRAND_IMPL_NONE
|
|
|
|
static ML_SystemRandomHandle
|
|
ML_random_open(MR_Bool *succeeded, MR_String *err_msg)
|
|
{
|
|
*succeeded = MR_FALSE;
|
|
*err_msg = MR_make_string_const(\"No system RNG available\");
|
|
return 0; // Dummy value.
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg)
|
|
{
|
|
MR_fatal_error(\"ML_random_close - no system RNG available.\");
|
|
}
|
|
|
|
static MR_Bool
|
|
ML_random_generate_bytes(ML_SystemRandomHandle handle,
|
|
unsigned char *buffer, size_t len, MR_String *err_msg)
|
|
{
|
|
MR_fatal_error(\"ML_random_generate_bytes - no system RNG available.\");
|
|
}
|
|
|
|
#endif
|
|
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_code("C#", "
|
|
|
|
public class ML_SystemRandomHandle {
|
|
|
|
private System.Security.Cryptography.RandomNumberGenerator handle;
|
|
|
|
public ML_SystemRandomHandle() {
|
|
handle =
|
|
System.Security.Cryptography.RandomNumberGenerator.Create();
|
|
}
|
|
|
|
public void close() {
|
|
handle = null;
|
|
}
|
|
|
|
public bool isClosed() {
|
|
return (handle == null);
|
|
}
|
|
|
|
public byte getByte() {
|
|
byte[] bytes = new byte[1];
|
|
handle.GetBytes(bytes);
|
|
return bytes[0];
|
|
}
|
|
|
|
public ushort getUShort() {
|
|
byte[] bytes = new byte[2];
|
|
handle.GetBytes(bytes);
|
|
return (ushort) (bytes[1] << 8 | (bytes[0] & 0x00ff));
|
|
}
|
|
|
|
public uint getUInt() {
|
|
byte[] bytes = new byte[4];
|
|
handle.GetBytes(bytes);
|
|
return (uint) (
|
|
bytes[3] << 24 |
|
|
bytes[2] << 16 |
|
|
bytes[1] << 8 |
|
|
bytes[0]);
|
|
}
|
|
|
|
public ulong getULong() {
|
|
byte[] bytes = new byte[8];
|
|
handle.GetBytes(bytes);
|
|
return (ulong) (
|
|
(ulong) bytes[7] << 56 |
|
|
(ulong) bytes[6] << 48 |
|
|
(ulong) bytes[5] << 40 |
|
|
(ulong) bytes[4] << 32 |
|
|
(ulong) bytes[3] << 24 |
|
|
(ulong) bytes[2] << 16 |
|
|
(ulong) bytes[1] << 8 |
|
|
(ulong) bytes[0]);
|
|
}
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_code("Java", "
|
|
|
|
public static class ML_SystemRandomHandle {
|
|
|
|
private java.security.SecureRandom handle;
|
|
|
|
public ML_SystemRandomHandle() {
|
|
handle = new java.security.SecureRandom();
|
|
}
|
|
|
|
public void close() {
|
|
handle = null;
|
|
}
|
|
|
|
public boolean isClosed() {
|
|
return (handle == null);
|
|
}
|
|
|
|
public byte getByte() {
|
|
byte[] bytes = new byte[1];
|
|
handle.nextBytes(bytes);
|
|
return bytes[0];
|
|
}
|
|
|
|
public short getShort() {
|
|
byte[] bytes = new byte[2];
|
|
handle.nextBytes(bytes);
|
|
return (short)
|
|
(bytes[0] << java.lang.Byte.SIZE | (bytes[1] & 0x00ff));
|
|
}
|
|
|
|
public int getInt() {
|
|
return handle.nextInt();
|
|
}
|
|
|
|
public long getLong() {
|
|
return handle.nextLong();
|
|
}
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module random.system_rng.
|
|
%---------------------------------------------------------------------------%
|