/// <summary> /// The first time you ever instantiate EntropyFileRNG, you *must* provide a newSeed. Otherwise, CryptographicException /// will be thrown. You better ensure it's at least 128 bits (16 bytes), preferably much more (>=32 bytes). Any subsequent /// time you instantiate EntropyFileRNG, you may use the parameter-less constructor, and it will leverage the original seed. /// Whenever you provide more seed bytes, entropy is always increased. (Does not lose previous entropy bytes.) /// NOTICE: byte[] newSeed will be zero'd out before returning, for security reasons. /// </summary> public EntropyFileRNG(byte[] newSeed = null, MixingAlgorithm mixingAlgorithm = MixingAlgorithm.SHA512, PrngAlgorithm prngAlgorithm = PrngAlgorithm.SHA512_512bit) { this.myMixingAlgorithm = mixingAlgorithm; this.myRNGAlgorithm = prngAlgorithm; byte[] pool; Initialize(out pool, this.myMixingAlgorithm, newSeed); // Clears the newSeed before returning CreateNewPRNG(pool); // Clears pool contents before returning this.mySeedMaterialAdded_Handler = new EventHandler<SeedMaterialAddedEventArgs>(AddedSeedMaterialMethod); SeedMaterialAdded += this.mySeedMaterialAdded_Handler; }
private static HashAlgorithm CreateMyHashAlgorithm(MixingAlgorithm algorithm) { switch (algorithm) { case MixingAlgorithm.MD5: return MD5.Create(); case MixingAlgorithm.RIPEMD160: return RIPEMD160.Create(); case MixingAlgorithm.SHA1: return SHA1.Create(); case MixingAlgorithm.SHA256: return SHA256.Create(); case MixingAlgorithm.SHA512: return SHA512.Create(); default: throw new ArgumentException("Unsupported algorithm"); } }
/// <summary> /// Opens randfile (or creates randfile, if newSeed provided and randfile nonexistent), reads in pool data, /// plants newSeed (if provided), modifies and writes out randfile. /// NOTICE: zero's the contents of newSeed before returning. /// </summary> private static void Initialize(out byte[] pool, MixingAlgorithm mixingAlgorithm, byte[] newSeed = null) { if (newSeed != null && newSeed.Length < 8) { throw new CryptographicException("Length >= 16 would be normal. Length 8 is lame. Length < 8 is insane."); } HashAlgorithm myHashAlgorithm = CreateMyHashAlgorithm(mixingAlgorithm); try { FileStream randFileStream = OpenRandFile(); // retries as much as 10,000 times try { pool = new byte[PoolSize]; int poolPosition = 0; if (randFileStream.Length == 0) { if (newSeed == null) { // Since newSeed is null, we require randFile contents. But it's zero. Fail. throw new CryptographicException("randFile nonexistent or zero-length, and newSeed not provided. randFile must be seeded before use."); } else { // WriteRandFileContents will plant newSeed WriteRandFileContents(randFileStream, newSeed, myHashAlgorithm, pool, ref poolPosition); } } else { // If the file already has data in it, then we read both "pool" and "poolPosition" from it. ReadRandFileContents(randFileStream, ref poolPosition, pool); // WriteRandFileContents will plant newSeed, if one was provided WriteRandFileContents(randFileStream, newSeed, myHashAlgorithm, pool, ref poolPosition); } } finally { randFileStream.Flush(); randFileStream.Close(); } } finally { myHashAlgorithm.Dispose(); if (newSeed != null) { Array.Clear(newSeed, 0, newSeed.Length); } } }
/// <summary> /// NOTICE: byte[] newSeed will be zero'd out before returning, for security reasons. /// </summary> public static void AddSeedMaterial(byte[] newSeed, MixingAlgorithm mixingAlgorithm = MixingAlgorithm.SHA256) { byte[] pool; Initialize(out pool, mixingAlgorithm, newSeed); // Clears the newSeed before returning try { if (SeedMaterialAdded != null) { SeedMaterialAdded(null, new SeedMaterialAddedEventArgs() { NewSeed = pool }); } } catch (Exception e) { // This should never occur, but I've seen it happen before, that a race condition occurs *just* in between // the checking of SeedMaterialAdded == null, and calling the method, that an instance unsubscribes, and then // it throws an exception. // So in general, if any such exception occurs, swallow it and ignore it. But if we happen to be debugging, // then have the debugger look at it. if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); GC.KeepAlive(e); // suppresses warning about unused variable } } Array.Clear(pool, 0, pool.Length); }