summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dub.json3
-rw-r--r--source/tanya/math/random.d194
2 files changed, 192 insertions, 5 deletions
diff --git a/dub.json b/dub.json
index 70cb5b4..f25a4e6 100644
--- a/dub.json
+++ b/dub.json
@@ -32,5 +32,6 @@
"lflags": ["arch/tanya.a"],
"versions": ["TanyaNative"]
}
- ]
+ ],
+ "libs-windows": ["advapi32"]
}
diff --git a/source/tanya/math/random.d b/source/tanya/math/random.d
index c68f477..79399c1 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)
{
import core.stdc.config : c_long;
@@ -159,6 +180,173 @@ version (linux)
defaultAllocator.dispose(entropy);
}
}
+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.
@@ -179,8 +367,6 @@ class Entropy
private ubyte sourceCount_;
- private shared Allocator allocator;
-
/// Entropy accumulator.
protected SHA!(maxGather * 8, 512) accumulator;
@@ -201,7 +387,7 @@ class Entropy
{
allocator.resize(sources, maxSources);
- version (linux)
+ static if (is(PlatformEntropySource))
{
this ~= allocator.make!PlatformEntropySource;
}
@@ -288,7 +474,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();