summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorNathan Sashihara <21227491+n8sh@users.noreply.github.com>2018-02-19 09:21:53 -0500
committerNathan Sashihara <21227491+n8sh@users.noreply@github.com>2018-02-21 03:18:52 -0500
commit9876d9245c76056e190a12c9ed471f047e57a37e (patch)
treee1cfe951a8384cfbc7186334c7ad45deeacaae60 /source
parentbd2b88f16ece2b0631175bc1f91afae56ba5b3ed (diff)
downloadtanya-9876d9245c76056e190a12c9ed471f047e57a37e.tar.gz
Implement PlatformEntropySource for macOS, Microsoft Windows, NetBSD, OpenBSD, Solaris
Diffstat (limited to 'source')
-rw-r--r--source/tanya/math/random.d194
1 files changed, 190 insertions, 4 deletions
diff --git a/source/tanya/math/random.d b/source/tanya/math/random.d
index 2275d98..6978f8d 100644
--- a/source/tanya/math/random.d
+++ b/source/tanya/math/random.d
@@ -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();