Advertisement
Guest User

Untitled

a guest
Jul 6th, 2014
309
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 57.19 KB | None | 0 0
  1. #region Disclaimer / License
  2. // Copyright (C) 2011, Kenneth Skovhede
  3. // http://www.hexad.dk, opensource@hexad.dk
  4. //
  5. // This library is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU Lesser General Public
  7. // License as published by the Free Software Foundation; either
  8. // version 2.1 of the License, or (at your option) any later version.
  9. //
  10. // This library is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13. // Lesser General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU Lesser General Public
  16. // License along with this library; if not, write to the Free Software
  17. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  18. //
  19. #endregion
  20.  
  21. #region Usage instructions, README
  22. /*************************************************************
  23.  
  24.  This code is an implementation of the AES Crypt tool:
  25.  http://www.aescrypt.com
  26.  
  27.  The code is primarily ported using the file format description,
  28.  using the Java code as an example where there were uncertainties.
  29.  It is tested against the AES Crypt binaries, ensuring that the
  30.  binaries and this code are compatible.
  31.  
  32.  I have NOT tested the version=0 and version=1 formats, they are
  33.  implemented purely by looking at the file format specs.
  34.  If you have test data for these version, please let me know
  35.  if it works.
  36.  
  37.  Usage:
  38.  There are simple static functions that you can call:
  39.     SharpAESCrypt.Encrypt("password", "inputfile", "outputfile");
  40.     SharpAESCrypt.Decrypt("password", "inputfile", "outputfile");
  41.     SharpAESCrypt.Decrypt("password", inputStream, outputStream);
  42.     SharpAESCrypt.Decrypt("password", inputStream, outputStream);
  43.  
  44.  You can control what headers are emitted using the static
  45.  variables:
  46.      SharpAESCrypt.Extension_CreatedByIdentifier
  47.      SharpAESCrypt.Extension_InsertCreateByIdentifier
  48.      SharpAESCrypt.Extension_InsertTimeStamp
  49.      SharpAESCrypt.Extension_InsertPlaceholder
  50.      SharpAESCrypt.DefaultFileVersion
  51.  
  52.  If you need more advanced processing, you can initiate an
  53.  instance and use it as a stream:
  54.     Stream aesStream = new SharpAESCrypt(password, inputStream, mode);
  55.  
  56.  You can then modify the Version and Extensions properties on
  57.  the instance. If you use the stream mode, make sure you call
  58.  either FlushFinalBlock() or Dispose() when you are done.
  59.  
  60.  Have fun!
  61.  
  62.  **************************************************************/
  63. #endregion
  64.  
  65. using System;
  66. using System.Collections.Generic;
  67. using System.Text;
  68. using System.IO;
  69. using System.Security.Cryptography;
  70.  
  71. namespace SharpAESCrypt
  72. {
  73.     /// <summary>
  74.     /// Enumerates the possible modes for encryption and decryption
  75.     /// </summary>
  76.     public enum OperationMode
  77.     {
  78.         /// <summary>
  79.         /// Indicates encryption, which means that the stream must be writeable
  80.         /// </summary>
  81.         Encrypt,
  82.         /// <summary>
  83.         /// Indicates decryption, which means that the stream must be readable
  84.         /// </summary>
  85.         Decrypt
  86.     }
  87.  
  88.     #region Translateable strings
  89.     /// <summary>
  90.     /// Placeholder for translateable strings
  91.     /// </summary>
  92.     public static class Strings
  93.     {
  94.         #region Command line
  95.         /// <summary>
  96.         /// A string displayed when the program is invoked without the correct number of arguments
  97.         /// </summary>
  98.         public static string CommandlineUsage = "SharpAESCrypt e|d password fromPath toPath";
  99.         /// <summary>
  100.         /// A string displayed when an error occurs while running the commandline program
  101.         /// </summary>
  102.         public static string CommandlineError = "Error: {0}";
  103.         /// <summary>
  104.         /// A string displayed if the mode is neither e nor d
  105.         /// </summary>
  106.         public static string CommandlineUnknownMode = "Invalid operation, must be (e)crypt or (d)ecrypt";
  107.         #endregion
  108.  
  109.         #region Exception messages
  110.         /// <summary>
  111.         /// An exception message that indicates that the hash algorithm is not supported
  112.         /// </summary>
  113.         public static string UnsupportedHashAlgorithmReuse = "The hash algortihm does not support reuse";
  114.         /// <summary>
  115.         /// An exception message that indicates that the hash algorithm is not supported
  116.         /// </summary>
  117.         public static string UnsupportedHashAlgorithmBlocks = "The hash algortihm does not support multiple blocks";
  118.         /// <summary>
  119.         /// An exception message that indicates that the hash algorithm is not supported
  120.         /// </summary>
  121.         public static string UnsupportedHashAlgorithmBlocksize = "Unable to digest {0} bytes, as the hash algorithm only returns {1} bytes";
  122.         /// <summary>
  123.         /// An exception message that indicates that an unexpected end of stream was encountered
  124.         /// </summary>
  125.         public static string UnexpectedEndOfStream = "The stream was exhausted unexpectedly";
  126.         /// <summary>
  127.         /// An exception message that indicates that the stream does not support writing
  128.         /// </summary>
  129.         public static string StreamMustBeWriteAble = "When encrypting, the stream must be writeable";
  130.         /// <summary>
  131.         /// An exception messaget that indicates that the stream does not support reading
  132.         /// </summary>
  133.         public static string StreamMustBeReadAble = "When decrypting, the stream must be readable";
  134.         /// <summary>
  135.         /// An exception message that indicates that the mode is not one of the allowed enumerations
  136.         /// </summary>
  137.         public static string InvalidOperationMode = "Invalid mode supplied";
  138.  
  139.         /// <summary>
  140.         /// An exception message that indicates that file is not in the correct format
  141.         /// </summary>
  142.         public static string InvalidFileFormat = "Invalid file format";
  143.         /// <summary>
  144.         /// An exception message that indicates that the header marker is invalid
  145.         /// </summary>
  146.         public static string InvalidHeaderMarker = "Invalid header marker";
  147.         /// <summary>
  148.         /// An exception message that indicates that the reserved field is not set to zero
  149.         /// </summary>
  150.         public static string InvalidReservedFieldValue = "Reserved field is not zero";
  151.         /// <summary>
  152.         /// An exception message that indicates that the detected file version is not supported
  153.         /// </summary>
  154.         public static string UnsupportedFileVersion = "Unsuported file version: {0}";
  155.         /// <summary>
  156.         /// An exception message that indicates that an extension had an invalid format
  157.         /// </summary>
  158.         public static string InvalidExtensionData = "Invalid extension data, separator (0x00) not found";
  159.         /// <summary>
  160.         /// An exception message that indicates that the format was accepted, but the password was not verified
  161.         /// </summary>
  162.         public static string InvalidPassword = "Invalid password or corrupted data";
  163.         /// <summary>
  164.         /// An exception message that indicates that the length of the file is incorrect
  165.         /// </summary>
  166.         public static string InvalidFileLength = "File length is invalid";
  167.  
  168.         /// <summary>
  169.         /// An exception message that indicates that the version is readonly when decrypting
  170.         /// </summary>
  171.         public static string VersionReadonlyForDecryption = "Version is readonly when decrypting";
  172.         /// <summary>
  173.         /// An exception message that indicates that the file version is readonly once encryption has started
  174.         /// </summary>
  175.         public static string VersionReadonly = "Version cannot be changed after encryption has started";
  176.         /// <summary>
  177.         /// An exception message that indicates that the supplied version number is unsupported
  178.         /// </summary>
  179.         public static string VersionUnsupported = "The maximum allowed version is {0}";
  180.         /// <summary>
  181.         /// An exception message that indicates that the stream must support seeking
  182.         /// </summary>
  183.         public static string StreamMustSupportSeeking = "The stream must be seekable writing version 0 files";
  184.  
  185.         /// <summary>
  186.         /// An exception message that indicates that the requsted operation is unsupported
  187.         /// </summary>
  188.         public static string CannotReadWhileEncrypting = "Cannot read while encrypting";
  189.         /// <summary>
  190.         /// An exception message that indicates that the requsted operation is unsupported
  191.         /// </summary>
  192.         public static string CannotWriteWhileDecrypting = "Cannot read while decrypting";
  193.  
  194.         /// <summary>
  195.         /// An exception message that indicates that the data has been altered
  196.         /// </summary>
  197.         public static string DataHMACMismatch = "Message has been altered, do not trust content";
  198.         /// <summary>
  199.         /// An exception message that indicates that the data has been altered or the password is invalid
  200.         /// </summary>
  201.         public static string DataHMACMismatch_v0 = "Invalid password or content has been altered";
  202.  
  203.         /// <summary>
  204.         /// An exception message that indicates that the system is missing a text encoding
  205.         /// </summary>
  206.         public static string EncodingNotSupported = "The required encoding (UTF-16LE) is not supported on this system";
  207.         #endregion
  208.     }
  209.     #endregion
  210.  
  211.     /// <summary>
  212.     /// Provides a stream wrapping an AESCrypt file for either encryption or decryption.
  213.     /// The file format declare support for 2^64 bytes encrypted data, but .Net has trouble
  214.     /// with files more than 2^63 bytes long, so this module 'only' supports 2^63 bytes
  215.     /// (long vs ulong).
  216.     /// </summary>
  217.     public class SharpAESCrypt : Stream
  218.     {
  219.         #region Shared constant values
  220.         /// <summary>
  221.         /// The header in an AESCrypt file
  222.         /// </summary>
  223.         private readonly byte[] MAGIC_HEADER = Encoding.UTF8.GetBytes("AES");
  224.  
  225.         /// <summary>
  226.         /// The maximum supported file version
  227.         /// </summary>
  228.         public const byte MAX_FILE_VERSION = 2;
  229.  
  230.         /// <summary>
  231.         /// The size of the block unit used by the algorithm in bytes
  232.         /// </summary>
  233.         private const int BLOCK_SIZE = 16;
  234.         /// <summary>
  235.         /// The size of the IV, in bytes, which is the same as the blocksize for AES
  236.         /// </summary>
  237.         private const int IV_SIZE = 16;
  238.         /// <summary>
  239.         /// The size of the key. For AES-256 that is 256/8 = 32
  240.         /// </summary>
  241.         private const int KEY_SIZE = 32;
  242.         /// <summary>
  243.         /// The size of the SHA-256 output, which matches the KEY_SIZE
  244.         /// </summary>
  245.         private const int HASH_SIZE = 32;
  246.         #endregion
  247.  
  248.         #region Private instance variables
  249.         /// <summary>
  250.         /// The stream being encrypted or decrypted
  251.         /// </summary>
  252.         private Stream m_stream;
  253.         /// <summary>
  254.         /// The mode of operation
  255.         /// </summary>
  256.         private OperationMode m_mode;
  257.         /// <summary>
  258.         /// The cryptostream used to perform bulk encryption
  259.         /// </summary>
  260.         private CryptoStream m_crypto;
  261.         /// <summary>
  262.         /// The HMAC used for validating data
  263.         /// </summary>
  264.         private HMAC m_hmac;
  265.         /// <summary>
  266.         /// The length of the data modulus <see cref="BLOCK_SIZE"/>
  267.         /// </summary>
  268.         private int m_length;
  269.         /// <summary>
  270.         /// The setup helper instance
  271.         /// </summary>
  272.         private SetupHelper m_helper;
  273.         /// <summary>
  274.         /// The list of extensions read from or written to the stream
  275.         /// </summary>
  276.         private List<KeyValuePair<string, byte[]>> m_extensions;
  277.         /// <summary>
  278.         /// The file format version
  279.         /// </summary>
  280.         private byte m_version = MAX_FILE_VERSION;
  281.         /// <summary>
  282.         /// True if the header is written, false otherwise. Used only for encryption.
  283.         /// </summary>
  284.         private bool m_hasWrittenHeader = false;
  285.         /// <summary>
  286.         /// True if the footer has been written, false otherwise. Used only for encryption.
  287.         /// </summary>
  288.         private bool m_hasFlushedFinalBlock = false;
  289.         /// <summary>
  290.         /// The size of the payload, including padding. Used only for decryption.
  291.         /// </summary>
  292.         private long m_payloadLength;
  293.         /// <summary>
  294.         /// The number of bytes read from the encrypted stream. Used only for decryption.
  295.         /// </summary>
  296.         private long m_readcount;
  297.         /// <summary>
  298.         /// The number of padding bytes. Used only for decryption.
  299.         /// </summary>
  300.         private byte m_paddingSize;
  301.         /// <summary>
  302.         /// True if the header HMAC has been read and verified, false otherwise. Used only for decryption.
  303.         /// </summary>
  304.         private bool m_hasReadFooter = false;
  305.         #endregion
  306.  
  307.         #region Private helper functions and properties
  308.         /// <summary>
  309.         /// Helper property to ensure that the crypto stream is initialized before being used
  310.         /// </summary>
  311.         private CryptoStream Crypto
  312.         {
  313.             get
  314.             {
  315.                 if (m_crypto == null)
  316.                     WriteEncryptionHeader();
  317.                 return m_crypto;
  318.             }
  319.         }
  320.  
  321.         /// <summary>
  322.         /// Helper function to read and validate the header
  323.         /// </summary>
  324.         private void ReadEncryptionHeader(string password)
  325.         {
  326.             byte[] tmp = new byte[MAGIC_HEADER.Length + 2];
  327.             if (m_stream.Read(tmp, 0, tmp.Length) != tmp.Length)
  328.                 throw new InvalidDataException(Strings.InvalidHeaderMarker);
  329.  
  330.             for (int i = 0; i < MAGIC_HEADER.Length; i++)
  331.                 if (MAGIC_HEADER[i] != tmp[i])
  332.                     throw new InvalidDataException(Strings.InvalidHeaderMarker);
  333.  
  334.             m_version = tmp[MAGIC_HEADER.Length];
  335.             if (m_version > MAX_FILE_VERSION)
  336.                 throw new InvalidDataException(string.Format(Strings.UnsupportedFileVersion, m_version));
  337.  
  338.             if (m_version == 0)
  339.             {
  340.                 m_paddingSize = tmp[MAGIC_HEADER.Length + 1];
  341.                 if (m_paddingSize >= BLOCK_SIZE)
  342.                     throw new InvalidDataException(Strings.InvalidHeaderMarker);
  343.             }
  344.             else if (tmp[MAGIC_HEADER.Length + 1] != 0)
  345.                 throw new InvalidDataException(Strings.InvalidReservedFieldValue);
  346.  
  347.             //Extensions are only supported in v2+
  348.             if (m_version >= 2)
  349.             {
  350.                 int extensionLength = 0;
  351.                 do
  352.                 {
  353.                     byte[] tmpLength = RepeatRead(m_stream, 2);
  354.                     extensionLength = (((int)tmpLength[0]) << 8) | (tmpLength[1]);
  355.  
  356.                     if (extensionLength != 0)
  357.                     {
  358.                         byte[] data = RepeatRead(m_stream, extensionLength);
  359.                         int separatorIndex = Array.IndexOf<byte>(data, 0);
  360.                         if (separatorIndex < 0)
  361.                             throw new InvalidDataException(Strings.InvalidExtensionData);
  362.  
  363.                         string key = System.Text.Encoding.UTF8.GetString(data, 0, separatorIndex);
  364.                         byte[] value = new byte[data.Length - separatorIndex - 1];
  365.                         Array.Copy(data, separatorIndex + 1, value, 0, value.Length);
  366.  
  367.                         m_extensions.Add(new KeyValuePair<string, byte[]>(key, value));
  368.                     }
  369.  
  370.                 } while (extensionLength > 0);
  371.             }
  372.  
  373.             byte[] iv1 = RepeatRead(m_stream, IV_SIZE);
  374.             m_helper = new SetupHelper(m_mode, password, iv1);
  375.  
  376.             if (m_version >= 1)
  377.             {
  378.                 byte[] hmac1 = m_helper.DecryptAESKey2(RepeatRead(m_stream, IV_SIZE + KEY_SIZE));
  379.                 byte[] hmac2 = RepeatRead(m_stream, hmac1.Length);
  380.                 for (int i = 0; i < hmac1.Length; i++)
  381.                     if (hmac1[i] != hmac2[i])
  382.                         throw new CryptographicException(Strings.InvalidPassword);
  383.  
  384.                 m_payloadLength = m_stream.Length - m_stream.Position - (HASH_SIZE + 1);
  385.             }
  386.             else
  387.             {
  388.                 m_helper.SetBulkKeyToKey1();
  389.  
  390.                 m_payloadLength = m_stream.Length - m_stream.Position - HASH_SIZE;
  391.             }
  392.  
  393.             if (m_payloadLength % BLOCK_SIZE != 0)
  394.                 throw new CryptographicException(Strings.InvalidFileLength);
  395.         }
  396.  
  397.         /// <summary>
  398.         /// Writes the header to the output stream and sets up the crypto stream
  399.         /// </summary>
  400.         private void WriteEncryptionHeader()
  401.         {
  402.             m_stream.Write(MAGIC_HEADER, 0, MAGIC_HEADER.Length);
  403.             m_stream.WriteByte(m_version);
  404.             m_stream.WriteByte(0); //Reserved or length % 16
  405.             if (m_version >= 2)
  406.             {
  407.                 foreach (KeyValuePair<string, byte[]> ext in m_extensions)
  408.                     WriteExtension(ext.Key, ext.Value);
  409.                 m_stream.Write(new byte[] { 0, 0 }, 0, 2); //No more extensions
  410.             }
  411.  
  412.             m_stream.Write(m_helper.IV1, 0, m_helper.IV1.Length);
  413.  
  414.             if (m_version == 0)
  415.                 m_helper.SetBulkKeyToKey1();
  416.             else
  417.             {
  418.                 //Generate and encrypt bulk key and its HMAC
  419.                 byte[] tmpKey = m_helper.EncryptAESKey2();
  420.                 m_stream.Write(tmpKey, 0, tmpKey.Length);
  421.                 tmpKey = m_helper.CalculateKeyHmac();
  422.                 m_stream.Write(tmpKey, 0, tmpKey.Length);
  423.             }
  424.  
  425.             m_hmac = m_helper.GetHMAC();
  426.  
  427.             //Insert the HMAC before the stream to calculate the HMAC for the ciphertext
  428.             m_crypto = new CryptoStream(new CryptoStream(new StreamHider(m_stream, 0), m_hmac, CryptoStreamMode.Write), m_helper.CreateCryptoStream(m_mode), CryptoStreamMode.Write);
  429.             m_hasWrittenHeader = true;
  430.         }
  431.  
  432.         /// <summary>
  433.         /// Writes an extension to the output stream, see:
  434.         /// http://www.aescrypt.com/aes_file_format.html
  435.         /// </summary>
  436.         /// <param name="identifier">The extension identifier</param>
  437.         /// <param name="value">The data to set in the extension</param>
  438.         private void WriteExtension(string identifier, byte[] value)
  439.         {
  440.             byte[] name = System.Text.Encoding.UTF8.GetBytes(identifier);
  441.             if (value == null)
  442.                 value = new byte[0];
  443.  
  444.             uint size = (uint)(name.Length + 1 + value.Length);
  445.             m_stream.WriteByte((byte)((size >> 8) & 0xff));
  446.             m_stream.WriteByte((byte)(size & 0xff));
  447.             m_stream.Write(name, 0, name.Length);
  448.             m_stream.WriteByte(0);
  449.             m_stream.Write(value, 0, value.Length);
  450.         }
  451.  
  452.         #endregion
  453.  
  454.         #region Private utility classes and functions
  455.         /// <summary>
  456.         /// Internal helper class used to encapsulate the setup process
  457.         /// </summary>
  458.         private class SetupHelper : IDisposable
  459.         {
  460.             /// <summary>
  461.             /// The MAC adress to use in case the network interface enumeration fails
  462.             /// </summary>
  463.             private static readonly byte[] DEFAULT_MAC = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
  464.  
  465.             /// <summary>
  466.             /// The hashing algorithm used to digest data
  467.             /// </summary>
  468.             private const string HASH_ALGORITHM = "SHA-256";
  469.  
  470.             /// <summary>
  471.             /// The algorithm used to encrypt and decrypt data
  472.             /// </summary>
  473.             private const string CRYPT_ALGORITHM = "Rijndael";
  474.  
  475.             /// <summary>
  476.             /// The algorithm used to generate random data
  477.             /// </summary>
  478.             private const string RAND_ALGORITHM = "SHA1PRNG";
  479.  
  480.             /// <summary>
  481.             /// The algorithm used to calculate the HMAC
  482.             /// </summary>
  483.             private const string HMAC_ALGORITHM = "HmacSHA256";
  484.  
  485.             /// <summary>
  486.             /// The encoding scheme for the password.
  487.             /// UTF-16 should mean UTF-16LE, but Mono rejects the full name.
  488.             /// A check is made when using the encoding, that it is indeed UTF-16LE.
  489.             /// </summary>
  490.             private const string PASSWORD_ENCODING = "utf-16";
  491.  
  492.             /// <summary>
  493.             /// The encryption instance
  494.             /// </summary>
  495.             private SymmetricAlgorithm m_crypt;
  496.             /// <summary>
  497.             /// The hash instance
  498.             /// </summary>
  499.             private HashAlgorithm m_hash;
  500.             /// <summary>
  501.             /// The random number generator instance
  502.             /// </summary>
  503.             private RandomNumberGenerator m_rand;
  504.             /// <summary>
  505.             /// The HMAC algorithm
  506.             /// </summary>
  507.             private HMAC m_hmac;
  508.  
  509.             /// <summary>
  510.             /// The IV used to encrypt/decrypt the bulk key
  511.             /// </summary>
  512.             private byte[] m_iv1;
  513.             /// <summary>
  514.             /// The private key used to encrypt/decrypt the bulk key
  515.             /// </summary>
  516.             private byte[] m_aesKey1;
  517.             /// <summary>
  518.             /// The IV used to encrypt/decrypt bulk data
  519.             /// </summary>
  520.             private byte[] m_iv2;
  521.             /// <summary>
  522.             /// The key used to encrypt/decrypt bulk data
  523.             /// </summary>
  524.             private byte[] m_aesKey2;
  525.  
  526.             /// <summary>
  527.             /// Initialize the setup
  528.             /// </summary>
  529.             /// <param name="mode">The mode to prepare for</param>
  530.             /// <param name="password">The password used to encrypt or decrypt</param>
  531.             /// <param name="iv">The IV used, set to null if encrypting</param>
  532.             public SetupHelper(OperationMode mode, string password, byte[] iv)
  533.             {
  534.                 m_crypt = SymmetricAlgorithm.Create(CRYPT_ALGORITHM);
  535.  
  536.                 //Not sure how to insert this with the CRYPT_ALGORITHM string
  537.                 m_crypt.Padding = PaddingMode.None;
  538.                 m_crypt.Mode = CipherMode.CBC;
  539.  
  540.                 m_hash = HashAlgorithm.Create(HASH_ALGORITHM);
  541.                 m_rand = RandomNumberGenerator.Create(/*RAND_ALGORITHM*/);
  542.                 m_hmac = HMAC.Create(HMAC_ALGORITHM);
  543.  
  544.                 if (mode == OperationMode.Encrypt)
  545.                 {
  546.                     m_iv1 = GenerateIv1();
  547.                     m_aesKey1 = GenerateAESKey1(EncodePassword(password));
  548.                     m_iv2 = GenerateIv2();
  549.                     m_aesKey2 = GenerateAESKey2();
  550.                 }
  551.                 else
  552.                 {
  553.                     m_iv1 = iv;
  554.                     m_aesKey1 = GenerateAESKey1(EncodePassword(password));
  555.                 }
  556.             }
  557.  
  558.             /// <summary>
  559.             /// Encodes the password in UTF-16LE,
  560.             /// used to fix missing support for the full encoding
  561.             /// name under Mono. Verifies that the encoding is correct.
  562.             /// </summary>
  563.             /// <param name="password">The password to encode as a byte array</param>
  564.             /// <returns>The password encoded as a byte array</returns>
  565.             private byte[] EncodePassword(string password)
  566.             {
  567.                 Encoding e = Encoding.GetEncoding(PASSWORD_ENCODING);
  568.  
  569.                 byte[] preamb = e == null ? null : e.GetPreamble();
  570.                 if (preamb == null || preamb.Length != 2)
  571.                     throw new SystemException(Strings.EncodingNotSupported);
  572.  
  573.                 if (preamb[0] == 0xff && preamb[1] == 0xfe)
  574.                     return e.GetBytes(password);
  575.                 else if (preamb[0] == 0xfe && preamb[1] == 0xff)
  576.                 {
  577.                     //We have a Big Endian, convert to Little endian
  578.                     byte[] tmp = e.GetBytes(password);
  579.                     if (tmp.Length % 2 != 0)
  580.                         throw new SystemException(Strings.EncodingNotSupported);
  581.  
  582.                     for (int i = 0; i < tmp.Length; i += 2)
  583.                     {
  584.                         byte x = tmp[i];
  585.                         tmp[i] = tmp[i + 1];
  586.                         tmp[i + 1] = x;
  587.                     }
  588.  
  589.                     return tmp;
  590.                 }
  591.                 else
  592.                     throw new SystemException(Strings.EncodingNotSupported);
  593.             }
  594.  
  595.             /// <summary>
  596.             /// Gets the IV used to encrypt the bulk data key
  597.             /// </summary>
  598.             public byte[] IV1
  599.             {
  600.                 get { return m_iv1; }
  601.             }
  602.  
  603.  
  604.             /// <summary>
  605.             /// Creates the iv used for encrypting the actual key and IV.
  606.             /// This IV is calculated using the network MAC adress as input.
  607.             /// </summary>
  608.             /// <returns>An IV</returns>
  609.             private byte[] GenerateIv1()
  610.             {
  611.                 byte[] iv = new byte[IV_SIZE];
  612.                 long time = DateTime.Now.Ticks;
  613.                 byte[] mac = null;
  614.  
  615.                 try
  616.                 {
  617.                     System.Net.NetworkInformation.NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
  618.                     for (int i = 0; i < interfaces.Length; i++)
  619.                         if (i != System.Net.NetworkInformation.NetworkInterface.LoopbackInterfaceIndex)
  620.                         {
  621.                             mac = interfaces[i].GetPhysicalAddress().GetAddressBytes();
  622.                             break;
  623.                         }
  624.                 }
  625.                 catch
  626.                 {
  627.                     //Not much to do, just go with default MAC
  628.                 }
  629.  
  630.                 if (mac == null)
  631.                     mac = DEFAULT_MAC;
  632.  
  633.                 for (int i = 0; i < 8; i++)
  634.                     iv[i] = (byte)((time >> (i * 8)) & 0xff);
  635.  
  636.                 Array.Copy(mac, 0, iv, 8, Math.Min(mac.Length, iv.Length - 8));
  637.                 return DigestRandomBytes(iv, 256);
  638.             }
  639.  
  640.             /// <summary>
  641.             /// Generates a key based on the IV and the password.
  642.             /// This key is used to encrypt the actual key and IV.
  643.             /// </summary>
  644.             /// <param name="password">The password supplied</param>
  645.             /// <returns>The key generated</returns>
  646.             private byte[] GenerateAESKey1(byte[] password)
  647.             {
  648.                 if (!m_hash.CanReuseTransform)
  649.                     throw new CryptographicException(Strings.UnsupportedHashAlgorithmReuse);
  650.                 if (!m_hash.CanTransformMultipleBlocks)
  651.                     throw new CryptographicException(Strings.UnsupportedHashAlgorithmBlocks);
  652.  
  653.                 if (KEY_SIZE < m_hash.HashSize / 8)
  654.                     throw new CryptographicException(string.Format(Strings.UnsupportedHashAlgorithmBlocksize, KEY_SIZE, m_hash.HashSize / 8));
  655.  
  656.                 byte[] key = new byte[KEY_SIZE];
  657.                 Array.Copy(m_iv1, key, m_iv1.Length);
  658.  
  659.                 for (int i = 0; i < 8192; i++)
  660.                 {
  661.                     m_hash.Initialize();
  662.                     m_hash.TransformBlock(key, 0, key.Length, key, 0);
  663.                     m_hash.TransformFinalBlock(password, 0, password.Length);
  664.                     key = m_hash.Hash;
  665.                 }
  666.  
  667.                 return key;
  668.             }
  669.  
  670.             /// <summary>
  671.             /// Generates a random IV for encrypting data
  672.             /// </summary>
  673.             /// <returns>A random IV</returns>
  674.             private byte[] GenerateIv2()
  675.             {
  676.                 m_crypt.GenerateIV();
  677.                 return DigestRandomBytes(m_crypt.IV, 256);
  678.             }
  679.  
  680.             /// <summary>
  681.             /// Generates a random key for encrypting data
  682.             /// </summary>
  683.             /// <returns></returns>
  684.             private byte[] GenerateAESKey2()
  685.             {
  686.                 m_crypt.GenerateKey();
  687.                 return DigestRandomBytes(m_crypt.Key, 32);
  688.             }
  689.  
  690.             /// <summary>
  691.             /// Encrypts the key and IV used to encrypt data with the initial key and IV.
  692.             /// </summary>
  693.             /// <returns>The encrypted AES Key (including IV)</returns>
  694.             public byte[] EncryptAESKey2()
  695.             {
  696.                 using (MemoryStream ms = new MemoryStream())
  697.                 using (CryptoStream cs = new CryptoStream(ms, m_crypt.CreateEncryptor(m_aesKey1, m_iv1), CryptoStreamMode.Write))
  698.                 {
  699.                     cs.Write(m_iv2, 0, m_iv2.Length);
  700.                     cs.Write(m_aesKey2, 0, m_aesKey2.Length);
  701.                     cs.FlushFinalBlock();
  702.  
  703.                     return ms.ToArray();
  704.                 }
  705.             }
  706.  
  707.             /// <summary>
  708.             /// Calculates the HMAC for the encrypted key
  709.             /// </summary>
  710.             /// <param name="data">The encrypted data to calculate the HMAC from</param>
  711.             /// <returns>The HMAC value</returns>
  712.             public byte[] CalculateKeyHmac()
  713.             {
  714.                 m_hmac.Initialize();
  715.                 m_hmac.Key = m_aesKey1;
  716.                 return m_hmac.ComputeHash(EncryptAESKey2());
  717.             }
  718.  
  719.             /// <summary>
  720.             /// Performs repeated hashing of the data in the byte[] combined with random data.
  721.             /// The update is performed on the input data, which is also returned.
  722.             /// </summary>
  723.             /// <param name="bytes">The bytes to start the digest operation with</param>
  724.             /// <param name="repetitions">The number of repetitions to perform</param>
  725.             /// <param name="hash">The hashing algorithm instance</param>
  726.             /// <returns>The digested input data, which is the same array as passed in</returns>
  727.             private byte[] DigestRandomBytes(byte[] bytes, int repetitions)
  728.             {
  729.                 if (bytes.Length > (m_hash.HashSize / 8))
  730.                     throw new CryptographicException(string.Format(Strings.UnsupportedHashAlgorithmBlocksize, bytes.Length, m_hash.HashSize / 8));
  731.  
  732.                 if (!m_hash.CanReuseTransform)
  733.                     throw new CryptographicException(Strings.UnsupportedHashAlgorithmReuse);
  734.                 if (!m_hash.CanTransformMultipleBlocks)
  735.                     throw new CryptographicException(Strings.UnsupportedHashAlgorithmBlocks);
  736.  
  737.                 m_hash.Initialize();
  738.                 m_hash.TransformBlock(bytes, 0, bytes.Length, bytes, 0);
  739.                 for (int i = 0; i < repetitions; i++)
  740.                 {
  741.                     m_rand.GetBytes(bytes);
  742.                     m_hash.TransformBlock(bytes, 0, bytes.Length, bytes, 0);
  743.                 }
  744.  
  745.                 m_hash.TransformFinalBlock(bytes, 0, 0);
  746.                 Array.Copy(m_hash.Hash, bytes, bytes.Length);
  747.                 return bytes;
  748.             }
  749.  
  750.             /// <summary>
  751.             /// Generates the CryptoTransform element used to encrypt/decrypt the bulk data
  752.             /// </summary>
  753.             /// <param name="mode">The operation mode</param>
  754.             /// <returns>An ICryptoTransform instance</returns>
  755.             public ICryptoTransform CreateCryptoStream(OperationMode mode)
  756.             {
  757.                 if (mode == OperationMode.Encrypt)
  758.                     return m_crypt.CreateEncryptor(m_aesKey2, m_iv2);
  759.                 else
  760.                     return m_crypt.CreateDecryptor(m_aesKey2, m_iv2);
  761.             }
  762.  
  763.             /// <summary>
  764.             /// Creates a fresh HMAC calculation algorithm
  765.             /// </summary>
  766.             /// <returns>An HMAC algortihm using AES Key 2</returns>
  767.             public HMAC GetHMAC()
  768.             {
  769.                 HMAC h = HMAC.Create(HMAC_ALGORITHM);
  770.                 h.Key = m_aesKey2;
  771.                 return h;
  772.             }
  773.  
  774.             /// <summary>
  775.             /// Decrypts the bulk key and IV
  776.             /// </summary>
  777.             /// <param name="data">The encrypted IV followed by the key</param>
  778.             /// <returns>The HMAC value for the key</returns>
  779.             public byte[] DecryptAESKey2(byte[] data)
  780.             {
  781.                 using (MemoryStream ms = new MemoryStream(data))
  782.                 using (CryptoStream cs = new CryptoStream(ms, m_crypt.CreateDecryptor(m_aesKey1, m_iv1), CryptoStreamMode.Read))
  783.                 {
  784.                     m_iv2 = RepeatRead(cs, IV_SIZE);
  785.                     m_aesKey2 = RepeatRead(cs, KEY_SIZE);
  786.                 }
  787.  
  788.                 m_hmac.Initialize();
  789.                 m_hmac.Key = m_aesKey1;
  790.                 m_hmac.TransformFinalBlock(data, 0, data.Length);
  791.                 return m_hmac.Hash;
  792.             }
  793.  
  794.             /// <summary>
  795.             /// Sets iv2 and aesKey2 to iv1 and aesKey1 respectively.
  796.             /// Used only for files with version = 0
  797.             /// </summary>
  798.             public void SetBulkKeyToKey1()
  799.             {
  800.                 m_iv2 = m_iv1;
  801.                 m_aesKey2 = m_aesKey1;
  802.             }
  803.  
  804.             #region IDisposable Members
  805.  
  806.             /// <summary>
  807.             /// Disposes all members
  808.             /// </summary>
  809.             public void Dispose()
  810.             {
  811.                 if (m_crypt != null)
  812.                 {
  813.                     if (m_aesKey1 != null)
  814.                         Array.Clear(m_aesKey1, 0, m_aesKey1.Length);
  815.                     if (m_iv1 != null)
  816.                         Array.Clear(m_iv1, 0, m_iv1.Length);
  817.                     if (m_aesKey2 != null)
  818.                         Array.Clear(m_aesKey2, 0, m_aesKey2.Length);
  819.                     if (m_iv2 != null)
  820.                         Array.Clear(m_iv2, 0, m_iv2.Length);
  821.  
  822.                     m_aesKey1 = null;
  823.                     m_iv1 = null;
  824.                     m_aesKey2 = null;
  825.                     m_iv2 = null;
  826.  
  827.                     m_hash = null;
  828.                     m_hmac = null;
  829.                     m_rand = null;
  830.                     m_crypt = null;
  831.                 }
  832.             }
  833.  
  834.             #endregion
  835.         }
  836.  
  837.         /// <summary>
  838.         /// Internal helper class, used to hide the trailing bytes from the cryptostream
  839.         /// </summary>
  840.         private class StreamHider : Stream
  841.         {
  842.             /// <summary>
  843.             /// The wrapped stream
  844.             /// </summary>
  845.             private Stream m_stream;
  846.  
  847.             /// <summary>
  848.             /// The number of bytes to hide
  849.             /// </summary>
  850.             private int m_hiddenByteCount;
  851.  
  852.             /// <summary>
  853.             /// Constructs the stream wrapper to hide the desired bytes
  854.             /// </summary>
  855.             /// <param name="stream">The stream to wrap</param>
  856.             /// <param name="count">The number of bytes to hide</param>
  857.             public StreamHider(Stream stream, int count)
  858.             {
  859.                 m_stream = stream;
  860.                 m_hiddenByteCount = count;
  861.             }
  862.  
  863.             #region Basic Stream implementation stuff
  864.             public override bool CanRead { get { return m_stream.CanRead; } }
  865.             public override bool CanSeek { get { return m_stream.CanSeek; } }
  866.             public override bool CanWrite { get { return m_stream.CanWrite; } }
  867.             public override void Flush() { m_stream.Flush(); }
  868.             public override long Length { get { return m_stream.Length; } }
  869.             public override long Seek(long offset, SeekOrigin origin) { return m_stream.Seek(offset, origin); }
  870.             public override void SetLength(long value) { m_stream.SetLength(value); }
  871.             public override long Position { get { return m_stream.Position; } set { m_stream.Position = value; } }
  872.             public override void Write(byte[] buffer, int offset, int count) { m_stream.Write(buffer, offset, count); }
  873.             #endregion
  874.  
  875.             /// <summary>
  876.             /// The overridden read function that ensures that the caller cannot see the hidden bytes
  877.             /// </summary>
  878.             /// <param name="buffer">The buffer to read into</param>
  879.             /// <param name="offset">The offset into the buffer</param>
  880.             /// <param name="count">The number of bytes to read</param>
  881.             /// <returns>The number of bytes read</returns>
  882.             public override int Read(byte[] buffer, int offset, int count)
  883.             {
  884.                 long allowedCount = Math.Max(0, Math.Min(count, m_stream.Length - (m_stream.Position + m_hiddenByteCount)));
  885.                 if (allowedCount == 0)
  886.                     return 0;
  887.                 else
  888.                     return m_stream.Read(buffer, offset, (int)allowedCount);
  889.             }
  890.         }
  891.  
  892.         /// <summary>
  893.         /// Helper function to support reading from streams that chunck data.
  894.         /// Will keep reading a stream until <paramref name="count"/> bytes have been read.
  895.         /// Throws an exception if the stream is exhausted before <paramref name="count"/> bytes are read.
  896.         /// </summary>
  897.         /// <param name="stream">The stream to read from</param>
  898.         /// <param name="count">The number of bytes to read</param>
  899.         /// <returns>The data read</returns>
  900.         internal static byte[] RepeatRead(Stream stream, int count)
  901.         {
  902.             byte[] tmp = new byte[count];
  903.             while (count > 0)
  904.             {
  905.                 int r = stream.Read(tmp, tmp.Length - count, count);
  906.                 count -= r;
  907.                 if (r == 0 && count != 0)
  908.                     throw new InvalidDataException(Strings.UnexpectedEndOfStream);
  909.             }
  910.  
  911.             return tmp;
  912.         }
  913.  
  914.         #endregion
  915.  
  916.         #region Public static API
  917.  
  918.         #region Default extension control variables
  919.         /// <summary>
  920.         /// The name inserted as the creator software in the extensions when creating output
  921.         /// </summary>
  922.         public static string Extension_CreatedByIdentifier = string.Format("SharpAESCrypt v{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
  923.  
  924.         /// <summary>
  925.         /// A value indicating if the extension data should contain the creator software
  926.         /// </summary>
  927.         public static bool Extension_InsertCreateByIdentifier = true;
  928.  
  929.         /// <summary>
  930.         /// A value indicating if the extensions data should contain timestamp data
  931.         /// </summary>
  932.         public static bool Extension_InsertTimeStamp = false;
  933.  
  934.         /// <summary>
  935.         /// A value indicating if the extensions data should contain an empty block as suggested by the file format
  936.         /// </summary>
  937.         public static bool Extension_InsertPlaceholder = true;
  938.         #endregion
  939.  
  940.         /// <summary>
  941.         /// The file version to use when creating a new file
  942.         /// </summary>
  943.         public static byte DefaultFileVersion = MAX_FILE_VERSION;
  944.  
  945.         /// <summary>
  946.         /// Encrypts a stream using the supplied password
  947.         /// </summary>
  948.         /// <param name="password">The password to decrypt with</param>
  949.         /// <param name="input">The stream with unencrypted data</param>
  950.         /// <param name="output">The encrypted output stream</param>
  951.         public static void Encrypt(string password, Stream input, Stream output)
  952.         {
  953.             int a;
  954.             byte[] buffer = new byte[1024 * 4];
  955.             SharpAESCrypt c = new SharpAESCrypt(password, output, OperationMode.Encrypt);
  956.             while ((a = input.Read(buffer, 0, buffer.Length)) != 0)
  957.                 c.Write(buffer, 0, a);
  958.             c.FlushFinalBlock();
  959.         }
  960.  
  961.         /// <summary>
  962.         /// Decrypts a stream using the supplied password
  963.         /// </summary>
  964.         /// <param name="password">The password to encrypt with</param>
  965.         /// <param name="input">The stream with encrypted data</param>
  966.         /// <param name="output">The unencrypted output stream</param>
  967.         public static void Decrypt(string password, Stream input, Stream output)
  968.         {
  969.             int a;
  970.             byte[] buffer = new byte[1024 * 4];
  971.             SharpAESCrypt c = new SharpAESCrypt(password, input, OperationMode.Decrypt);
  972.             while ((a = c.Read(buffer, 0, buffer.Length)) != 0)
  973.                 output.Write(buffer, 0, a);
  974.         }
  975.  
  976.         /// <summary>
  977.         /// Encrypts a file using the supplied password
  978.         /// </summary>
  979.         /// <param name="password">The password to encrypt with</param>
  980.         /// <param name="input">The file with unencrypted data</param>
  981.         /// <param name="output">The encrypted output file</param>
  982.         public static void Encrypt(string password, string inputfile, string outputfile)
  983.         {
  984.             using (FileStream infs = File.OpenRead(inputfile))
  985.             using (FileStream outfs = File.Create(outputfile))
  986.                 Encrypt(password, infs, outfs);
  987.         }
  988.  
  989.         /// <summary>
  990.         /// Decrypts a file using the supplied password
  991.         /// </summary>
  992.         /// <param name="password">The password to decrypt with</param>
  993.         /// <param name="input">The file with encrypted data</param>
  994.         /// <param name="output">The unencrypted output file</param>
  995.         public static void Decrypt(string password, string inputfile, string outputfile)
  996.         {
  997.             using (FileStream infs = File.OpenRead(inputfile))
  998.             using (FileStream outfs = File.Create(outputfile))
  999.                 Decrypt(password, infs, outfs);
  1000.         }
  1001.         #endregion
  1002.  
  1003.         #region Public instance API
  1004.         /// <summary>
  1005.         /// Constructs a new AESCrypt instance, operating on the supplied stream
  1006.         /// </summary>
  1007.         /// <param name="password">The password used for encryption or decryption</param>
  1008.         /// <param name="stream">The stream to operate on, must be writeable for encryption, and readable for decryption</param>
  1009.         /// <param name="mode">The mode of operation, either OperationMode.Encrypt or OperationMode.Decrypt</param>
  1010.         public SharpAESCrypt(string password, Stream stream, OperationMode mode)
  1011.         {
  1012.             //Basic input checks
  1013.             if (stream == null)
  1014.                 throw new ArgumentNullException("stream");
  1015.             if (password == null)
  1016.                 throw new ArgumentNullException("password");
  1017.             if (mode != OperationMode.Encrypt && mode != OperationMode.Decrypt)
  1018.                 throw new ArgumentException(Strings.InvalidOperationMode, "mode");
  1019.             if (mode == OperationMode.Encrypt && !stream.CanWrite)
  1020.                 throw new ArgumentException(Strings.StreamMustBeWriteAble, "stream");
  1021.             if (mode == OperationMode.Decrypt && !stream.CanRead)
  1022.                 throw new ArgumentException(Strings.StreamMustBeReadAble, "stream");
  1023.  
  1024.             m_mode = mode;
  1025.             m_stream = stream;
  1026.             m_extensions = new List<KeyValuePair<string, byte[]>>();
  1027.  
  1028.             if (mode == OperationMode.Encrypt)
  1029.             {
  1030.                 this.Version = DefaultFileVersion;
  1031.  
  1032.                 m_helper = new SetupHelper(mode, password, null);
  1033.  
  1034.                 //Setup default extensions
  1035.                 if (Extension_InsertCreateByIdentifier)
  1036.                     m_extensions.Add(new KeyValuePair<string, byte[]>("CREATED-BY", System.Text.Encoding.UTF8.GetBytes(Extension_CreatedByIdentifier)));
  1037.  
  1038.                 if (Extension_InsertTimeStamp)
  1039.                 {
  1040.                     m_extensions.Add(new KeyValuePair<string, byte[]>("CREATED-DATE", System.Text.Encoding.UTF8.GetBytes(DateTime.Now.ToString("yyyy-MM-dd"))));
  1041.                     m_extensions.Add(new KeyValuePair<string, byte[]>("CREATED-TIME", System.Text.Encoding.UTF8.GetBytes(DateTime.Now.ToUniversalTime().ToString("hh-mm-ss"))));
  1042.                 }
  1043.  
  1044.                 if (Extension_InsertPlaceholder)
  1045.                     m_extensions.Add(new KeyValuePair<string, byte[]>("", new byte[127])); //Suggested extension space
  1046.  
  1047.                 //We defer creation of the cryptostream until it is needed,
  1048.                 // so the caller can change version, extensions, etc.
  1049.                 // before we write the header
  1050.                 m_crypto = null;
  1051.             }
  1052.             else
  1053.             {
  1054.                 //Read and validate
  1055.                 ReadEncryptionHeader(password);
  1056.  
  1057.                 m_hmac = m_helper.GetHMAC();
  1058.  
  1059.                 //Insert the HMAC before the decryption so the HMAC is calculated for the ciphertext
  1060.                 m_crypto = new CryptoStream(new CryptoStream(new StreamHider(m_stream, m_version == 0 ? HASH_SIZE : (HASH_SIZE + 1)), m_hmac, CryptoStreamMode.Read), m_helper.CreateCryptoStream(m_mode), CryptoStreamMode.Read);
  1061.             }
  1062.         }
  1063.  
  1064.         /// <summary>
  1065.         /// Gets or sets the version number.
  1066.         /// Note that this can only be set when encrypting,
  1067.         /// and must be done before encryption has started.
  1068.         /// See <value>MAX_FILE_VERSION</value> for the maximum supported version.
  1069.         /// Note that version 0 requires a seekable stream.
  1070.         /// </summary>
  1071.         public byte Version
  1072.         {
  1073.             get { return m_version; }
  1074.             set
  1075.             {
  1076.                 if (m_mode == OperationMode.Decrypt)
  1077.                     throw new InvalidOperationException(Strings.VersionReadonlyForDecryption);
  1078.                 if (m_mode == OperationMode.Encrypt && m_crypto != null)
  1079.                     throw new InvalidOperationException(Strings.VersionReadonly);
  1080.                 if (value > MAX_FILE_VERSION)
  1081.                     throw new ArgumentOutOfRangeException(string.Format(Strings.VersionUnsupported, MAX_FILE_VERSION));
  1082.                 if (value == 0 && !m_stream.CanSeek)
  1083.                     throw new InvalidOperationException(Strings.StreamMustSupportSeeking);
  1084.  
  1085.                 m_version = value;
  1086.             }
  1087.         }
  1088.  
  1089.         /// <summary>
  1090.         /// Provides access to the extensions found in the file.
  1091.         /// This collection cannot be updated when decrypting,
  1092.         /// nor after the encryption has started.
  1093.         /// </summary>
  1094.         public IList<KeyValuePair<string, byte[]>> Extensions
  1095.         {
  1096.             get
  1097.             {
  1098.                 if (m_mode == OperationMode.Decrypt || (m_mode == OperationMode.Encrypt && m_crypto != null))
  1099.                     return m_extensions.AsReadOnly();
  1100.                 else
  1101.                     return m_extensions;
  1102.             }
  1103.         }
  1104.  
  1105.         #region Basic stream implementation stuff, all mapped directly to the cryptostream
  1106.         public override bool CanRead { get { return Crypto.CanRead; } }
  1107.         public override bool CanSeek { get { return Crypto.CanSeek; } }
  1108.         public override bool CanWrite { get { return Crypto.CanWrite; } }
  1109.         public override void Flush() { Crypto.Flush(); }
  1110.         public override long Length { get { return Crypto.Length; } }
  1111.         public override long Position
  1112.         {
  1113.             get { return Crypto.Position; }
  1114.             set { Crypto.Position = value; }
  1115.         }
  1116.         public override long Seek(long offset, System.IO.SeekOrigin origin) { return Crypto.Seek(offset, origin); }
  1117.         public override void SetLength(long value) { Crypto.SetLength(value); }
  1118.         #endregion
  1119.  
  1120.         /// <summary>
  1121.         /// Reads unencrypted data from the underlying stream
  1122.         /// </summary>
  1123.         /// <param name="buffer">The buffer to read data into</param>
  1124.         /// <param name="offset">The offset into the buffer</param>
  1125.         /// <param name="count">The number of bytes to read</param>
  1126.         /// <returns>The number of bytes read</returns>
  1127.         public override int Read(byte[] buffer, int offset, int count)
  1128.         {
  1129.             if (m_mode != OperationMode.Decrypt)
  1130.                 throw new InvalidOperationException(Strings.CannotReadWhileEncrypting);
  1131.  
  1132.             if (m_hasReadFooter)
  1133.                 return 0;
  1134.  
  1135.             count = Crypto.Read(buffer, offset, count);
  1136.  
  1137.             //TODO: If the cryptostream supporting seeking in future versions of .Net,
  1138.             // this counter system does not work
  1139.             m_readcount += count;
  1140.             m_length = (m_length + count) % BLOCK_SIZE;
  1141.  
  1142.             if (!m_hasReadFooter && m_readcount == m_payloadLength)
  1143.             {
  1144.                 m_hasReadFooter = true;
  1145.  
  1146.                 //Verify the data
  1147.                 if (m_version >= 1)
  1148.                 {
  1149.                     int l = m_stream.ReadByte();
  1150.                     if (l < 0)
  1151.                         throw new InvalidDataException(Strings.UnexpectedEndOfStream);
  1152.                     m_paddingSize = (byte)l;
  1153.                     if (m_paddingSize > BLOCK_SIZE)
  1154.                         throw new InvalidDataException(Strings.InvalidFileLength);
  1155.                 }
  1156.  
  1157.                 if (m_paddingSize > 0)
  1158.                     count -= (BLOCK_SIZE - m_paddingSize);
  1159.  
  1160.                 if (m_length % BLOCK_SIZE != 0 || m_readcount % BLOCK_SIZE != 0)
  1161.                     throw new InvalidDataException(Strings.InvalidFileLength);
  1162.  
  1163.                 //Required because we want to read the hash,
  1164.                 // so FlushFinalBlock need to be called.
  1165.                 //We cannot call FlushFinalBlock directly because it may
  1166.                 // have been called by the read operation.
  1167.                 //The StreamHider makes sure that the underlying stream
  1168.                 // is not closed
  1169.                 Crypto.Close();
  1170.  
  1171.                 byte[] hmac1 = m_hmac.Hash;
  1172.                 byte[] hmac2 = RepeatRead(m_stream, hmac1.Length);
  1173.                 for (int i = 0; i < hmac1.Length; i++)
  1174.                     if (hmac1[i] != hmac2[i])
  1175.                         throw new InvalidDataException(m_version == 0 ? Strings.DataHMACMismatch_v0 : Strings.DataHMACMismatch);
  1176.             }
  1177.  
  1178.             return count;
  1179.         }
  1180.  
  1181.         /// <summary>
  1182.         /// Writes unencrypted data into an encrypted stream
  1183.         /// </summary>
  1184.         /// <param name="buffer">The data to write</param>
  1185.         /// <param name="offset">The offset into the buffer</param>
  1186.         /// <param name="count">The number of bytes to write</param>
  1187.         public override void Write(byte[] buffer, int offset, int count)
  1188.         {
  1189.             if (m_mode != OperationMode.Encrypt)
  1190.                 throw new InvalidOperationException(Strings.CannotWriteWhileDecrypting);
  1191.  
  1192.             m_length = (m_length + count) % BLOCK_SIZE;
  1193.             Crypto.Write(buffer, offset, count);
  1194.         }
  1195.  
  1196.         /// <summary>
  1197.         /// Flushes any remaining data to the stream
  1198.         /// </summary>
  1199.         public void FlushFinalBlock()
  1200.         {
  1201.             if (!m_hasFlushedFinalBlock)
  1202.             {
  1203.                 if (m_mode == OperationMode.Encrypt)
  1204.                 {
  1205.                     if (!m_hasWrittenHeader)
  1206.                         WriteEncryptionHeader();
  1207.  
  1208.                     byte lastLen = (byte)(m_length %= BLOCK_SIZE);
  1209.  
  1210.                     //Apply PaddingMode.PKCS7 manually, the original AES crypt uses non-standard padding
  1211.                     if (lastLen != 0)
  1212.                     {
  1213.                         byte[] padding = new byte[BLOCK_SIZE - lastLen];
  1214.                         for (int i = 0; i < padding.Length; i++)
  1215.                             padding[i] = (byte)padding.Length;
  1216.                         Write(padding, 0, padding.Length);
  1217.                     }
  1218.  
  1219.                     //Not required without padding, but throws exception if the stream is used incorrectly
  1220.                     Crypto.FlushFinalBlock();
  1221.                     //The StreamHider makes sure the underlying stream is not closed.
  1222.                     Crypto.Close();
  1223.  
  1224.                     byte[] hmac = m_hmac.Hash;
  1225.  
  1226.                     if (m_version == 0)
  1227.                     {
  1228.                         m_stream.Write(hmac, 0, hmac.Length);
  1229.                         long pos = m_stream.Position;
  1230.                         m_stream.Seek(MAGIC_HEADER.Length + 1, SeekOrigin.Begin);
  1231.                         m_stream.WriteByte(lastLen);
  1232.                         m_stream.Seek(pos, SeekOrigin.Begin);
  1233.                         m_stream.Flush();
  1234.                     }
  1235.                     else
  1236.                     {
  1237.                         m_stream.WriteByte(lastLen);
  1238.                         m_stream.Write(hmac, 0, hmac.Length);
  1239.                         m_stream.Flush();
  1240.                     }
  1241.                 }
  1242.             }
  1243.         }
  1244.  
  1245.         /// <summary>
  1246.         /// Releases all resources used by the instance, and flushes any data currently held, into the stream
  1247.         /// </summary>
  1248.         protected override void Dispose(bool disposing)
  1249.         {
  1250.             base.Dispose(disposing);
  1251.  
  1252.             if (disposing)
  1253.             {
  1254.                 if (m_mode == OperationMode.Encrypt && !m_hasFlushedFinalBlock)
  1255.                     FlushFinalBlock();
  1256.  
  1257.                 m_crypto.Dispose();
  1258.                 m_crypto = null;
  1259.                 m_stream.Dispose();
  1260.                 m_stream = null;
  1261.                 m_extensions = null;
  1262.                 m_helper.Dispose();
  1263.                 m_helper = null;
  1264.                 m_hmac = null;
  1265.             }
  1266.         }
  1267.  
  1268.         #endregion
  1269.  
  1270.         #region Unittest code
  1271.         #if DEBUG
  1272.         /// <summary>
  1273.         /// Performs a unittest to ensure that the program performs as expected
  1274.         /// </summary>
  1275.         private static void Unittest()
  1276.         {
  1277.             const int MIN_SIZE = 1024 * 5;
  1278.             const int MAX_SIZE = 1024 * 1024 * 100; //100mb
  1279.             const int REPETIONS = 1000;
  1280.  
  1281.             bool allpass = true;
  1282.  
  1283.             Random rnd = new Random();
  1284.             Console.WriteLine("Running unittest");
  1285.  
  1286.             //Test each supported version
  1287.             for (byte v = 0; v <= MAX_FILE_VERSION; v++)
  1288.             {
  1289.                 SharpAESCrypt.DefaultFileVersion = v;
  1290.  
  1291.                 //Test boundary 0 and around the block/keysize margins
  1292.                 for (int i = 0; i < MIN_SIZE; i++)
  1293.                     using (MemoryStream ms = new MemoryStream())
  1294.                     {
  1295.                         byte[] tmp = new byte[i];
  1296.                         rnd.NextBytes(tmp);
  1297.                         ms.Write(tmp, 0, tmp.Length);
  1298.                         allpass &= Unittest(string.Format("Testing version {0} with length = {1} => ", v, ms.Length), ms);
  1299.                     }
  1300.             }
  1301.  
  1302.             SharpAESCrypt.DefaultFileVersion = MAX_FILE_VERSION;
  1303.             Console.WriteLine(string.Format("Initial tests complete, running bulk tests with v{0}", SharpAESCrypt.DefaultFileVersion));
  1304.  
  1305.             for (int i = 0; i < REPETIONS; i++)
  1306.             {
  1307.                 using (MemoryStream ms = new MemoryStream())
  1308.                 {
  1309.                     byte[] tmp = new byte[rnd.Next(MIN_SIZE, MAX_SIZE)];
  1310.                     rnd.NextBytes(tmp);
  1311.                     ms.Write(tmp, 0, tmp.Length);
  1312.                     allpass |= Unittest(string.Format("Testing bulk {0} of {1} with length = {2} => ", i, REPETIONS, ms.Length), ms);
  1313.                 }
  1314.             }
  1315.  
  1316.             if (allpass)
  1317.             {
  1318.                 Console.WriteLine();
  1319.                 Console.WriteLine();
  1320.                 Console.WriteLine("**** All unittests passed ****");
  1321.                 Console.WriteLine();
  1322.             }
  1323.         }
  1324.  
  1325.         /// <summary>
  1326.         /// Helper function to
  1327.         /// </summary>
  1328.         /// <param name="message">A message printed to the console</param>
  1329.         /// <param name="input">The stream to test with</param>
  1330.         private static bool Unittest(string message, MemoryStream input)
  1331.         {
  1332.             Console.Write(message);
  1333.  
  1334.             const string PASSWORD_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#¤%&/()=?`*'^¨-_.:,;<>|";
  1335.             const int MIN_LEN = 1;
  1336.             const int MAX_LEN = 25;
  1337.  
  1338.             try
  1339.             {
  1340.                 Random rnd = new Random();
  1341.                 char[] pwdchars = new char[rnd.Next(MIN_LEN, MAX_LEN)];
  1342.                 for (int i = 0; i < pwdchars.Length; i++)
  1343.                     pwdchars[i] = PASSWORD_CHARS[rnd.Next(0, PASSWORD_CHARS.Length)];
  1344.  
  1345.                 input.Position = 0;
  1346.  
  1347.                 using (MemoryStream enc = new MemoryStream())
  1348.                 using (MemoryStream dec = new MemoryStream())
  1349.                 {
  1350.                     Encrypt(new string(pwdchars), input, enc);
  1351.                     enc.Position = 0;
  1352.                     Decrypt(new string(pwdchars), enc, dec);
  1353.  
  1354.                     dec.Position = 0;
  1355.                     input.Position = 0;
  1356.  
  1357.                     if (dec.Length != input.Length)
  1358.                         throw new Exception(string.Format("Length differ {0} vs {1}", dec.Length, input.Length));
  1359.  
  1360.                     for (int i = 0; i < dec.Length; i++)
  1361.                         if (dec.ReadByte() != input.ReadByte())
  1362.                             throw new Exception(string.Format("Streams differ at byte {0}", i));
  1363.                 }
  1364.             }
  1365.             catch (Exception ex)
  1366.             {
  1367.                 //TODO: Hashværdien skal færdigøres før hash værdien hentes?
  1368.                 Console.WriteLine("FAILED: " + ex.Message);
  1369.                 return false;
  1370.             }
  1371.  
  1372.             Console.WriteLine("OK!");
  1373.             return true;
  1374.         }
  1375. #endif
  1376.         #endregion
  1377.     }
  1378. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement