/// <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);
                    }
                }
        }
예제 #2
0
        /// <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);
            }
        }