Implement PlatformEntropySource for macOS, Microsoft Windows, NetBSD, OpenBSD, Solaris
This commit is contained in:
parent
bd2b88f16e
commit
9876d9245c
3
dub.json
3
dub.json
@ -28,5 +28,6 @@
|
||||
"lflags": ["arch/tanya.a"],
|
||||
"versions": ["TanyaNative"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"libs-windows": ["advapi32"]
|
||||
}
|
||||
|
@ -94,6 +94,27 @@ abstract class EntropySource
|
||||
Nullable!ubyte poll(out ubyte[maxGather] output) @nogc;
|
||||
}
|
||||
|
||||
version (CRuntime_Bionic)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (OSX)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (OpenBSD)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (NetBSD)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
else version (Solaris)
|
||||
{
|
||||
version = SecureARC4Random;
|
||||
}
|
||||
|
||||
version (linux)
|
||||
{
|
||||
extern (C) long syscall(long number, ...) nothrow @system @nogc;
|
||||
@ -160,6 +181,173 @@ version (linux)
|
||||
}
|
||||
}
|
||||
}
|
||||
else version (SecureARC4Random)
|
||||
{
|
||||
private extern (C) void arc4random_buf(scope void* buf, size_t nbytes) nothrow @nogc @system;
|
||||
|
||||
/**
|
||||
* Uses arc4random_buf.
|
||||
*/
|
||||
class PlatformEntropySource : EntropySource
|
||||
{
|
||||
/**
|
||||
* Returns: Minimum bytes required from the entropy source.
|
||||
*/
|
||||
override @property ubyte threshold() const pure nothrow @safe @nogc
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether this entropy source is strong.
|
||||
*/
|
||||
override @property bool strong() const pure nothrow @safe @nogc
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the entropy source.
|
||||
*
|
||||
* Params:
|
||||
* output = Buffer to save the generate random sequence (the method will
|
||||
* to fill the buffer).
|
||||
*
|
||||
* Returns: Number of bytes that were copied to the $(D_PARAM output)
|
||||
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
|
||||
*/
|
||||
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow @nogc @safe
|
||||
{
|
||||
(() @trusted => arc4random_buf(output.ptr, output.length))();
|
||||
return Nullable!ubyte(cast(ubyte) (output.length));
|
||||
}
|
||||
}
|
||||
|
||||
@nogc @system unittest
|
||||
{
|
||||
auto entropy = defaultAllocator.make!Entropy();
|
||||
ubyte[blockSize] output;
|
||||
output = entropy.random;
|
||||
|
||||
defaultAllocator.dispose(entropy);
|
||||
}
|
||||
}
|
||||
else version (Windows)
|
||||
{
|
||||
import core.sys.windows.basetsd : ULONG_PTR;
|
||||
import core.sys.windows.windef : BOOL, DWORD, PBYTE;
|
||||
import core.sys.windows.winnt : LPCSTR, LPCWSTR;
|
||||
import core.sys.windows.wincrypt;
|
||||
private extern(Windows) @nogc nothrow
|
||||
{
|
||||
BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE);
|
||||
BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD);
|
||||
BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD);
|
||||
BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR);
|
||||
}
|
||||
|
||||
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) @nogc nothrow @trusted
|
||||
{
|
||||
import core.sys.windows.winbase : GetLastError;
|
||||
import core.sys.windows.winerror : NTE_BAD_KEYSET;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx
|
||||
// For performance reasons, we recommend that you set the pszContainer
|
||||
// parameter to NULL and the dwFlags parameter to CRYPT_VERIFYCONTEXT
|
||||
// in all situations where you do not require a persisted key.
|
||||
// CRYPT_SILENT is intended for use with applications for which the UI cannot be displayed by the CSP.
|
||||
if (!CryptAcquireContextW(&hProvider, null, null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
||||
{
|
||||
if (GetLastError() == NTE_BAD_KEYSET)
|
||||
{
|
||||
// Attempt to create default container
|
||||
if (!CryptAcquireContextA(&hProvider, null, null, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class PlatformEntropySource : EntropySource
|
||||
{
|
||||
private HCRYPTPROV hProvider;
|
||||
|
||||
/**
|
||||
* Uses CryptGenRandom.
|
||||
*/
|
||||
this() @nogc
|
||||
{
|
||||
if (!initCryptGenRandom(hProvider))
|
||||
{
|
||||
throw defaultAllocator.make!EntropyException("CryptAcquireContextW failed.");
|
||||
}
|
||||
assert(hProvider > 0, "hProvider not properly initialized.");
|
||||
}
|
||||
|
||||
~this() @nogc nothrow @safe
|
||||
{
|
||||
if (hProvider > 0)
|
||||
{
|
||||
(() @trusted => CryptReleaseContext(hProvider, 0))();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Minimum bytes required from the entropy source.
|
||||
*/
|
||||
override @property ubyte threshold() const pure nothrow @safe @nogc
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Whether this entropy source is strong.
|
||||
*/
|
||||
override @property bool strong() const pure nothrow @safe @nogc
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the entropy source.
|
||||
*
|
||||
* Params:
|
||||
* output = Buffer to save the generate random sequence (the method will
|
||||
* to fill the buffer).
|
||||
*
|
||||
* Returns: Number of bytes that were copied to the $(D_PARAM output)
|
||||
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
|
||||
*/
|
||||
override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow @safe
|
||||
in
|
||||
{
|
||||
assert(hProvider > 0, "hProvider not properly initialized.");
|
||||
}
|
||||
do
|
||||
{
|
||||
Nullable!ubyte ret;
|
||||
if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))())
|
||||
{
|
||||
ret = cast(ubyte) (output.length);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@nogc @system unittest
|
||||
{
|
||||
auto entropy = defaultAllocator.make!Entropy();
|
||||
ubyte[blockSize] output;
|
||||
output = entropy.random;
|
||||
|
||||
defaultAllocator.dispose(entropy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pseudorandom number generator.
|
||||
@ -180,8 +368,6 @@ class Entropy
|
||||
|
||||
private ubyte sourceCount_;
|
||||
|
||||
private shared Allocator allocator;
|
||||
|
||||
/// Entropy accumulator.
|
||||
protected SHA!(maxGather * 8, 512) accumulator;
|
||||
|
||||
@ -202,7 +388,7 @@ class Entropy
|
||||
{
|
||||
allocator.resize(sources, maxSources);
|
||||
|
||||
version (linux)
|
||||
static if (is(PlatformEntropySource))
|
||||
{
|
||||
this ~= allocator.make!PlatformEntropySource;
|
||||
}
|
||||
@ -289,7 +475,7 @@ class Entropy
|
||||
|
||||
if (!haveStrong)
|
||||
{
|
||||
throw allocator.make!EntropyException("No strong entropy source defined.");
|
||||
throw defaultAllocator.make!EntropyException("No strong entropy source defined.");
|
||||
}
|
||||
|
||||
output = accumulator.finish();
|
||||
|
Loading…
Reference in New Issue
Block a user