summaryrefslogtreecommitdiff
path: root/osrng.cpp
diff options
context:
space:
mode:
authorJeffrey Walton <noloader@gmail.com>2016-05-03 00:23:05 -0400
committerJeffrey Walton <noloader@gmail.com>2016-05-09 03:07:57 -0400
commitd294b4290b997f174ade322a9638db2f1e15ed41 (patch)
tree9c8faf70d56d2be3c5fb0a7c040ca652e8c69e5a /osrng.cpp
parent69f7802b53dfbc7ac9aacbaff02b45d6582aad3d (diff)
downloadcryptopp-git-d294b4290b997f174ade322a9638db2f1e15ed41.tar.gz
Add changes for Windows Sotre that went missing with my clumsy Git skills
Diffstat (limited to 'osrng.cpp')
-rw-r--r--osrng.cpp118
1 files changed, 99 insertions, 19 deletions
diff --git a/osrng.cpp b/osrng.cpp
index f9181ec2..eaec1532 100644
--- a/osrng.cpp
+++ b/osrng.cpp
@@ -3,21 +3,49 @@
// Thanks to Leonard Janke for the suggestion for AutoSeededRandomPool.
#include "pch.h"
+#include "config.h"
#ifndef CRYPTOPP_IMPORTS
-#include "osrng.h"
+// Win32 has CryptoAPI and <wincrypt.h>. Windows 10 and Windows Store 10 have CNG and <bcrypt.h>.
+// There's a hole for Windows Phone 8 and Windows Store 8. There is no userland crypto available.
+// Also see http://stackoverflow.com/questions/36974545/random-numbers-for-windows-phone-8-and-windows-store-8
+#if defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(OS_RNG_AVAILABLE)
+# pragma message("WARNING: Compiling for Windows but an OS RNG is not available. This is likely a Windows Phone 8 or Windows Store 8 app.")
+#endif
-#ifdef OS_RNG_AVAILABLE
+#if !defined(OS_NO_DEPENDENCE) && defined(OS_RNG_AVAILABLE)
+#include "osrng.h"
#include "rng.h"
#ifdef CRYPTOPP_WIN32_AVAILABLE
-#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0400
-#endif
+//#ifndef _WIN32_WINNT
+//#define _WIN32_WINNT 0x0400
+//#endif
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+#if defined(USE_MS_CRYPTOAPI)
#include <wincrypt.h>
+#ifndef CRYPT_NEWKEYSET
+# define CRYPT_NEWKEYSET 0x00000008
+#endif
+#ifndef CRYPT_MACHINE_KEYSET
+# define CRYPT_MACHINE_KEYSET 0x00000020
+#endif
+#elif defined(USE_MS_CNGAPI)
+//#include <ntdef.h>
+#include <bcrypt.h>
+#ifndef BCRYPT_SUCCESS
+# define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+#ifndef STATUS_INVALID_PARAMETER
+# define STATUS_INVALID_PARAMETER 0xC000000D
+#endif
+#ifndef STATUS_INVALID_HANDLE
+# define STATUS_INVALID_HANDLE 0xC0000008
+#endif
+#endif
#endif
#ifdef CRYPTOPP_UNIX_AVAILABLE
@@ -45,18 +73,62 @@ OS_RNG_Err::OS_RNG_Err(const std::string &operation)
#ifdef CRYPTOPP_WIN32_AVAILABLE
-MicrosoftCryptoProvider::MicrosoftCryptoProvider()
+#if defined(USE_MS_CNGAPI)
+inline DWORD NtStatusToErrorCode(NTSTATUS status)
+{
+ if (status == STATUS_INVALID_PARAMETER)
+ return ERROR_INVALID_PARAMETER;
+ else if (status == STATUS_INVALID_HANDLE)
+ return ERROR_INVALID_HANDLE;
+ else
+ return (DWORD)status;
+}
+#endif
+
+#if defined(UNICODE) || defined(_UNICODE)
+# define CRYPTOPP_CONTAINER L"Crypto++ RNG"
+#else
+# define CRYPTOPP_CONTAINER "Crypto++ RNG"
+#endif
+
+MicrosoftCryptoProvider::MicrosoftCryptoProvider() : m_hProvider(0)
{
+#if defined(USE_MS_CRYPTOAPI)
+ // See http://support.microsoft.com/en-us/kb/238187 for CRYPT_NEWKEYSET fallback strategy
if (!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
- throw OS_RNG_Err("CryptAcquireContext");
+ {
+ const DWORD firstErr = GetLastError();
+ if (!CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET /*user*/) &&
+ !CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET))
+ {
+ // Set original error with original code
+ SetLastError(firstErr);
+ throw OS_RNG_Err("CryptAcquireContext");
+ }
+ }
+#elif defined(USE_MS_CNGAPI)
+ NTSTATUS ret = BCryptOpenAlgorithmProvider(&m_hProvider, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
+ if (!(BCRYPT_SUCCESS(ret)))
+ {
+ // Hack... OS_RNG_Err calls GetLastError()
+ SetLastError(NtStatusToErrorCode(ret));
+ throw OS_RNG_Err("BCryptOpenAlgorithmProvider");
+ }
+#endif
}
MicrosoftCryptoProvider::~MicrosoftCryptoProvider()
{
- CryptReleaseContext(m_hProvider, 0);
+#if defined(USE_MS_CRYPTOAPI)
+ if (m_hProvider)
+ CryptReleaseContext(m_hProvider, 0);
+#elif defined(USE_MS_CNGAPI)
+ if (m_hProvider)
+ BCryptCloseAlgorithmProvider(m_hProvider, 0);
+#endif
}
-#endif
+#endif // CRYPTOPP_WIN32_AVAILABLE
NonblockingRng::NonblockingRng()
{
@@ -77,16 +149,24 @@ NonblockingRng::~NonblockingRng()
void NonblockingRng::GenerateBlock(byte *output, size_t size)
{
#ifdef CRYPTOPP_WIN32_AVAILABLE
-# ifdef WORKAROUND_MS_BUG_Q258000
- const MicrosoftCryptoProvider &m_Provider = Singleton<MicrosoftCryptoProvider>().Ref();
-# endif
- if (!CryptGenRandom(m_Provider.GetProviderHandle(), (DWORD)size, output))
+ // Acquiring a provider is expensive. Do it once and retain the reference.
+ static const MicrosoftCryptoProvider &hProvider = Singleton<MicrosoftCryptoProvider>().Ref();
+# if defined(USE_MS_CRYPTOAPI)
+ if (!CryptGenRandom(hProvider.GetProviderHandle(), (DWORD)size, output))
throw OS_RNG_Err("CryptGenRandom");
+# elif defined(USE_MS_CNGAPI)
+ NTSTATUS ret = BCryptGenRandom(hProvider.GetProviderHandle(), output, (ULONG)size, 0);
+ if (!(BCRYPT_SUCCESS(ret)))
+ {
+ // Hack... OS_RNG_Err calls GetLastError()
+ SetLastError(NtStatusToErrorCode(ret));
+ throw OS_RNG_Err("BCryptGenRandom");
+ }
+# endif
#else
while (size)
{
ssize_t len = read(m_fd, output, size);
-
if (len < 0)
{
// /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well)
@@ -99,10 +179,10 @@ void NonblockingRng::GenerateBlock(byte *output, size_t size)
output += len;
size -= len;
}
-#endif
+#endif // CRYPTOPP_WIN32_AVAILABLE
}
-#endif
+#endif // NONBLOCKING_RNG_AVAILABLE
// *************************************************************
@@ -151,7 +231,7 @@ void BlockingRng::GenerateBlock(byte *output, size_t size)
}
}
-#endif
+#endif // BLOCKING_RNG_AVAILABLE
// *************************************************************
@@ -187,6 +267,6 @@ void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
NAMESPACE_END
-#endif
+#endif // OS_RNG_AVAILABLE
-#endif
+#endif // CRYPTOPP_IMPORTS