/// <inheritdoc /> public override void ForwardProcessDataStream(Stream inStream, Stream outStream, Dictionary <string, string> options, out long writtenBytes) { if (options == null) { throw new ArgumentException("Options dictionary was null", "options"); } else if (!options.ContainsKey(paddedSizeOptionName) || !options.ContainsKey(padTypeOptionName) || !options.ContainsKey(padExceptionOptionName)) { throw new ArgumentException("Options dictionary did not contain the necessary padding options", "options"); } int paddingSize; DataPaddingType padType; bool padException; if (!int.TryParse(options[paddedSizeOptionName], out paddingSize) || !bool.TryParse(options[padExceptionOptionName], out padException)) { throw new ArgumentException("Options dictionary contained invalid options for DataPadder", "options"); } try { padType = (DataPaddingType)Enum.Parse(typeof(DataPaddingType), options[padTypeOptionName]); } catch (ArgumentException) { throw new ArgumentException("Options dictionary contained invalid options for DataPadder", "options"); } if (padException && inStream.Length > paddingSize - 4) { throw new ArgumentException("Options dictionary contained invalid options for DataPadder. Not enough data padding was allowed", "options"); } paddingSize = paddingSize - 4 - (int)inStream.Length; if (paddingSize < 0) { paddingSize = 0; } byte[] padData; if (padType == DataPaddingType.Random) { #if NETFX_CORE CryptographicBuffer.CopyToByteArray(CryptographicBuffer.GenerateRandom((uint)paddingSize), out padData); #else padData = new byte[paddingSize]; rand.GetBytes(padData); #endif } else { padData = new byte[paddingSize]; } StreamTools.Write(inStream, 0, inStream.Length, outStream, 8192, double.MaxValue, int.MaxValue); if (paddingSize != 0) { outStream.Write(padData, 0, padData.Length); } outStream.Write(BitConverter.GetBytes(paddingSize + 4), 0, 4); writtenBytes = outStream.Position; }
/// <inheritdoc /> public override void ReverseProcessDataStream(Stream inStream, Stream outStream, Dictionary <string, string> options, out long writtenBytes) { inStream.Seek(inStream.Length - 4, SeekOrigin.Begin); byte[] buffer = new byte[4]; inStream.Read(buffer, 0, 4); inStream.Seek(0, SeekOrigin.Begin); //read the last 4 bytes from the input stream int padSize = BitConverter.ToInt32(buffer, 0); StreamTools.Write(inStream, 0, inStream.Length - padSize, outStream, 8192, double.MaxValue, int.MaxValue); writtenBytes = outStream.Length; }
/// <inheritdoc /> public override void ForwardProcessDataStream(System.IO.Stream inStream, System.IO.Stream outStream, Dictionary <string, string> options, out long writtenBytes) { if (options == null) { throw new ArgumentNullException("options"); } else if (!options.ContainsKey(PasswordOption)) { throw new ArgumentException("Options must contain encryption key", "options"); } if (outStream == null) { throw new ArgumentNullException("outStream"); } #if NETFX_CORE inStream.Seek(0, 0); outStream.Seek(0, 0); IBuffer pwBuffer = CryptographicBuffer.ConvertStringToBinary(options[PasswordOption], BinaryStringEncoding.Utf8); IBuffer saltBuffer = CryptographicBuffer.CreateFromByteArray(SALT); // Derive key material for password size 32 bytes for AES256 algorithm KeyDerivationAlgorithmProvider keyDerivationProvider = Windows.Security.Cryptography.Core.KeyDerivationAlgorithmProvider.OpenAlgorithm("PBKDF2_SHA1"); // using salt and 1000 iterations KeyDerivationParameters pbkdf2Parms = KeyDerivationParameters.BuildForPbkdf2(saltBuffer, 1000); // create a key based on original key and derivation parmaters CryptographicKey keyOriginal = keyDerivationProvider.CreateKey(pwBuffer); IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial(keyOriginal, pbkdf2Parms, 32); CryptographicKey derivedPwKey = keyDerivationProvider.CreateKey(pwBuffer); // derive buffer to be used for encryption salt from derived password key IBuffer saltMaterial = CryptographicEngine.DeriveKeyMaterial(derivedPwKey, pbkdf2Parms, 16); // display the buffers - because KeyDerivationProvider always gets cleared after each use, they are very similar unforunately string keyMaterialString = CryptographicBuffer.EncodeToBase64String(keyMaterial); string saltMaterialString = CryptographicBuffer.EncodeToBase64String(saltMaterial); SymmetricKeyAlgorithmProvider symProvider = SymmetricKeyAlgorithmProvider.OpenAlgorithm("AES_CBC_PKCS7"); // create symmetric key from derived password key CryptographicKey symmKey = symProvider.CreateSymmetricKey(keyMaterial); using (MemoryStream ms = new MemoryStream()) { inStream.CopyTo(ms); // encrypt data buffer using symmetric key and derived salt material IBuffer resultBuffer = CryptographicEngine.Encrypt(symmKey, WindowsRuntimeBufferExtensions.GetWindowsRuntimeBuffer(ms), saltMaterial); resultBuffer.AsStream().CopyTo(outStream); writtenBytes = outStream.Position; } #else Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(options[PasswordOption], SALT); var key = pdb.GetBytes(32); pdb.Reset(); var iv = pdb.GetBytes(16); using (var transform = encrypter.CreateEncryptor(key, iv)) { using (MemoryStream internalStream = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(internalStream, transform, CryptoStreamMode.Write)) { StreamTools.Write(inStream, csEncrypt); inStream.Flush(); csEncrypt.FlushFinalBlock(); internalStream.Seek(0, 0); StreamTools.Write(internalStream, outStream); writtenBytes = outStream.Position; } } } #endif }
/// <summary> /// Sets the item data using the provided data stream. Useful for setting data after deserialisation /// </summary> /// <param name="itemIdentifier"></param> /// <param name="itemDataStream"></param> public void SetData(string itemIdentifier, Stream itemDataStream) { lock (dataLocker) { if (itemIdentifier == null) { throw new ArgumentNullException("itemIdentifier"); } if (itemDataStream == null) { throw new ArgumentNullException("itemDataStream"); } #region Build Internal Data Structure if (DataBuildMode == DataBuildMode.Disk_Single || //ItemBuildMode == ItemBuildMode.Both_Single || DataBuildMode == DataBuildMode.Memory_Single) { CompleteDataStream = new StreamTools.ThreadSafeStream(itemDataStream); } else { //Break the itemDataStream into blocks ChunkDataStreams = new StreamTools.ThreadSafeStream[ChunkPositionLengthDict.Count]; //If the itemDataStream is a memory stream we can try to access the buffer as it makes creating the streams more efficient byte[] itemDataStreamBuffer = null; if (itemDataStream is MemoryStream) { try { itemDataStreamBuffer = ((MemoryStream)itemDataStream).GetBuffer(); } catch (UnauthorizedAccessException) { /* Ignore */ } } for (int i = 0; i < ChunkPositionLengthDict.Count; i++) { if (itemDataStreamBuffer != null) { //This is the fastest way to create a block of data streams ChunkDataStreams[i] = new StreamTools.ThreadSafeStream(new MemoryStream(itemDataStreamBuffer, ChunkPositionLengthDict[i].Position, ChunkPositionLengthDict[i].Length)); } else { //We now need to available the respective data into blocks Stream destinationStream = null; if (DataBuildMode == DataBuildMode.Disk_Blocks) { string folderLocation = "DFS_" + NetworkComms.NetworkIdentifier; string fileName = Path.Combine(folderLocation, itemIdentifier + ".DFSItemData_" + i.ToString()); if (File.Exists(fileName)) { destinationStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); //if (StreamTools.MD5(destinationStream) != checksum) // throw new Exception("Wrong place, wrong time, wrong file!"); } else { //Create the folder if it does not exist yet lock (DFS.globalDFSLocker) { if (!Directory.Exists(folderLocation)) { Directory.CreateDirectory(folderLocation); } } destinationStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 8192, FileOptions.DeleteOnClose); destinationStream.SetLength(ChunkPositionLengthDict[i].Length); destinationStream.Flush(); } if (!File.Exists(fileName)) { throw new Exception("At this point the item data file should have been created. This exception should not really be possible."); } } else { //If we are not exclusively building to the disk we just use a memory stream at this stage destinationStream = new MemoryStream(ChunkPositionLengthDict[i].Length); } //Ensure we start at the beginning of the stream destinationStream.Seek(0, SeekOrigin.Begin); //Copy over the correct part of the itemDataStream to the chunks StreamTools.Write(itemDataStream, ChunkPositionLengthDict[i].Position, ChunkPositionLengthDict[i].Length, destinationStream, 8192, double.MaxValue, int.MaxValue); ChunkDataStreams[i] = new StreamTools.ThreadSafeStream(destinationStream); } } } #endregion this.CompleteDataCheckSum = StreamTools.MD5(itemDataStream); } }