/// <summary> /// Removes sign from <see cref="DataPack"/> stream /// </summary> /// <param name="prefixSize">Size of prefix</param> /// <param name="sourceStream">Source stream</param> /// <param name="destinationStream">Destination stream</param> public static void RemoveSign(uint prefixSize, Stream sourceStream, Stream destinationStream) { if (sourceStream == null) { throw new ArgumentNullException("sourceStream"); } if (destinationStream == null) { throw new ArgumentNullException("destinationStream"); } var buffer = BufferProvider.Current.TakeBuffer(); try { using (var wrapper = new NonClosingStreamWrapper(sourceStream)) // To prevent stream from closing by BinaryReader using (var br = new BinaryReader(wrapper)) { int byteCount; uint prefixBytesLeft = prefixSize + 1; while ((byteCount = wrapper.Read(buffer, 0, (int)Math.Min(buffer.Length, prefixBytesLeft))) > 0) { destinationStream.Write(buffer, 0, byteCount); prefixBytesLeft -= (uint)byteCount; if (prefixBytesLeft == 0) { break; } } uint protectedDataSize = br.ReadUInt32(); // Protected data size // ReSharper disable once UnusedVariable uint signSize = br.ReadUInt32(); // Sign size destinationStream.WriteByte(0); wrapper.Seek(4 + 4, SeekOrigin.Current); while ((byteCount = wrapper.Read(buffer, 0, (int)Math.Min(buffer.Length, protectedDataSize))) > 0) { destinationStream.Write(buffer, 0, byteCount); protectedDataSize -= (uint)byteCount; if (protectedDataSize == 0) { break; } } } } finally { BufferProvider.Current.ReturnBuffer(buffer); } }
/// <summary> /// Checks if given <see cref="DataPack"/> stream sign is correc /// </summary> /// <param name="prefixSize">Size of prefix</param> /// <param name="signAlgorithm">Sign algorithm</param> /// <param name="stream">Stream of <see cref="DataPack"/></param> /// <returns></returns> public static bool IsSignMatch(uint prefixSize, ISignAlgorithm signAlgorithm, Stream stream) { if (signAlgorithm == null) { throw new ArgumentNullException("signAlgorithm"); } 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."); } long position = stream.Position; stream.Seek(prefixSize + 1, SeekOrigin.Current); int isSignedByte = stream.ReadByte(); if (isSignedByte == 0) { throw new InvalidOperationException("Stream is not signed."); } using (var wrapper = new NonClosingStreamWrapper(stream)) // To prevent stream from closing by BinaryReader using (var br = new BinaryReader(wrapper)) { uint protectedDataSize = br.ReadUInt32(); // Protected data size uint signSize = br.ReadUInt32(); // Sign size using (var signStream = new FilteredStream(wrapper, wrapper.Position + protectedDataSize, signSize)) { using (var dataStream = new FilteredStream(wrapper, wrapper.Position, protectedDataSize - 8)) { var result = signAlgorithm.VerifySign(dataStream, signStream); wrapper.Position = position; return(result); } } } }
/// <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); } }