/// <summary> /// Read all bytes from the specified stream and return it as an array. /// </summary> public static byte[] ReadAll(Stream stream, out int count) { // create the byte collection for the bytes byte[] result = BufferCache.Get(); // create a buffer components int length = Global.BufferSizeLocal; count = 0; // iterate while there are more bytes to be read while (length == Global.BufferSizeLocal) { // does the result buffer require resizing? if (count + Global.BufferSizeLocal > result.Length) { // yes, resize Array.Resize(ref result, count + count); } // read the next buffer length = stream.Read(result, count, Global.BufferSizeLocal); count += length; } // return the byte buffer return(result); }
/// <summary> /// Decript the specified message. /// NOTE : CryptKey needs to be {KeyBitSize} bits! /// NOTE : AuthKey needs to be {KeyBitSize} bits! /// </summary> public static byte[] Decrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0) { using (var hmac = new HMACSHA256(authKey)) { int sentTagLength = hmac.HashSize / 8; byte[] sentTag = BufferCache.Get(sentTagLength); // calculate tag byte[] calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTagLength); const int ivLength = BlockBitSize / 8; // if message length is to small just return null if (encryptedMessage.Length < sentTagLength + nonSecretPayloadLength + ivLength) { return(null); } // grab sent Array.Copy(encryptedMessage, encryptedMessage.Length - sentTagLength, sentTag, 0, sentTagLength); //Compare Tag with constant time comparison var compare = 0; for (var i = 0; i < sentTagLength; ++i) { compare |= sentTag[i] ^ calcTag[i]; } //if message doesn't authenticate return null if (compare != 0) { return(null); } using (var aes = new AesManaged { KeySize = KeyBitSize * 8, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }) { //Grab IV from message var iv = new byte[ivLength]; Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, ivLength); using (var decrypter = aes.CreateDecryptor(cryptKey, iv)) using (var decrypted = new MemoryStream()) { using (var decrypterStream = new CryptoStream(decrypted, decrypter, CryptoStreamMode.Write)) using (var binaryWriter = new BinaryWriter(decrypterStream)) { //Decrypt Cipher Text from Message binaryWriter.Write( encryptedMessage, nonSecretPayloadLength + ivLength, encryptedMessage.Length - nonSecretPayloadLength - ivLength - sentTagLength ); } // return the decrypted bytes as an array return(decrypted.ToArray()); } } } }
/// <summary> /// Get a stream as the result of the specified stream being compressed using the specified method. Optionally specify a length /// for the number of bytes to read. /// </summary> public static MemoryStream Compress(System.Net.DecompressionMethods method, CompressionLevel level, Stream source, int length = -1) { // create a new memory stream to receive the compressed stream MemoryStream memStream = new MemoryStream(Global.BufferSizeLocal); // resolve the compression stream Stream compressionStream; switch (method) { case System.Net.DecompressionMethods.Deflate: compressionStream = new GZipStream(memStream, level, true); break; case System.Net.DecompressionMethods.GZip: compressionStream = new DeflateStream(memStream, level, true); break; default: // no compression - return uncompressed source.CopyTo(memStream); return(memStream); } // was the length specifed? if (length == -1) { // no, write the buffer to the compression source.CopyTo(compressionStream, Global.BufferSizeLocal); } else { // yes, create a buffer for the copy byte[] buffer = BufferCache.Get(); int count = Global.BufferSizeLocal; // while the buffer is filled by reading from the stream while (count == Global.BufferSizeLocal) { // read from the stream count = length < Global.BufferSizeLocal ? source.Read(buffer, 0, length) : source.Read(buffer, 0, Global.BufferSizeLocal); // write to the compression stream compressionStream.Write(buffer, 0, count); // decrement the remaining bytes length -= count; } BufferCache.Set(buffer); } // close the compression stream compressionStream.Close(); // return the memory stream return(memStream); }
/// <summary> /// Secure hash of the specified text into base 64. /// </summary> public static string Hash(string text, bool ascii = true) { byte[] textBytes = ascii ? Encoding.ASCII.GetBytes(text) : Encoding.UTF8.GetBytes(text); byte[] bytes = BufferCache.Get(textBytes.Length + _salt.Length); Micron.CopyMemory(textBytes, bytes, textBytes.Length); Micron.CopyMemory(_salt, 0, bytes, textBytes.Length, _salt.Length); return(Convert.ToBase64String(_sha256.ComputeHash(bytes, 0, textBytes.Length + _salt.Length))); }
/// <summary> /// Inner method compress stream method. /// </summary> private static void Compress(IRun callback, Stream result, Stream destination, Stream source, int length, bool dispose) { // reset the buffer byte[] buffer = BufferCache.Get(); int count; if (length == -1) { // read from the stream count = source.Read(buffer, 0, Global.BufferSizeLocal); } else { // read from the stream count = length < Global.BufferSizeLocal ? source.Read(buffer, 0, length) : source.Read(buffer, 0, Global.BufferSizeLocal); // decrement the remaining bytes length -= count; } // write to the compression stream destination.Write(buffer, 0, count); // reset the buffer BufferCache.Set(buffer); // has the compression been completed? if (count == Global.BufferSizeLocal) { // no, run a new compression iteration ManagerUpdate.Control.AddSingle(Compress, callback, result, destination, source, length, dispose); } else { // yes, dispose of the compression stream destination.Dispose(); // should the source be disposed? yes if (dispose) { source.Dispose(); } // reset the memory stream position result.Position = 0L; // yes, run the callback callback.Run(); } }
/// <summary> /// Continuing compute of a hash of the specified stream. /// </summary> protected void ComputeHashInner(Stream stream, IAction <byte[]> onComplete, byte[] buffer, int index) { if (buffer == null) { buffer = BufferCache.Get(); index = 0; } int count = stream.Read(buffer, index, Global.BufferSizeLocal); bool last = count != Global.BufferSizeLocal; _h1 = _seed; _length = 0; // read 128 bits, 16 bytes, 2 longs in eacy cycle while (index + 16 <= count) { ulong k1 = GetUInt64(buffer, index); ulong k2 = GetUInt64(buffer, index + 8); _length += ReadSize; MixBody(k1, k2); index += 16; } if (index + 16 == count) { BufferCache.Set(buffer); buffer = null; } else { if (last) { // if the input MOD 16 != 0 if (index < count) { ProcessBytesRemaining(buffer, count - index, index); } onComplete.ArgA = Hash; onComplete.Run(); return; } Micron.CopyMemory(buffer, index, buffer, 0, count - index); index -= count; } ManagerUpdate.Control.AddSingle(ComputeHashInner, stream, onComplete, buffer, index); }
/// <summary> /// Compress the specified byte buffer. /// </summary> public static Stream Decompress(System.Net.DecompressionMethods method, Stream source, ref int count) { // create a new memory stream to receive the compressed stream MemoryStream memStream = new MemoryStream(); // resolve the compression stream Stream compressionStream; switch (method) { case System.Net.DecompressionMethods.Deflate: compressionStream = new DeflateStream(memStream, CompressionMode.Decompress, true); break; case System.Net.DecompressionMethods.GZip: compressionStream = new GZipStream(memStream, CompressionMode.Decompress, true); break; default: // no compression - return uncompressed return(source); } // write the buffer to the compression var buffer = BufferCache.Get(); int bufferCount = Global.BufferSizeLocal; while (bufferCount == Global.BufferSizeLocal) { bufferCount = source.Read(buffer, 0, count > Global.BufferSizeLocal ? Global.BufferSizeLocal : count); count -= bufferCount; compressionStream.Write(buffer, 0, bufferCount); } // pass the buffer back to the cache BufferCache.Set(buffer); // close the stream compressionStream.Dispose(); // get the count memStream.Position = 0L; count = (int)memStream.Length; // get the bytes from the memory stream return(memStream); }
/// <summary> /// Copy the content of a stream to another over a series of async tasks. /// </summary> public static void BufferedCopy(this Stream inStream, Stream outStream, IAction onComplete = null) { var buffer = BufferCache.Get(); int count = inStream.Read(buffer, 0, Global.BufferSizeLocal); if (count == 0) { outStream.Flush(); BufferCache.Set(buffer); if (onComplete != null) { onComplete.Run(); } } else { outStream.Write(buffer, 0, count); BufferCache.Set(buffer); ManagerUpdate.Control.AddSingle(BufferedCopy, inStream, outStream, onComplete); } }
/// <summary> /// Encrypt bytes with a string password. /// </summary> public static byte[] EncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null) { nonSecretPayload = nonSecretPayload ?? new byte[] {}; var payload = BufferCache.Get((SaltBitSize * 2) + nonSecretPayload.Length); Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length); int payloadIndex = nonSecretPayload.Length; byte[] cryptKey; byte[] authKey; //Use Random Salt to prevent pre-generated weak password attacks. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize, Iterations)) { var salt = generator.Salt; //Generate Keys cryptKey = generator.GetBytes(KeyBitSize); //Create Non Secret Payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); payloadIndex += salt.Length; } //Deriving separate key, might be less efficient than using HKDF, //but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize, Iterations)) { var salt = generator.Salt; //Generate Keys authKey = generator.GetBytes(KeyBitSize); //Create Rest of Non Secret Payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); } return(Encrypt(secretMessage, cryptKey, authKey, payload)); }
/// <summary> /// Decrypt with password. The stream must be navigatable. /// </summary> public static Stream DecryptWithPassword(Stream stream, string password, int nonSecretPayloadLength = 0) { const int ivLength = BlockBitSize / 8; var cryptSalt = new byte[SaltBitSize]; var authSalt = new byte[SaltBitSize]; var streamStartPosition = stream.Position; // grab Salt from Non-Secret Payload if (nonSecretPayloadLength != 0) { stream.Position = streamStartPosition + nonSecretPayloadLength; } stream.Read(cryptSalt, 0, SaltBitSize); stream.Read(authSalt, 0, SaltBitSize); // grab IV from message var iv = new byte[ivLength]; stream.Read(iv, 0, ivLength); byte[] cryptKey; byte[] authKey; // generate crypt key using (var generator = new Rfc2898DeriveBytes(password, cryptSalt, Iterations)) { cryptKey = generator.GetBytes(KeyBitSize); } // generate auth key using (var generator = new Rfc2898DeriveBytes(password, authSalt, Iterations)) { authKey = generator.GetBytes(KeyBitSize); } using (var hmac = new HMACSHA256(authKey)) { int sentTagLength = hmac.HashSize / 8; // if message length is to small just return null if (stream.Length - streamStartPosition < sentTagLength + nonSecretPayloadLength + ivLength) { throw new CryptographicException("Insufficient bytes in stream."); } // get the number of encrypted bytes that constitute the message int count = (int)(stream.Length - streamStartPosition - sentTagLength - nonSecretPayloadLength - ivLength - SaltBitSize - SaltBitSize); byte[] tagContent = BufferCache.Get(count); // read the bytes required for the tag hash count = stream.Read(tagContent, 0, count); // calculate the tag hash byte[] calcTag = hmac.ComputeHash(tagContent, 0, count); // read sent tag stream.Read(tagContent, 0, sentTagLength); // compare tag with constant time comparison var compare = 0; for (var i = 0; i < sentTagLength; ++i) { compare |= tagContent[i] ^ calcTag[i]; } BufferCache.Set(tagContent); // if message doesn't authenticate, throw if (compare != 0) { throw new CryptographicException("Data hash was not correct."); } using (var aes = new AesManaged { Key = cryptKey, IV = iv, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros }) { // create a memory stream for the decrypted bytes var decrypted = new MemoryStream(); using (var decrypter = aes.CreateDecryptor()) { var decrypterStream = new CryptoStream(decrypted, decrypter, CryptoStreamMode.Write); byte[] buffer = BufferCache.Get(); // move to the start of the encrypted data stream.Position = streamStartPosition + nonSecretPayloadLength + SaltBitSize + SaltBitSize + ivLength; // determine the bytes to be written to the decrypter stream int remaining = (int)(stream.Length - stream.Position - sentTagLength); // read the first buffer from the stream count = stream.Read(buffer, 0, remaining > Global.BufferSizeLocal ? Global.BufferSizeLocal : remaining); // while the buffer is full while (count == Global.BufferSizeLocal) { // decrement the remaining bytes remaining -= count; // write the buffer decrypterStream.Write(buffer, 0, count); // read a new buffer count = stream.Read(buffer, 0, Global.BufferSizeLocal); } // if the stream ended prematurely, throw if (count != remaining) { throw new EndOfStreamException(); } // write final buffer decrypterStream.Write(buffer, 0, count); BufferCache.Set(buffer); // flush the decryption stream decrypterStream.FlushFinalBlock(); // set the decrpted memory stream position decrypted.Position = 0; // return the decrypted stream return(decrypted); } } } }
/// <summary> /// Encrypt bytes from the stream using a string password. /// </summary> public static MemoryStream EncryptWithPassword(Stream stream, string password, byte[] nonSecretPayload = null) { byte[] payload; int payloadIndex; if (nonSecretPayload == null) { payload = BufferCache.Get(SaltBitSize * 2); payloadIndex = 0; } else { payload = BufferCache.Get((SaltBitSize * 2) + nonSecretPayload.Length); Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length); payloadIndex = nonSecretPayload.Length; } byte[] cryptKey; byte[] authKey; //Use Random Salt to prevent pre-generated weak password attacks. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize, Iterations)) { var salt = generator.Salt; // generate the cryptography key cryptKey = generator.GetBytes(KeyBitSize); // copy the salt Array.Copy(salt, 0, payload, payloadIndex, salt.Length); payloadIndex += salt.Length; } //Deriving separate key, might be less efficient than using HKDF, //but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF. using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize, Iterations)) { var salt = generator.Salt; // generate the auth key authKey = generator.GetBytes(KeyBitSize); // create the rest of the non-secret payload Array.Copy(salt, 0, payload, payloadIndex, salt.Length); payloadIndex += salt.Length; } byte[] iv; byte[] buffer = BufferCache.Get(); using (var aes = new AesManaged { Key = cryptKey, BlockSize = BlockBitSize, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros }) { // use random IV aes.GenerateIV(); iv = aes.IV; using (var encrypter = aes.CreateEncryptor()) using (var cipherStream = new MemoryStream()) using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write)) { // read from the stream int count = stream.Read(buffer, 0, Global.BufferSizeLocal); // while the buffer is filled while (count == Global.BufferSizeLocal) { // write to the crypto stream cryptoStream.Write(buffer, 0, count); // read from the stream count = stream.Read(buffer, 0, Global.BufferSizeLocal); } // write the final buffer cryptoStream.Write(buffer, 0, count); // flush the encryped stream cryptoStream.Flush(); cipherStream.Position = 0; // assemble encrypted message and add authentication var encryptedStream = new MemoryStream(); // prepend payload encryptedStream.Write(payload, 0, payloadIndex); // prepend IV encryptedStream.Write(iv, 0, iv.Length); // read from the stream count = cipherStream.Read(buffer, 0, Global.BufferSizeLocal); while (count == Global.BufferSizeLocal) { // write to the crypto stream encryptedStream.Write(buffer, 0, count); // read from the stream count = cipherStream.Read(buffer, 0, Global.BufferSizeLocal); } // write the final buffer encryptedStream.Write(buffer, 0, count); BufferCache.Set(buffer); // authenticate all data by postpending a hash of all data encryptedStream.Position = payloadIndex + iv.Length; using (var hmac = new HMACSHA256(authKey)) { // calculate a hash of the entire stream excluding the payload and iv // that can be used to authenticate the received data byte[] tag = hmac.ComputeHash(encryptedStream); // write the tag to the end of the encrypted stream encryptedStream.Write(tag, 0, tag.Length); } // reset the stream position encryptedStream.Position = 0; // return the complete, encrypted stream return(encryptedStream); } } }