/// <summary> /// Tries to read the crypt header from a stream /// </summary> /// <param name="Input">Input stream</param> /// <returns>Crypt header.</returns> /// <remarks>This will leave the stream as-is</remarks> public static CryptHeader GetHeader(Stream Input) { var H = new CryptHeader(); try { H.ReadFrom(Input); } catch { return(new CryptHeader()); } return(H); }
/// <summary> /// Encrypts a stream /// </summary> /// <param name="Input">Source stream</param> /// <param name="Output">Output stream</param> /// <returns>true, if successfull</returns> /// <remarks>Output stream must be seekable</remarks> public CryptResult Encrypt(Stream Input, Stream Output) { if (Key == null || Salt == null) { return(CryptResult.PasswordInvalid); } if (!Input.CanRead) { return(CryptResult.StreamCantRead); } if (!Output.CanWrite) { return(CryptResult.StreamCantWrite); } if (!Output.CanSeek) { return(CryptResult.IOError); } else { using (Rijndael R = Rijndael.Create()) { var Header = new CryptHeader(); Header.Valid = true; Header.Cycles = Difficulty; R.GenerateIV(); Header.IV = R.IV; //Randomly generate a salt for each encryption task. //This makes the password different for each file even if the source file and password are identical. Header.Salt = Salt; //Get Hash for password verification. //This hash allows us to check if a user supplied the correct password for decryption. //This should not be insecure as it still goes through the password generator and thus is very slow. Header.KeyHash = GetPasswordByteHash(Key); //Placeholder for the File hash. When decrypting, this is used to verify integrity. Header.FileHash = new byte[256 / 8]; long HashPos = 0; NotClosingCryptoStream CS; Header.WriteTo(Output); HashPos = Output.Position - Header.FileHash.Length - sizeof(int) /*Header.Cycles*/; try { CS = new NotClosingCryptoStream(Output, R.CreateEncryptor(Key, R.IV), CryptoStreamMode.Write); } catch { return(CryptResult.CryptoStreamError); } using (CS) { using (var Hasher = (SHA256)HashAlgorithm.Create(HASHALG)) { int readed = 0; byte[] Buffer = new byte[R.BlockSize * 10]; do { try { readed = Input.Read(Buffer, 0, Buffer.Length); } catch { return(CryptResult.IOError); } if (readed > 0) { try { CS.Write(Buffer, 0, readed); } catch (IOException) { return(CryptResult.IOError); } catch { return(CryptResult.CryptoStreamError); } if (Input.Position == Input.Length) { var temp = Hasher.TransformFinalBlock(Buffer, 0, readed); } else { Hasher.TransformBlock(Buffer, 0, readed, Buffer, 0); } } } while (readed > 0); Header.FileHash = CreateHMAC(Key, (byte[])Hasher.Hash.Clone()); } try { CS.FlushFinalBlock(); } catch (IOException) { return(CryptResult.IOError); } catch { return(CryptResult.CryptoStreamError); } //Store File hash and seek back to the end try { Output.Flush(); long CurrentPos = Output.Position; Output.Seek(HashPos, SeekOrigin.Begin); Output.Write(Header.FileHash, 0, Header.FileHash.Length); Output.Flush(); Output.Seek(Output.Position, SeekOrigin.Begin); } catch { return(CryptResult.IOError); } } } } return(CryptResult.Success); }