BCryptGenRandom() private method

private BCryptGenRandom ( IntPtr hAlgorithm, [ pbBuffer, int cbBuffer, int dwFlags ) : NTSTATUS
hAlgorithm System.IntPtr
pbBuffer [
cbBuffer int
dwFlags int
return NTSTATUS
		}//NextBytes()

		void NextBytesInternal(ArraySegment<byte> bufferSegment)
		{
			BCrypt.NTSTATUS status;
			var buffer = bufferSegment.Array;
			var offset = bufferSegment.Offset;
			var count = bufferSegment.Count;

			if (count > CACHE_THRESHOLD)
			{
				status = (offset == 0) ? BCrypt.BCryptGenRandom(buffer, count) : BCrypt.BCryptGenRandom_PinnedBuffer(buffer, offset, count);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				throw new CryptographicException((int)status);
			}

			lock (_byteCache)
			{
				if (_byteCachePosition + count <= BYTE_CACHE_SIZE)
				{
					Utils.BlockCopy(_byteCache, _byteCachePosition, buffer, offset, count);
					_byteCachePosition += count;
					return;
				}

				status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = count;
					Utils.BlockCopy(_byteCache, 0, buffer, offset, count);
					return;
				}
				throw new CryptographicException((int)status);
			}// lock
		}//NextBytesInternal()
		}//NextBytes()

		void NextBytesInternal(byte[] buffer, int offset, int count)
		{
			BCrypt.NTSTATUS status;

			if (count > CACHE_THRESHOLD)
			{
				status = (offset == 0) ? BCrypt.BCryptGenRandom(buffer, count) : BCrypt.BCryptGenRandom_WithOffset(buffer, offset, count);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				ThrowNewCryptographicException((int)status);
			}

			lock (_byteCache)
			{
				if (_byteCachePosition + count <= BYTE_CACHE_SIZE)
				{
					Utils.BlockCopy(_byteCache, _byteCachePosition, buffer, offset, count);
					_byteCachePosition += count;
					return;
				}

				status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = count;
					Utils.BlockCopy(_byteCache, 0, buffer, offset, count);
					return;
				}
				ThrowNewCryptographicException((int)status);
			}// lock
		}//NextBytesInternal()
		}//GetRandomUInt()

		/// <summary>
		/// Gets one random unsigned 64bit integer in a thread safe manner.
		/// </summary>
		ulong GetRandomULong()
		{
			BCrypt.NTSTATUS status;
			while (true)
			{
				int currentByteCachePosition = Interlocked.Add(ref _byteCachePosition, sizeof(ulong));
				if (currentByteCachePosition <= BYTE_CACHE_SIZE && currentByteCachePosition > 0)
					return BitConverter.ToUInt64(_byteCache, currentByteCachePosition - sizeof(ulong));

				lock (_byteCache)
				{
					currentByteCachePosition = _byteCachePosition; // atomic read
					if (currentByteCachePosition > (BYTE_CACHE_SIZE - sizeof(ulong)) || currentByteCachePosition <= 0)
					{
						status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
						if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
						{
							_byteCachePosition = sizeof(ulong); // atomic write
							return BitConverter.ToUInt64(_byteCache, 0);
						}

						// defensive logic to prevent _byteCachePosition from wrapping into valid range due to BCryptGenRandom failures
						if (currentByteCachePosition > BYTE_CACHE_SIZE || currentByteCachePosition < 0) _byteCachePosition = BYTE_CACHE_SIZE;

						throw new CryptographicException((int)status);
					}// if outside the valid range
				}// lock
			}// while(true)
		}//GetRandomULong()
Example #4
0
		}//NextBytesInternal()

#if (NETCOREAPP2_1 || NETSTANDARD2_1)
		void NextBytesInternal_Span(Span<byte> buffer, int count)
		{
			BCrypt.NTSTATUS status;

			if (count > CACHE_THRESHOLD)
			{
				status = BCrypt.BCryptGenRandom_Span(buffer);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				ThrowNewCryptographicException((int)status);
			}

			lock (_byteCache)
			{
				if (_byteCachePosition + count <= BYTE_CACHE_SIZE)
				{
					new Span<byte>(_byteCache, _byteCachePosition, count).CopyTo(buffer);
					_byteCachePosition += count;
					return;
				}

				status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = count;
					new Span<byte>(_byteCache, 0, count).CopyTo(buffer);
					return;
				}
				ThrowNewCryptographicException((int)status);
			}// lock
		}//NextBytesInternal_Span()
		}// ctor

		static void SanityCheck()
		{
			var testBuffer = new byte[BYTE_CACHE_SIZE / 2];
			int status, i, j;
			const int COLLISION_FREE_BLOCK_SIZE = 16;

			status = (int)BCrypt.BCryptGenRandom(testBuffer, testBuffer.Length);
			if (status != (int)BCrypt.NTSTATUS.STATUS_SUCCESS) throw new CryptographicException(status);

			if (testBuffer.Length < COLLISION_FREE_BLOCK_SIZE * 2) return; // should be compiled away
			for (i = 0; i < testBuffer.Length - COLLISION_FREE_BLOCK_SIZE; i += COLLISION_FREE_BLOCK_SIZE)
			{
				for (j = 0, status = 0; j < COLLISION_FREE_BLOCK_SIZE; ++j)
					status |= testBuffer[i + j] ^ testBuffer[i + j + COLLISION_FREE_BLOCK_SIZE];
				if (status == 0) throw new CryptographicException("CryptoRandom failed sanity check #2.");
			}
		}// SanityCheck()
		void NextBytesInternal(ArraySegment<byte> bufferSegment)
		{
			BCrypt.NTSTATUS status;
			var buffer = bufferSegment.Array;
			var offset = bufferSegment.Offset;
			var count = bufferSegment.Count;

			if (count > CACHE_THRESHOLD)
			{
				status = (offset == 0) ? BCrypt.BCryptGenRandom(buffer, count) : BCrypt.BCryptGenRandom_PinnedBuffer(buffer, offset, count);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				throw new CryptographicException((int)status);
			}

			while (true)
			{
				int currentByteCachePosition = Interlocked.Add(ref _byteCachePosition, count);
				if (currentByteCachePosition <= BYTE_CACHE_SIZE && currentByteCachePosition > 0)
				{
					Utils.BlockCopy(_byteCache, currentByteCachePosition - count, buffer, 0, count);
					return;
				}

				lock (_byteCache)
				{
					currentByteCachePosition = _byteCachePosition; // atomic read
					if (currentByteCachePosition > (BYTE_CACHE_SIZE - count) || currentByteCachePosition <= 0)
					{
						status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
						if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
						{
							_byteCachePosition = count; // atomic write
							Utils.BlockCopy(_byteCache, 0, buffer, 0, count);
							return;
						}

						// defensive logic to prevent _byteCachePosition from wrapping into valid range due to BCryptGenRandom failures
						if (currentByteCachePosition > BYTE_CACHE_SIZE || currentByteCachePosition < 0) _byteCachePosition = BYTE_CACHE_SIZE;

						throw new CryptographicException((int)status);
					}// if outside the valid range
				}// lock
			}// while(true)
		}//NextBytes()
		internal static NTSTATUS BCryptGenRandom_PinnedBuffer(byte[] pbBuffer, int obBuffer, int cbBuffer)
		{
			Debug.Assert(pbBuffer != null);
			Debug.Assert(cbBuffer >= 0 && obBuffer >= 0 && (obBuffer + cbBuffer) <= pbBuffer.Length);

			GCHandle pinnedBufferHandle = default;
			NTSTATUS status;
			try
			{
				pinnedBufferHandle = GCHandle.Alloc(pbBuffer, GCHandleType.Pinned);
				status = BCrypt.BCryptGenRandom(IntPtr.Zero, pinnedBufferHandle.AddrOfPinnedObject() + obBuffer, cbBuffer, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
			}
			finally
			{
				if (pinnedBufferHandle.IsAllocated) pinnedBufferHandle.Free();
			}

			return status;
		}// BCryptGenRandom()
		}//GetRandomInt()

		/// <summary>
		/// Gets one random signed 64bit integer in a thread safe manner.
		/// </summary>
		long GetRandomLong()
		{
			lock (_byteCache)
			{
				if (_byteCachePosition + sizeof(long) <= BYTE_CACHE_SIZE)
				{
					var result = BitConverter.ToInt64(_byteCache, _byteCachePosition);
					_byteCachePosition += sizeof(long);
					return result;
				}

				BCrypt.NTSTATUS status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = sizeof(long);
					return BitConverter.ToInt64(_byteCache, 0);
				}
				throw new CryptographicException((int)status);
			}// lock
		}//GetRandomLong()
		}//NextBytesInternal()

		/// <summary>
		/// Gets one random signed 32bit integer in a thread safe manner.
		/// </summary>
		int GetRandomInt()
		{
			lock (_byteCache)
			{
				if (_byteCachePosition + sizeof(int) <= BYTE_CACHE_SIZE)
				{
					var result = Unsafe.As<byte, int>(ref _byteCache[_byteCachePosition]);//BitConverter.ToInt32(_byteCache, _byteCachePosition);
					_byteCachePosition += sizeof(int);
					return result;
				}

				BCrypt.NTSTATUS status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = sizeof(int);
					return Unsafe.As<byte, int>(ref _byteCache[0]);//BitConverter.ToInt32(_byteCache, 0);
				}
				return ThrowNewCryptographicException((int)status);
			}// lock
		}//GetRandomInt()