Ejemplo n.º 1
0
        /// <summary>
        /// A static publicly exposed version of GetDerivedKeyBytes_PBKDF2_HMACSHA512 which matches the exact specification in Rfc2898 PBKDF2 using HMACSHA512
        /// </summary>
        /// <param name="P">Password passed as a Byte Array</param>
        /// <param name="S">Salt passed as a Byte Array</param>
        /// <param name="c">Iterations to perform the underlying PRF over</param>
        /// <param name="dkLen">Length of Bytes to return, an AES 256 key wold require 32 Bytes</param>
        /// <returns>Derived Key in Byte Array form ready for use by chosen encryption function</returns>
        public static Byte[] PBKDF2(Byte[] P, Byte[] S, int c = CMinIterations, int dkLen = hLen)
        {
            Rfc2898_pbkdf2_hmacsha512 rfcObj = new Rfc2898_pbkdf2_hmacsha512(P, S, c);

            return(rfcObj.GetDerivedKeyBytes_PBKDF2_HMACSHA512(dkLen));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Safely get Crypto Random byte array at the size you desire.
        /// </summary>
        /// <param name="size">Size of the crypto random byte array to build</param>
        /// <param name="seedStretchingIterations">Optional parameter to specify how many SHA512 passes occur over our seed before we use it. Higher value is greater security but uses more computational power. If random byte generation is taking too long try specifying values lower than the default of 5000. You can set 0 to turn off stretching</param>
        /// <returns>A byte array of completely random bytes</returns>
        public static byte[] GetRandomBytes(int size, int seedStretchingIterations = 5000)
        {
            //varies from system to system, a tiny amount of entropy, tiny
            int processorCount = System.Environment.ProcessorCount;

            //another tiny amount of entropy due to the varying nature of thread id
            int currentThreadId = System.Environment.CurrentManagedThreadId;

            //a GUID is considered unique so also provides some entropy
            byte[] guidBytes = Guid.NewGuid().ToByteArray();

            //this combined with DateTime.Now is the default seed in BouncyCastles SecureRandom
            byte[] threadedSeedBytes = new ThreadedSeedGenerator().GenerateSeed(24, true);

            byte[] output = new byte[size];

            //if for whatever reason it says 0 or less processors just make it 16
            if (processorCount <= 0)
            {
                processorCount = 16;
            }

            //if some fool trys to set stretching to < 0 we protect them from themselves
            if (seedStretchingIterations < 0)
            {
                seedStretchingIterations = 0;
            }

            //we create a SecureRandom based off SHA256 just to get a random int which will be used to determine what bytes to "take" from our built seed hash and then rehash those taken seed bytes using a KDF (key stretching) such that it would slow down anyone trying to rebuild private keys from common seeds.
            SecureRandom seedByteTakeDetermine = SecureRandom.GetInstance("SHA256PRNG");

            guidBytes = HmacSha512Digest(guidBytes, 0, guidBytes.Length, MergeByteArrays(threadedSeedBytes, UTF8Encoding.UTF8.GetBytes(Convert.ToString(System.Environment.TickCount))));

            try
            {
                seedByteTakeDetermine.SetSeed(((DateTime.Now.Ticks - System.Environment.TickCount) * processorCount) + currentThreadId);
                seedByteTakeDetermine.SetSeed(guidBytes);
                seedByteTakeDetermine.SetSeed(seedByteTakeDetermine.GenerateSeed(1 + currentThreadId));
                seedByteTakeDetermine.SetSeed(threadedSeedBytes);
            }
            catch
            {
                try
                {
                    //if the number is too big or causes an error or whatever we will failover to this, as it's not our main source of random bytes and not used in the KDF stretching it's ok.
                    seedByteTakeDetermine.SetSeed((DateTime.Now.Ticks - System.Environment.TickCount) + currentThreadId);
                    seedByteTakeDetermine.SetSeed(guidBytes);
                    seedByteTakeDetermine.SetSeed(seedByteTakeDetermine.GenerateSeed(1 + currentThreadId));
                    seedByteTakeDetermine.SetSeed(threadedSeedBytes);
                }
                catch
                {
                    //if again the number is too big or causes an error or whatever we will failover to this, as it's not our main source of random bytes and not used in the KDF stretching it's ok.
                    seedByteTakeDetermine.SetSeed(DateTime.Now.Ticks - System.Environment.TickCount);
                    seedByteTakeDetermine.SetSeed(guidBytes);
                    seedByteTakeDetermine.SetSeed(seedByteTakeDetermine.GenerateSeed(1 + currentThreadId));
                    seedByteTakeDetermine.SetSeed(threadedSeedBytes);
                }
            }

            //hardened seed
            byte[] toHashForSeed;

            try
            {
                toHashForSeed = BitConverter.GetBytes(((processorCount - seedByteTakeDetermine.Next(0, processorCount)) * System.Environment.TickCount) * currentThreadId);
            }
            catch
            {
                try
                {
                    //if the number was too large or something we failover to this
                    toHashForSeed = BitConverter.GetBytes(((processorCount - seedByteTakeDetermine.Next(0, processorCount)) + System.Environment.TickCount) * currentThreadId);
                }
                catch
                {
                    //if the number was again too large or something we failover to this
                    toHashForSeed = BitConverter.GetBytes(((processorCount - seedByteTakeDetermine.Next(0, processorCount)) + System.Environment.TickCount) + currentThreadId);
                }
            }

            toHashForSeed = Sha512Digest(toHashForSeed, 0, toHashForSeed.Length);
            toHashForSeed = MergeByteArrays(toHashForSeed, guidBytes);
            toHashForSeed = MergeByteArrays(toHashForSeed, BitConverter.GetBytes(currentThreadId));
            toHashForSeed = MergeByteArrays(toHashForSeed, BitConverter.GetBytes(DateTime.UtcNow.Ticks));
            toHashForSeed = MergeByteArrays(toHashForSeed, BitConverter.GetBytes(DateTime.Now.Ticks));
            toHashForSeed = MergeByteArrays(toHashForSeed, BitConverter.GetBytes(System.Environment.TickCount));
            toHashForSeed = MergeByteArrays(toHashForSeed, BitConverter.GetBytes(processorCount));
            toHashForSeed = MergeByteArrays(toHashForSeed, threadedSeedBytes);
            toHashForSeed = Sha512Digest(toHashForSeed, 0, toHashForSeed.Length);

            //we grab a random amount of bytes between 24 and 64 to rehash  make a new set of 64 bytes, using guidBytes as hmackey
            toHashForSeed = Sha512Digest(HmacSha512Digest(toHashForSeed, 0, seedByteTakeDetermine.Next(24, 64), guidBytes), 0, 64);

            seedByteTakeDetermine.SetSeed(currentThreadId + (DateTime.Now.Ticks - System.Environment.TickCount));

            //by making the iterations also random we are again making it hard to determin our seed by brute force
            int iterations = seedStretchingIterations - (seedByteTakeDetermine.Next(0, (seedStretchingIterations / seedByteTakeDetermine.Next(9, 100))));

            //here we use key stretching techniques to make it harder to replay the random seed values by forcing computational time up
            byte[] seedMaterial = Rfc2898_pbkdf2_hmacsha512.PBKDF2(toHashForSeed, seedByteTakeDetermine.GenerateSeed(64), iterations);

            //build a SecureRandom object that uses Sha512 to provide randomness and we will give it our created above hardened seed
            SecureRandom secRand = new SecureRandom(new Org.BouncyCastle.Crypto.Prng.DigestRandomGenerator(new Sha512Digest()));

            //set the seed that we created just above
            secRand.SetSeed(seedMaterial);

            //generate more seed materisal
            secRand.SetSeed(currentThreadId);
            secRand.SetSeed(MergeByteArrays(guidBytes, threadedSeedBytes));
            secRand.SetSeed(secRand.GenerateSeed(1 + secRand.Next(64)));

            //add our prefab seed again onto the previous material just to be sure the above statements are adding and not clobbering seed material
            secRand.SetSeed(seedMaterial);

            //here we derive our random bytes
            secRand.NextBytes(output, 0, size);

            return(output);
        }