/// <summary> /// Construct a new cryptographically secure random stream object. /// </summary> /// <param name="a">Algorithm to use.</param> /// <param name="pbKey">Initialization key. Must not be <c>null</c> /// and must contain at least 1 byte.</param> public CryptoRandomStream(CrsAlgorithm a, byte[] pbKey) { if (pbKey == null) { Debug.Assert(false); throw new ArgumentNullException("pbKey"); } int cbKey = pbKey.Length; if (cbKey <= 0) { Debug.Assert(false); // Need at least one byte throw new ArgumentOutOfRangeException("pbKey"); } m_crsAlgorithm = a; if (a == CrsAlgorithm.ChaCha20) { byte[] pbKey32 = new byte[32]; byte[] pbIV12 = new byte[12]; using (SHA512Managed h = new SHA512Managed()) { byte[] pbHash = h.ComputeHash(pbKey); Array.Copy(pbHash, pbKey32, 32); Array.Copy(pbHash, 32, pbIV12, 0, 12); MemUtil.ZeroByteArray(pbHash); } m_chacha20 = new ChaCha20Cipher(pbKey32, pbIV12, true); } else if (a == CrsAlgorithm.Salsa20) { byte[] pbKey32 = CryptoUtil.HashSha256(pbKey); byte[] pbIV8 = new byte[8] { 0xE8, 0x30, 0x09, 0x4B, 0x97, 0x20, 0x5D, 0x2A }; // Unique constant m_salsa20 = new Salsa20Cipher(pbKey32, pbIV8); } else if (a == CrsAlgorithm.ArcFourVariant) { // Fill the state linearly m_pbState = new byte[256]; for (int w = 0; w < 256; ++w) { m_pbState[w] = (byte)w; } unchecked { byte j = 0, t; int inxKey = 0; for (int w = 0; w < 256; ++w) // Key setup { j += (byte)(m_pbState[w] + pbKey[inxKey]); t = m_pbState[0]; // Swap entries m_pbState[0] = m_pbState[j]; m_pbState[j] = t; ++inxKey; if (inxKey >= cbKey) { inxKey = 0; } } } GetRandomBytes(512); // Increases security, see cryptanalysis } else // Unknown algorithm { Debug.Assert(false); throw new ArgumentOutOfRangeException("a"); } }