mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
library/random.system_rng.m:
Better document the error behaviour of some predicates in this module.
Adjust the header comment to group the description of what happens
on Cygwin with the rest of the Windows description.
Adjust a comment.
972 lines
28 KiB
Mathematica
972 lines
28 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, OpenBSD, NetBSD and versions of FreeBSD from 12 onwards, we use
|
|
% the arc4random() family of functions.
|
|
%
|
|
% For Windows, we use rand_s() from the Windows CRT; except when using Cygwin,
|
|
% where we use the arc4random() family of functions instead.
|
|
%
|
|
% 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):
|
|
%
|
|
% Attempt to open a handle through which the system RNG can be accessed.
|
|
% On success, return ok(Handle). The caller is responsible for releasing
|
|
% the handle and any associated OS resources by calling close_system_rng/3
|
|
% when it is no longer needed.
|
|
%
|
|
% Return error(Msg), where Msg is a human-readable description of the
|
|
% error
|
|
% - if the system RNG is not supported on this platform, or
|
|
% - if a handle cannot be obtained (for example because /dev/urandom
|
|
% could not be opened).
|
|
%
|
|
:- 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_uint8(Handle, U8, !IO):
|
|
% generate_uint16(Handle, U16, !IO):
|
|
% generate_uint32(Handle, U32, !IO):
|
|
% generate_uint64(Handle, U64, !IO):
|
|
%
|
|
% Generate a uniformly distributed unsigned integer of 8-, 16-, 32- or
|
|
% 64-bits respectively using the system RNG accessed via Handle.
|
|
% Throw an exception if an error occurs, for example if Handle has been
|
|
% closed or if the underlying RNG cannot be read.
|
|
%
|
|
:- 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 before version 12 used RC4.
|
|
#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.YES;
|
|
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.
|
|
%---------------------------------------------------------------------------%
|