Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto/rand: Currently using deprecated API for random number generation on Windows #33542

Open
randomvariable opened this issue Aug 8, 2019 · 11 comments

Comments

@randomvariable
Copy link

@randomvariable randomvariable commented Aug 8, 2019

What version of Go are you using (go version)?

N/A

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

Any release of Windows supported by Microsoft

What did you do?

Reviewing cryptographic protocols for a downstream project

What did you expect to see?

Use of a modern Windows API to retrieve entropy, namely BCryptGenRandom from CryptoNG, which is compliant with CTR_DRBG from NIST SP800-90.

What did you see instead?

Go calls CryptGenRandom from a deprecated API. The algorithm is not fully documented and is only known in part.

@bcmills bcmills added this to the Go1.14 milestone Aug 8, 2019
@bcmills

This comment has been minimized.

Copy link
Member

@bcmills bcmills commented Aug 8, 2019

@jtn7

This comment has been minimized.

Copy link

@jtn7 jtn7 commented Dec 5, 2019

I noticed, recently, that crypto/rand used CryptGenRandom instead of (BCryptGenRandom). So I decided to call it manually.

In case anyone is interested:

var (
	bcrypt, libErr     = syscall.LoadLibrary("bcrypt.dll")
	genRandom, procErr = syscall.GetProcAddress(bcrypt, "BCryptGenRandom")
)

const BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002

func bcryptGenRandom(buf []byte) (ret uintptr, callErr error) {
	const nargs = 4
	ret, _, callErr = syscall.Syscall6(
		genRandom,
		nargs,
		0,
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(len(buf)),
		BCRYPT_USE_SYSTEM_PREFERRED_RNG,
		0,
		0,
	)
	return
}
@networkimprov

This comment has been minimized.

Copy link

@networkimprov networkimprov commented Dec 5, 2019

@zx2c4

This comment has been minimized.

Copy link
Contributor

@zx2c4 zx2c4 commented Dec 5, 2019

The RNG function that we should be using on Windows is actually RtlGenRandom. I'll submit a patch to use that. We already have a wrapper around it in getRandomData.

@gopherbot

This comment has been minimized.

Copy link

@gopherbot gopherbot commented Dec 5, 2019

Change https://golang.org/cl/210057 mentions this issue: crypto/rand: generate random numbers using RtlGenRandom on Windows

@FiloSottile FiloSottile modified the milestones: Unplanned, Go1.15 Dec 5, 2019
@randomvariable

This comment has been minimized.

Copy link
Author

@randomvariable randomvariable commented Dec 6, 2019

Most confusing documentation ever, Microsoft...

@zikaeroh

This comment has been minimized.

Copy link

@zikaeroh zikaeroh commented Dec 6, 2019

Out of curiosity (forgive my minimal experience in this area), why RtlGenRandom?

I was curious and looked at the docs for these APIs, and it seems like the docs for RtlGenRandom say "use CryptGenRandom" (the thing that this issue is saying is deprecated), and the docs for CryptGenRandom say "use CNG", i.e. BCryptGenRandom.

@jtn7

This comment has been minimized.

Copy link

@jtn7 jtn7 commented Dec 7, 2019

@zikaeroh According to this rust issue CryptGenRandom eventually makes call to RtlGenRandom. Another comment, in that issue, makes a more compelling argument: rust-random/rand#111 (comment). The comment explains that Microsoft uses it in their C standard library function rand_s and RtlGenRandom isn't going anywhere.

Looking at rust source I see references to both RtlGenRandom and BCryptGenRandom.

@zx2c4

This comment has been minimized.

Copy link
Contributor

@zx2c4 zx2c4 commented Dec 10, 2019

Time to reverse engineer some stuff.

RtlGenRandom/SystemFunction036 in advapi32.dll is a stub for cryptbase.dll, which has it as:

bool SystemFunction036(void *buf, uint32_t len)
{
  return ProcessPrng(buf, len);
}

ProcessPrng lives in bcryptPrimitives.dll as:

bool ProcessPrng(void *buf, uint64_t len)
{
  bool ret = true;
  uint64_t bytes_to_read;
  int64_t out;
  uint128_t *aes_key;

  success_1 = 1;
  if (g_rngState == 1)
  {
    ret = false;
    out = 0;
    AesRNGState_select(&aes_key);
    for (; len; len -= bytes_to_read)
    {
      bytes_to_read = 0x10000;
      if (len < 0x10000)
        bytes_to_read = len;
      buf += bytes_to_read;
      ret = AesRNGState_generate(aes_key, buf, bytes_to_read, &out);
    }
  }
  else
  {
    if (g_rngState != 2)
    {
      for (;;)
        *(unsigned int *)0 = 0x78676E72;
    }
    EmergencyRng(buf, len);
  }
  return ret;
}

From there I assume that this is some sort of per-process and per-cpu expansion machine mostly in userspace, seeded with some kernel entropy.

@alexbrainman

This comment has been minimized.

Copy link
Member

@alexbrainman alexbrainman commented Dec 11, 2019

we should be using on Windows is actually RtlGenRandom

The RtlGenRandom doco

https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom

suggests that we should use CryptGenRandom

It may be altered or unavailable in subsequent versions. Instead, use the CryptGenRandom function

@zx2c4 why do you prefer RtlGenRandom to current CryptGenRandom? I am fine with either.

Also, if we follow CryptGenRandom doco

https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom

This API is deprecated. New and existing software should start using Cryptography Next Generation APIs.

We should, probably, use BCryptGenRandom. No?

Alex

@tmthrgd

This comment has been minimized.

Copy link
Contributor

@tmthrgd tmthrgd commented Dec 18, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
10 participants
You can’t perform that action at this time.