Skip to content

RFC: Improve std.random.unpredictableSeed#5230

Closed
yshui wants to merge 1 commit intodlang:masterfrom
yshui:random
Closed

RFC: Improve std.random.unpredictableSeed#5230
yshui wants to merge 1 commit intodlang:masterfrom
yshui:random

Conversation

@yshui
Copy link

@yshui yshui commented Mar 3, 2017

Implemented according to suggestions from Cédric Picard (see 1 2)

I only have linux machine to test with, so I opened this pull request to abuse the build bot.

Shoot if you spot any problems.

@yshui yshui force-pushed the random branch 2 times, most recently from 582727d to d424605 Compare March 3, 2017 02:30
@wilzbach
Copy link
Contributor

wilzbach commented Mar 3, 2017

so I opened this pull request to abuse the build bot.

That's what it's for.

For Linux >= 3.17 there's the awesome [getrandom`](http://man7.org/linux/man-pages/man2/getrandom.2.html) system call which should be used. For more details please see this intro article

Also you might be very interested in my related work at mir.random which also exposes a genRandom{,Blocking} API.

std/random.d Outdated
{
bool ret = CryptAcquireContext(&hProv, null, null, PROV_RSA_FULL, 0);
if (!ret)
throw new Exception("CryptAcquireContext");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's possible to recover from this:

            if (GetLastError() == NTE_BAD_KEYSET)
            {
                 // Attempt to create default container
                 if (!CryptAcquireContextA(&hProvider, null, null, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT))
                    return 1;

std/random.d Outdated
uint rand;
if (!initialized)
{
bool ret = CryptAcquireContext(&hProv, null, null, PROV_RSA_FULL, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CryptAcquireContextW(&hProv, null, null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))

-> https://msdn.microsoft.com/en-us/library/windows/desktop/aa379942(v=vs.85).aspx

@wilzbach
Copy link
Contributor

wilzbach commented Mar 3, 2017

It took me a very long time (and many runs on AppVeyor) to figure out that the Wincrypt headers in druntime aren't correct for x64. Hence I highly recommend to have use the overrides I use in Mir - it's tested ;-)

// the wincrypt headers in druntime are broken for x64!
private alias ULONG_PTR = size_t; // uint in druntime
private alias BOOL = bool;
private alias DWORD = size_t; // uint in druntime
private alias LPCWSTR = wchar*;
private alias PBYTE = ubyte*;
private alias HCRYPTPROV = ULONG_PTR;
private alias LPCSTR = const(char)*;

private extern(Windows) BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE) @nogc @safe nothrow;
private extern(Windows) BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD) @nogc nothrow;
private extern(Windows) BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD) @nogc nothrow;
private extern(Windows) BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR) @nogc nothrow;

private __gshared ULONG_PTR hProvider;

@wilzbach wilzbach self-assigned this Mar 3, 2017
@yshui
Copy link
Author

yshui commented Mar 3, 2017

@wilzbach What's the correct way to detect Linux version in D?

@yshui
Copy link
Author

yshui commented Mar 3, 2017

@wilzbach Maybe you should push changes to druntime?

@wilzbach
Copy link
Contributor

wilzbach commented Mar 3, 2017

@wilzbach What's the correct way to detect Linux version in D?

There's no "correct" way, but the system call uname is a good option.

-> Another friendly redirect to my work: https://github.com/libmir/mir-random/pull/13/files
Copied here for convenience:

import core.sys.posix.sys.utsname : utsname;

// druntime's version isn't annotated with attributes
private extern(C) int uname(utsname* __name) @nogc nothrow;

private bool hasGetRandom() @nogc @trusted nothrow
{
    import core.stdc.string : strtok;
    import core.stdc.stdlib : atoi;

    utsname uts;
    uname(&uts);
    char* p = uts.release.ptr;

    // poor man's version check
    auto token = strtok(p, ".");
    int major = atoi(token);
    if (major  > 3)
        return true;

    if (major == 3)
    {
        token = strtok(p, ".");
        if (atoi(token) >= 17)
            return true;
    }

    return false;
}

(this is done without the D standard library, s.t. it can be used in -betterC)

@wilzbach Maybe you should push changes to druntime?

I haven't used Windows for the last five years, so I don't know much about the Windows bindings in druntime.

else version(NetBSD)
{
version = ARC4_RANDOM;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macOS also has the arc4random function. Not sure what's best to use.

[1] https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/arc4random.3.html

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In recent version of OpenBSD/NetBSD, arc4random is supported by ChaCha20. On macOS it's ARC4.

OTH macOS's /dev/random uses Yarrow, so I guess that's a better choice?

return rand;
}
}
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even support any non-windows and non-posix OS's?

std/random.d Outdated
static bool seeded;
static MinstdRand0 rand;
if (!seeded)
version(Windows)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space between version and (

@@ -1281,17 +1294,79 @@ A single unsigned integer seed value, different on each successive call
*/
@property uint unpredictableSeed() @trusted
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a template with trusted escapes so the attributes can be inferred

std/random.d Outdated

version(ARC4_RANDOM)
{
extern(C) uint arc4random();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nogc nothrow pure

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this can be pure?

@yshui yshui force-pushed the random branch 9 times, most recently from 6414960 to 2d2e2af Compare March 9, 2017 22:14
Implemented according to suggestions from Cédric Picard.
{
version = ARC4_RANDOM;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest also setting version = ARC4_RANDOM for version (FreeBSD) and version (OSX).

@n8sh
Copy link
Member

n8sh commented Oct 9, 2017

On my machine I profiled all of the implementations except the one using CryptGenRandom:

  • The current unpredictableSeed takes 2.9 seconds for 100 million calls.
  • arc4random() took 3.2 seconds for 100 million calls. I ran this test under OS X whose arc4random uses AES rather than ChaCha20.
  • The implementation that reads from /dev/urandom on every call takes around a minute and ten seconds for 100 million calls.
  • The fallback implementation based on SHA256Digest takes nine minutes for 100 million calls.

I think the last two have the wrong speed/unpredictability tradeoff for a non-cryptographic API.

@JackStouffer
Copy link
Contributor

Going to close this in a week if @yshui doesn't respond.

@yshui
Copy link
Author

yshui commented Nov 18, 2017

@JackStouffer I'm not sure where this pull request is going...

@JackStouffer
Copy link
Contributor

@yshui

  • Does it matter how slow unpredictableSeed is (I suspect no, because you should really only be calling it once per run of the software)?
  • Does arc4random work on OSX and FreeBSD?
  • Add info to the docs about the crypto security of the seed on each platform
  • Add a warning that this function shouldn't be re-called for every new random number created and should really just be called once per run

Also, @wilzbach you seem to know about this area, any other comments on the implementation?

@n8sh
Copy link
Member

n8sh commented Nov 20, 2017

Does arc4random work on OSX and FreeBSD?

OSX has arc4random with AES instead of ChaCha20.

FreeBSD has an arc4random that still uses RC4 and has the problem that forked processes see the same sequences when the PID wraps. It shouldn't be used anywhere a cryptographically secure PRNG is required, but for non-cryptographic uses it would still be a significant improvement over the current unpredictableSeed.

@JackStouffer
Copy link
Contributor

Ping @yshui

@quickfur
Copy link
Member

quickfur commented Jan 4, 2018

One general comment / question: reading the articles posted at the two given links in the original PR description, I come away with the understanding that what the article was advocating for was not to try to make unpredictableSeed cryptographically secure, but rather to point out that std.random was never intended to be used for cryptographic purposes in the first place, and therefore user code should not be relying on std.random for any cryptographic properties it may or may not have.

In that context, this PR seems a little misplaced. What's the value of going through all of this trouble to use /dev/urandom and/or its equivalent to get a supposedly cryptographically-secure seed value, only to use it to seed a PRNG that was never designed for cryptographic applications in the first place, and that is therefore almost certainly insecure?

@yshui
Copy link
Author

yshui commented Jan 8, 2018

@quickfur I agree. I have the impression that this issue has already been solved when the disclaimer is add to the std.random document.

I will close this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants