/// <summary> /// Read all data parts from <see cref="DataPack"/> /// </summary> /// <param name="dataPack"></param> /// <param name="br">Binary reader</param> /// <param name="dataPartInfos">List of datapart information</param> /// <param name="stream">Stream of transport container</param> protected override void ReadDataParts(DataPack dataPack, BinaryReader br, List <DataPartInfo> dataPartInfos, Stream stream) { foreach (var dataPartInfo in dataPartInfos) { IDataContainer dataContainer; using (var filteredStream = new FilteredStream(stream, dataPartInfo.DataAddress, dataPartInfo.DataSize)) { dataContainer = new TempFileDataContainer(filteredStream); } var dataPart = new DataPart(dataContainer); if (dataPartInfo.HeadersCount > 0) { stream.Seek(dataPartInfo.HeadersAddress, SeekOrigin.Begin); using (var filter = new FilteredStream(stream, dataPartInfo.HeadersAddress, dataPartInfo.HeadersSize)) { for (ushort i = 0; i < dataPartInfo.HeadersCount; i++) { dataPart.Headers.Add(Serializer.DeserializeWithLengthPrefix <DataPair>(filter, PrefixStyle.Base128)); } } } if (dataPartInfo.PropertiesCount > 0) { stream.Seek(dataPartInfo.PropertiesAddress, SeekOrigin.Begin); using (var filter = new FilteredStream(stream, dataPartInfo.PropertiesAddress, dataPartInfo.PropertiesSize)) { for (ushort i = 0; i < dataPartInfo.PropertiesCount; i++) { dataPart.Properties.AddOrReplace(Serializer.DeserializeWithLengthPrefix <DataPair>(filter, PrefixStyle.Base128)); } } } dataPack.DataParts.Add(dataPart); } }
/// <summary> /// Writes <see cref="DataPack"/> to stream /// </summary> /// <param name="dataPack">Data</param> /// <param name="stream">Stream where <see cref="DataPack"/> is written to</param> /// <param name="signAlgorithm">Sign algorithm if needed</param> public void Write(DataPack dataPack, Stream stream, ISignAlgorithm signAlgorithm = null) { if (stream == null) { throw new ArgumentNullException("stream"); } if (dataPack == null) { throw new ArgumentNullException("dataPack"); } if (!stream.CanSeek) { throw new InvalidOperationException("Stream doesn't support seeking."); } if (!stream.CanWrite) { throw new InvalidOperationException("Stream doesn't support writing."); } using (var wrapper = new NonClosingStreamWrapper(stream)) // To prevent source stream from closing by BinaryWriter using (var bw = new BinaryWriter(wrapper)) { // Size of prefix bw.Write(dataPack.PrefixSize); // Prefix of data bw.Write(dataPack.GetPrefix(), 0, dataPack.PrefixSize); uint signInfoAddress = 0; uint dataSizeAddress = 0; if (signAlgorithm == null) { // Stream not signed bw.Write((byte)0); } else { // Stream is signed bw.Write((byte)1); signInfoAddress = GetAddress(bw); bw.Write(0); // Protected data size bw.Write(0); // Sign size dataSizeAddress = GetAddress(bw) - 8; } // Create implicit properties var implicitProperties = new Properties(); if (dataPack.DateCreate != null) { implicitProperties["DateCreate"] = dataPack.DateCreate.Value.ToString(Consts.DateTimeFormat, CultureInfo.InvariantCulture); } if (dataPack.FileId != null) { implicitProperties["FileId"] = dataPack.FileId != null ? dataPack.FileId.Value.ToString() : null; } if (dataPack.Description != null) { implicitProperties["Description"] = dataPack.Description; } // Info section implicit properties var properties = implicitProperties.GetPropertiesList(); bw.Write(InfoSection); uint address = GetAddress(bw); bw.Write(0u); ushort cnt = (ushort)properties.Count; bw.Write(cnt); for (ushort i = 0; i < cnt; i++) { Serializer.SerializeWithLengthPrefix(wrapper, properties[i], PrefixStyle.Base128); } WriteSize(bw, address, address + 4); // Info section with data headers info bw.Write(InfoSection); address = GetAddress(bw); bw.Write(0u); cnt = (ushort)dataPack.Headers.Count; bw.Write(cnt); for (ushort i = 0; i < cnt; i++) { Serializer.SerializeWithLengthPrefix(wrapper, dataPack.Headers[i], PrefixStyle.Base128); } WriteSize(bw, address, address + 4); // Header section with data properties info properties = dataPack.Properties.GetPropertiesList(); bw.Write(InfoSection); address = GetAddress(bw); bw.Write(0u); cnt = (ushort)properties.Count; bw.Write(cnt); for (ushort i = 0; i < cnt; i++) { Serializer.SerializeWithLengthPrefix(wrapper, properties[i], PrefixStyle.Base128); } WriteSize(bw, address, address + 4); // Header section with data dataPart's info bw.Write(InfoSection); var dataPartAddress = GetAddress(bw); bw.Write(0u); cnt = (ushort)dataPack.DataParts.Count; bw.Write(cnt); for (ushort i = 0; i < cnt; i++) { bw.Write(0u); bw.Write((ushort)0); bw.Write(0u); bw.Write(0u); bw.Write((ushort)0); bw.Write(0u); bw.Write(0u); bw.Write(0u); } WriteSize(bw, dataPartAddress, dataPartAddress + 4); bw.Write(DataSection); address = GetAddress(bw); bw.Write(0u); for (ushort i = 0; i < cnt; i++) { WriteDataPart(bw, dataPartAddress + 6, i, dataPack); } WriteSize(bw, address, address + 4); bw.Seek(0, SeekOrigin.End); bw.Flush(); if (signAlgorithm != null) { uint protectedDataSize = GetAddress(bw) - dataSizeAddress; wrapper.Position = dataSizeAddress; bw.Write(protectedDataSize); bw.Flush(); byte[] sign; using (var filter = new FilteredStream(wrapper, dataSizeAddress, protectedDataSize)) { sign = signAlgorithm.GetSign(filter); } wrapper.Seek(0, SeekOrigin.End); bw.Write(sign, 0, sign.Length); bw.Flush(); wrapper.Position = signInfoAddress; bw.Write((uint)sign.Length); bw.Flush(); wrapper.Seek(0, SeekOrigin.End); } } }
/// <summary> /// Reads <see cref="DataPack"/> from stream, using prefix if provided /// </summary> /// <param name="stream">Stream with data to deserialize</param> /// <param name="prefix">Byte array with bytes to check before deserialize</param> /// <returns></returns> public DataPack Read(Stream stream, byte[] prefix = null) { if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanSeek) { throw new InvalidOperationException("Stream doesn't support seeking."); } if (!stream.CanRead) { throw new InvalidOperationException("Stream doesn't support reading."); } DataPack dataPack; using (var wrapper = new NonClosingStreamWrapper(stream)) // To prevent source stream from closing by BinaryReader using (var br = new BinaryReader(wrapper)) { byte prefixSize = br.ReadByte(); byte[] dataPrefix = null; if (prefixSize > 0) { dataPrefix = br.ReadBytes(prefixSize); } dataPack = new DataPack(dataPrefix) { DateCreate = null, Description = null, FileId = null }; if (!dataPack.IsPrefixMatch(prefix)) { throw new InvalidOperationException("Data prefix is wrong."); } byte isSignedByte = br.ReadByte(); switch (isSignedByte) { case 0: // No sign. Nothing to do break; case 1: // ReSharper disable once UnusedVariable uint protectedDataSize = br.ReadUInt32(); // Protected data size // ReSharper disable once UnusedVariable uint signSize = br.ReadUInt32(); // Sign size break; default: throw new InvalidOperationException("Unknown information about file sign."); } if (br.ReadByte() != InfoSection) { throw new InvalidOperationException("Implicit properties info section not found."); } uint size = br.ReadUInt32(); ushort propertiesCount = br.ReadUInt16(); var implicitProperties = new Properties(); using (var filter = new FilteredStream(wrapper, wrapper.Position, size - 2)) { for (ushort i = 0; i < propertiesCount; i++) { implicitProperties.AddOrReplace(Serializer.DeserializeWithLengthPrefix <DataPair>(filter, PrefixStyle.Base128)); } } var dateCreateString = implicitProperties.TryGetPropertyValue("DateCreate", null); if (dateCreateString != null) { dataPack.DateCreate = DateTime.ParseExact(dateCreateString, Consts.DateTimeFormat, CultureInfo.InvariantCulture); } var fileIdString = implicitProperties.TryGetPropertyValue("FileId", null); if (fileIdString != null) { #if NET20 || NET30 || NET35 dataPack.FileId = new Guid(fileIdString); #endif #if NET40 || NET45 dataPack.FileId = Guid.Parse(fileIdString); #endif } var descriptionString = implicitProperties.TryGetPropertyValue("Description", null); if (descriptionString != null) { dataPack.Description = descriptionString; } if (br.ReadByte() != InfoSection) { throw new InvalidOperationException("Headers info section not found."); } size = br.ReadUInt32(); ushort headersCount = br.ReadUInt16(); using (var filter = new FilteredStream(wrapper, br.BaseStream.Position, size - 2)) { for (ushort i = 0; i < headersCount; i++) { dataPack.Headers.Add(Serializer.DeserializeWithLengthPrefix <DataPair>(filter, PrefixStyle.Base128)); } } if (br.ReadByte() != InfoSection) { throw new InvalidOperationException("Properties info section not found."); } size = br.ReadUInt32(); propertiesCount = br.ReadUInt16(); using (var filter = new FilteredStream(wrapper, br.BaseStream.Position, size - 2)) { for (ushort i = 0; i < propertiesCount; i++) { dataPack.Properties.AddOrReplace(Serializer.DeserializeWithLengthPrefix <DataPair>(filter, PrefixStyle.Base128)); } } if (br.ReadByte() != InfoSection) { throw new InvalidOperationException("DataPart's info section not found."); } // ReSharper disable once RedundantAssignment size = br.ReadUInt32(); ushort dataPartsCount = br.ReadUInt16(); var dataPartInfos = new List <DataPartInfo>(); for (ushort i = 0; i < dataPartsCount; i++) { dataPartInfos.Add(new DataPartInfo { HeadersAddress = br.ReadUInt32(), HeadersCount = br.ReadUInt16(), HeadersSize = br.ReadUInt32(), PropertiesAddress = br.ReadUInt32(), PropertiesCount = br.ReadUInt16(), PropertiesSize = br.ReadUInt32(), DataAddress = br.ReadUInt32(), DataSize = br.ReadUInt32() }); } ReadDataParts(dataPack, br, dataPartInfos, stream); } return(dataPack); }
/// <summary> /// Sign source <see cref="DataPack"/> stream using given sign algorithm and outputs result to destinationStream /// </summary> /// <param name="prefixSize">Size of file prefix</param> /// <param name="signAlgorithm">Sign algorithm</param> /// <param name="sourceStream">Source stream</param> /// <param name="destinationStream">Destination stream</param> public static void Sign(uint prefixSize, ISignAlgorithm signAlgorithm, Stream sourceStream, Stream destinationStream) { if (signAlgorithm == null) { throw new ArgumentNullException("signAlgorithm"); } if (sourceStream == null) { throw new ArgumentNullException("sourceStream"); } if (destinationStream == null) { throw new ArgumentNullException("destinationStream"); } var position = sourceStream.Position; if (!sourceStream.CanSeek) { throw new InvalidOperationException("Source stream doesn't support seeking."); } if (!sourceStream.CanRead) { throw new InvalidOperationException("Source stream doesn't support reading."); } if (!destinationStream.CanSeek) { throw new InvalidOperationException("Destination stream doesn't support seeking."); } if (!destinationStream.CanRead) { throw new InvalidOperationException("Destination stream doesn't support reading."); } if (!destinationStream.CanWrite) { throw new InvalidOperationException("Destination stream doesn't support writing."); } sourceStream.Seek(prefixSize + 1, SeekOrigin.Current); int isSignedByte = sourceStream.ReadByte(); if (isSignedByte == 1) { throw new InvalidOperationException("Source stream already signed."); } sourceStream.Position = position; var buffer = BufferProvider.Current.TakeBuffer(); try { int byteCount; uint prefixBytesLeft = prefixSize + 1; while ((byteCount = sourceStream.Read(buffer, 0, (int)Math.Min(buffer.Length, prefixBytesLeft))) > 0) { destinationStream.Write(buffer, 0, byteCount); prefixBytesLeft -= (uint)byteCount; if (prefixBytesLeft == 0) { break; } } using (var wrapper = new NonClosingStreamWrapper(destinationStream)) // To prevent stream from closing by BinaryWriter using (var bw = new BinaryWriter(wrapper)) { bw.Write((byte)1); uint signInfoAddress = GetAddress(bw); bw.Write(0); // Protected data size bw.Write(0); // Sign size uint dataSizeAddress = GetAddress(bw) - 8; while ((byteCount = sourceStream.Read(buffer, 0, buffer.Length)) > 0) { wrapper.Write(buffer, 0, byteCount); } wrapper.Flush(); uint protectedDataSize = GetAddress(bw) - dataSizeAddress; wrapper.Position = dataSizeAddress; bw.Write(protectedDataSize); bw.Flush(); byte[] sign; using (var filter = new FilteredStream(wrapper, dataSizeAddress, protectedDataSize)) { sign = signAlgorithm.GetSign(filter); } wrapper.Seek(0, SeekOrigin.End); bw.Write(sign, 0, sign.Length); bw.Flush(); wrapper.Position = signInfoAddress; bw.Write((uint)sign.Length); bw.Flush(); wrapper.Seek(0, SeekOrigin.End); } } finally { BufferProvider.Current.ReturnBuffer(buffer); } }