Обрезать поток неизвестной длины х байтов с конца?(.СЕТЬ) - PullRequest
1 голос
/ 21 марта 2012

Мне нужно прочитать поток неизвестной длины, исключая последние 20 байтов (хэш-данные).Настройка примерно равна:

Исходный поток (с хешем SHA1 последние 20 байтов) -> Поток хэширования SHA1 (вычисление на лету и сравнение с хешем встроенного потока, когда поток заканчивается) -> Поток расшифровки AES ->Делайте что-то с данными ...

Я не могу буферизовать весь исходный поток перед обработкой, так как это может быть много гигабайт, все это должно происходить на лету.Исходный поток не доступен для поиска.В настоящее время поток SHA1 считывает последние 20 байтов в свои буферы, которые разбивают все, и я не знаю ни одного способа управления этим поведением.

Я думал о вставке потока-обертки между источникоми потоки SHA1, реализующие скользящий буфер (?), который представляет исходный поток оболочке AES в 4096 байтовых кусках, а затем «подделывает» конец потока на 20 байтов ранее при последнем чтении.Затем 20-байтовый хеш будет предоставлен через свойство.

Будет ли это лучшим решением и как я его реализую?

Пример грубого кода приведен ниже (из памяти, вероятно, выигралне компилируется):

SourceStream = TcpClient.Stream
HashedStream = New CryptoStream(SourceStream, Sha1Hasher, CryptoStreamMode.Read)
AesDecryptedStream = New CryptoStream(HashedStream, AesDecryptor, CryptoStreamMode.Read)

' Read out and deserialize data
AesDecryptedStream.Read(etc...)

' Check if signatures match, throw data away if not
If Not Sha1Hash.SequenceEqual(ExpectedHash)

' Do stuff with the data here

Редактировать : Формат потока следующий:

[  StreamFormat  |  String  |  Required  ]
[  WrapperFlags  |  8 Bit BitArray  |  Required  ]
[  Sha1 Hashed Data Wrapper  |  Optional  ]
   [  AesIV  |  16 Bytes  |  Required if Aes Encrypted  ]
   [  Aes Encrypted Data Wrapper  |  Optional  ]
      [  Gzip Compressed Data Wrapper  |  Optional  ]
         [  Payload Data  |  Binary  |  Required  ]
      [  End Gzip Compressed Data  ]
   [  End Aes Encrypted Data  ]
[  End Sha1 Hashed Data  ]
[  Sha1HashValue  |  20 Bytes  |  Required if Sha1 Hashed  ]

1 Ответ

1 голос
/ 21 марта 2012

Я написал вам быстрый маленький поток, который буферизует вперед на 20 байтов. Единственная реальная реализация, которую я переопределил должным образом, - это член Read(), возможно, вам придется изучить другие члены Stream в соответствии с вашим делом. Поставляется с бесплатным тестовым классом тоже! Бонус! Я проверил это более тщательно, но вы можете адаптировать эти тестовые примеры к своей воле. Да, и, кстати, я не тестировал его для потоков длиной менее 20 байт.

Контрольный пример

[TestClass]
    public class TruncateStreamTests
    {
        [TestMethod]
        public void TestTruncateLast20Bytes()
        {
            string testInput = "This is a string.-- final 20 bytes --";
            string expectedOutput = "This is a string.";
            string testOutput;
            using (var testStream = new StreamWhichEndsBeforeFinal20Bytes(new MemoryStream(Encoding.ASCII.GetBytes(testInput))))
            using (var streamReader = new StreamReader(testStream, Encoding.ASCII))
            {
                testOutput = streamReader.ReadLine();
            }

            Assert.AreEqual(expectedOutput, testOutput);
        }

        [TestMethod]
        public void TestTruncateLast20BytesRead3BytesAtATime()
        {
            string testInput = "This is a really really really really really long string, longer than all the others\n\rit even has some carriage returns in it, etc.-- final 20 bytes --";
            string expectedOutput = "This is a really really really really really long string, longer than all the others\n\rit even has some carriage returns in it, etc.";
            StringBuilder testOutputBuilder = new StringBuilder();
            using (var testStream = new StreamWhichEndsBeforeFinal20Bytes(new MemoryStream(Encoding.ASCII.GetBytes(testInput))))
            {
                int bytesRead = 0;
                do
                {
                    byte[] buffer = new byte[3];
                    bytesRead = testStream.Read(buffer, 0, 3);
                    testOutputBuilder.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
                } while (bytesRead > 0);
            }
            Assert.AreEqual(expectedOutput, testOutputBuilder.ToString());
        }
    }

Потоковый класс

 public class StreamWhichEndsBeforeFinal20Bytes : Stream
    {
        private readonly Stream sourceStream;

        private static int TailBytesCount = 20;

        public StreamWhichEndsBeforeFinal20Bytes(Stream sourceStream)
        {
            this.sourceStream = sourceStream; 
        }

        public byte[] TailBytes { get { return previousTailBuffer; } }

        public override void Flush()
        {
            sourceStream.Flush();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return sourceStream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            sourceStream.SetLength(value);
        }

        private byte[] previousTailBuffer;

        public override int Read(byte[] buffer, int offset, int count)
        {
            byte[] tailBuffer = new byte[TailBytesCount];
            int expectedBytesRead;

            if (previousTailBuffer == null)
                expectedBytesRead = count + TailBytesCount;
            else
                expectedBytesRead = count;

            try
            {
                byte[] readBuffer = new byte[expectedBytesRead];
                int actualBytesRead = sourceStream.Read(readBuffer, offset, expectedBytesRead);

                if (actualBytesRead == 0) return 0;

                if (actualBytesRead < TailBytesCount)
                {
                    int pickPreviousByteCount = TailBytesCount - actualBytesRead;

                    if (previousTailBuffer != null)
                    {
                        int pickFromIndex = previousTailBuffer.Length - pickPreviousByteCount;
                        Array.Copy(previousTailBuffer, 0, buffer, offset, count);
                        Array.Copy(previousTailBuffer, pickFromIndex, tailBuffer, 0, pickPreviousByteCount);
                    }

                    Array.Copy(readBuffer, 0, tailBuffer, pickPreviousByteCount, actualBytesRead);
                    return actualBytesRead;
                }

                Array.Copy(readBuffer, actualBytesRead - TailBytesCount, tailBuffer, 0, TailBytesCount);
                Array.Copy(readBuffer, 0, buffer, offset, actualBytesRead - TailBytesCount);

                if (actualBytesRead < expectedBytesRead)
                {
                    return actualBytesRead - TailBytesCount;
                }
                return count;
            }
            finally
            {
                previousTailBuffer = tailBuffer;
            }
        }


        public override void Write(byte[] buffer, int offset, int count)
        {
            sourceStream.Write(buffer, offset, count);
        }

        public override bool CanRead
        {
            get { return sourceStream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return sourceStream.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return sourceStream.CanWrite; }
        }

        public override long Length
        {
            get
            {
                if (sourceStream.Length < TailBytesCount) return sourceStream.Length;
                return sourceStream.Length - TailBytesCount;
            }
        }

        public override long Position
        {
            get { return sourceStream.Position; }
            set { sourceStream.Position = value; }
        }
    }
...