Deprecate Entropy (leaving platform sources alone)

Also introduces unavoidable breaking change in EntropySource interface:
poll() returns Option!ubyte instead of Nullable.
This commit is contained in:
Eugen Wissner 2018-10-05 12:50:39 +02:00
parent 9364112690
commit a8b18d7603
1 changed files with 65 additions and 72 deletions

View File

@ -15,10 +15,11 @@
module tanya.math.random;
import std.digest.sha;
import std.typecons;
import tanya.memory;
import tanya.typecons;
/// Block size of entropy accumulator (SHA-512).
deprecated
enum blockSize = 64;
/// Maximum amount gathered from the entropy sources.
@ -39,7 +40,7 @@ class EntropyException : Exception
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) pure @safe nothrow const @nogc
Throwable next = null) const @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
@ -56,17 +57,17 @@ abstract class EntropySource
/**
* Returns: Minimum bytes required from the entropy source.
*/
@property ubyte threshold() const pure nothrow @safe @nogc;
@property ubyte threshold() const @nogc nothrow pure @safe;
/**
* Returns: Whether this entropy source is strong.
*/
@property bool strong() const pure nothrow @safe @nogc;
@property bool strong() const @nogc nothrow pure @safe;
/**
* Returns: Amount of already generated entropy.
*/
@property ushort size() const pure nothrow @safe @nogc
@property ushort size() const @nogc nothrow pure @safe
{
return size_;
}
@ -76,7 +77,7 @@ abstract class EntropySource
* size = Amount of already generated entropy. Cannot be smaller than the
* already set value.
*/
@property void size(ushort size) pure nothrow @safe @nogc
@property void size(ushort size) @nogc nothrow pure @safe
{
size_ = size;
}
@ -89,9 +90,13 @@ abstract class EntropySource
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
* or nothing on error.
*
* Postcondition: Returned length is less than or equal to
* $(D_PARAM output) length.
*/
Nullable!ubyte poll(out ubyte[maxGather] output) @nogc;
Option!ubyte poll(out ubyte[maxGather] output) @nogc
out (length; length.isNothing || length.get <= maxGather);
}
version (CRuntime_Bionic)
@ -118,7 +123,7 @@ else version (Solaris)
version (linux)
{
import core.stdc.config : c_long;
extern (C) c_long syscall(c_long number, ...) nothrow @system @nogc;
private extern(C) c_long syscall(c_long number, ...) @nogc nothrow @system;
/**
* Uses getrandom system call.
@ -128,7 +133,7 @@ version (linux)
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const pure nothrow @safe @nogc
override @property ubyte threshold() const @nogc nothrow pure @safe
{
return 32;
}
@ -136,7 +141,7 @@ version (linux)
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const pure nothrow @safe @nogc
override @property bool strong() const @nogc nothrow pure @safe
{
return true;
}
@ -149,19 +154,14 @@ version (linux)
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
* or nothing on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow @nogc
out (length)
{
assert(length <= maxGather);
}
do
override Option!ubyte poll(out ubyte[maxGather] output) @nogc nothrow
{
// int getrandom(void *buf, size_t buflen, unsigned int flags);
import mir.linux._asm.unistd : NR_getrandom;
auto length = syscall(NR_getrandom, output.ptr, output.length, 0);
Nullable!ubyte ret;
Option!ubyte ret;
if (length >= 0)
{
@ -170,19 +170,11 @@ version (linux)
return ret;
}
}
@nogc @system unittest
{
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
defaultAllocator.dispose(entropy);
}
}
else version (SecureARC4Random)
{
private extern (C) void arc4random_buf(scope void* buf, size_t nbytes) nothrow @nogc @system;
private extern(C) void arc4random_buf(scope void* buf, size_t nbytes)
@nogc nothrow @system;
/**
* Uses arc4random_buf.
@ -192,7 +184,7 @@ else version (SecureARC4Random)
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const pure nothrow @safe @nogc
override @property ubyte threshold() const @nogc nothrow pure @safe
{
return 32;
}
@ -200,7 +192,7 @@ else version (SecureARC4Random)
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const pure nothrow @safe @nogc
override @property bool strong() const @nogc nothrow pure @safe
{
return true;
}
@ -213,23 +205,15 @@ else version (SecureARC4Random)
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
* or nothing on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow @nogc @safe
override Option!ubyte poll(out ubyte[maxGather] output)
@nogc nothrow @safe
{
(() @trusted => arc4random_buf(output.ptr, output.length))();
return Nullable!ubyte(cast(ubyte) (output.length));
return Option!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)
{
@ -248,22 +232,31 @@ else version (Windows)
BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR);
}
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) @nogc nothrow @trusted
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider)
@nogc nothrow @trusted
{
// 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))
// 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)
if (GetLastError() != NTE_BAD_KEYSET)
{
// Attempt to create default container
if (!CryptAcquireContextA(&hProvider, null, null, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT))
return false;
return false;
}
else
// Attempt to create default container
if (!CryptAcquireContextA(&hProvider,
null,
null,
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_SILENT))
{
return false;
}
@ -299,7 +292,7 @@ else version (Windows)
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const pure nothrow @safe @nogc
override @property ubyte threshold() const @nogc nothrow pure @safe
{
return 32;
}
@ -307,7 +300,7 @@ else version (Windows)
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const pure nothrow @safe @nogc
override @property bool strong() const @nogc nothrow pure @safe
{
return true;
}
@ -320,16 +313,14 @@ else version (Windows)
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
* or nothing on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow @safe
in
override Option!ubyte poll(out ubyte[maxGather] output)
@nogc nothrow @safe
{
assert(hProvider > 0, "hProvider not properly initialized.");
}
do
{
Nullable!ubyte ret;
Option!ubyte ret;
assert(hProvider > 0, "hProvider not properly initialized");
if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))())
{
ret = cast(ubyte) (output.length);
@ -337,15 +328,16 @@ else version (Windows)
return ret;
}
}
}
@nogc @system unittest
{
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
static if (is(PlatformEntropySource)) @nogc @system unittest
{
import tanya.memory.smartref : unique;
defaultAllocator.dispose(entropy);
}
auto source = defaultAllocator.unique!PlatformEntropySource();
assert(source.threshold == 32);
assert(source.strong);
}
/**
@ -360,6 +352,7 @@ else version (Windows)
* defaultAllocator.dispose(entropy);
* ---
*/
deprecated
class Entropy
{
/// Entropy sources.
@ -396,7 +389,7 @@ class Entropy
/**
* Returns: Amount of the registered entropy sources.
*/
@property ubyte sourceCount() const pure nothrow @safe @nogc
@property ubyte sourceCount() const @nogc nothrow pure @safe
{
return sourceCount_;
}
@ -413,7 +406,7 @@ class Entropy
* $(D_PSYMBOL EntropySource)
*/
Entropy opOpAssign(string op)(EntropySource source)
pure nothrow @safe @nogc
@nogc nothrow pure @safe
if (op == "~")
in
{
@ -451,7 +444,7 @@ class Entropy
{
auto outputLength = sources[i].poll(buffer);
if (!outputLength.isNull)
if (!outputLength.isNothing)
{
if (outputLength > 0)
{
@ -502,7 +495,7 @@ class Entropy
*/
protected void update(in ubyte sourceId,
ref ubyte[maxGather] data,
ubyte length) pure nothrow @safe @nogc
ubyte length) @nogc nothrow pure @safe
{
ubyte[2] header;