context: /*
    KeePass Password Safe - The Open-Source Password Manager
    Copyright (C) 2003-2016 Dominik Reichl <dominik.reichl@t-online.de>
    
    Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Globalization;
  using System.IO;
  
  #if !KeePassUAP
  using System.Drawing;
  using System.Security.Cryptography;
  #endif
  
  using KeePassLib.Native;
  using KeePassLib.Utility;
  
  namespace KeePassLib.Cryptography
  {
  	/// <summary>
  	/// Cryptographically secure pseudo-random number generator.
  	/// The returned values are unpredictable and cannot be reproduced.
  	/// <c>CryptoRandom</c> is a singleton class.
  	/// </summary>
  	public sealed class CryptoRandom
  	{
  		private byte[] m_pbEntropyPool = new byte[64];
  		private ulong m_uCounter;
  		private RNGCryptoServiceProvider m_rng = new RNGCryptoServiceProvider();
  		private ulong m_uGeneratedBytesCount = 0;
  
  		private static object g_oSyncRoot = new object();
  		private object m_oSyncRoot = new object();
  
  		private static CryptoRandom g_pInstance = null;
  		public static CryptoRandom Instance
  		{
  			get
  			{
  				CryptoRandom cr;
  				lock(g_oSyncRoot)
  				{
  					cr = g_pInstance;
  					if(cr == null)
  					{
  						cr = new CryptoRandom();
  						g_pInstance = cr;
  					}
  				}
  
  				return cr;
  			}
  		}
  
  		/// <summary>
  		/// Get the number of random bytes that this instance generated so far.
  		/// Note that this number can be higher than the number of random bytes
  		/// actually requested using the <c>GetRandomBytes</c> method.
  		/// </summary>
  		public ulong GeneratedBytesCount
  		{
  			get
  			{
  				ulong u;
  				lock(m_oSyncRoot) { u = m_uGeneratedBytesCount; }
  				return u;
  			}
  		}
  
  		/// <summary>
  		/// Event that is triggered whenever the internal <c>GenerateRandom256</c>
  		/// method is called to generate random bytes.
  		/// </summary>
  		public event EventHandler GenerateRandom256Pre;
  
  		private CryptoRandom()
  		{
  
  			// byte[] pb = new byte[8];
  			// rWeak.NextBytes(pb);
  			// m_uCounter = MemUtil.BytesToUInt64(pb);
  			m_uCounter = (ulong)DateTime.UtcNow.ToBinary();
  
  			AddEntropy(GetSystemData());
  			AddEntropy(GetCspData());
  		}
  
  		/// <summary>
  		/// Update the internal seed of the random number generator based
  		/// on entropy data.
  		/// This method is thread-safe.
  		/// </summary>
  		/// <param name="pbEntropy">Entropy bytes.</param>
  		public void AddEntropy(byte[] pbEntropy)
  		{
  			if(pbEntropy == null) { Debug.Assert(false); return; }
  			if(pbEntropy.Length == 0) { Debug.Assert(false); return; }
  
  			byte[] pbNewData = pbEntropy;
  			if(pbEntropy.Length > 64)
  			{
  #if KeePassLibSD
  				using(SHA256Managed shaNew = new SHA256Managed())
  #else
  				using(SHA512Managed shaNew = new SHA512Managed())
  #endif
  				{
  				pbNewData = shaNew.ComputeHash(pbEntropy);
  			}
  			}
  
  			lock(m_oSyncRoot)
  			{
  				int cbPool = m_pbEntropyPool.Length;
  				int cbNew = pbNewData.Length;
  
  				byte[] pbCmp = new byte[cbPool + cbNew];
  				Array.Copy(m_pbEntropyPool, pbCmp, cbPool);
  				Array.Copy(pbNewData, 0, pbCmp, cbPool, cbNew);
  
  				MemUtil.ZeroByteArray(m_pbEntropyPool);
  
  #if KeePassLibSD
  				using(SHA256Managed shaPool = new SHA256Managed())
  #else
  				using(SHA512Managed shaPool = new SHA512Managed())
  #endif
  				{
  					m_pbEntropyPool = shaPool.ComputeHash(pbCmp);
  			}
  
  				MemUtil.ZeroByteArray(pbCmp);
  		}
  		}
  
  		private static byte[] GetSystemData()
  		{
  			MemoryStream ms = new MemoryStream();
  			byte[] pb;
  
  			pb = MemUtil.Int32ToBytes(Environment.TickCount);
  			MemUtil.Write(ms, pb);
  
  			pb = MemUtil.Int64ToBytes(DateTime.UtcNow.ToBinary());
  			MemUtil.Write(ms, pb);
  
  #if !KeePassLibSD
  			/*Not supported on Android
  			// In try-catch for systems without GUI;
  			// https://sourceforge.net/p/keepass/discussion/329221/thread/20335b73/
  			try
  			{
  				Point pt = Cursor.Position;
  				pb = MemUtil.Int32ToBytes(pt.X);
  				MemUtil.Write(ms, pb);
  				pb = MemUtil.Int32ToBytes(pt.Y);
  				MemUtil.Write(ms, pb);
  			}
  			catch(Exception) { }
  			 */
  #endif
  
  			pb = MemUtil.UInt32ToBytes((uint)NativeLib.GetPlatformID());
  			MemUtil.Write(ms, pb);
  
  			try
  			{
  #if KeePassUAP
  				string strOS = EnvironmentExt.OSVersion.VersionString;
  #else
  				string strOS = Environment.OSVersion.VersionString;
  #endif
  				AddStrHash(ms, strOS);
  
  				pb = MemUtil.Int32ToBytes(Environment.ProcessorCount);
  				MemUtil.Write(ms, pb);
  
  #if !KeePassUAP
  				AddStrHash(ms, Environment.CommandLine);
  
  				pb = MemUtil.Int64ToBytes(Environment.WorkingSet);
  				MemUtil.Write(ms, pb);
  #endif
  			}
  			catch(Exception) { Debug.Assert(false); }
  
  			try
  			{
  				foreach(DictionaryEntry de in Environment.GetEnvironmentVariables())
  				{
  					AddStrHash(ms, (de.Key as string));
  					AddStrHash(ms, (de.Value as string));
  				}
  			}
  			catch(Exception) { Debug.Assert(false); }
  
  #if KeePassUAP
  			pb = DiagnosticsExt.GetProcessEntropy();
  			MemUtil.Write(ms, pb);
  #elif !KeePassLibSD
  			Process p = null;
  			try
  			{
  				p = Process.GetCurrentProcess();
  				// Not supported in Mono 1.2.6:
  				pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
  				MemUtil.Write(ms, pb);
  				pb = MemUtil.Int32ToBytes(p.HandleCount);
  				MemUtil.Write(ms, pb);
  
gt: 				pb = MemUtil.Int32ToBytes(p.Id);