/// <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> /// 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); } }