1

最後の 20 バイト (ハッシュ データ) を除いて、長さが不明なストリームを読み取る必要があります。セットアップは大まかに次のとおりです。

ソース ストリーム (SHA1 ハッシュの最後の 20 バイト) -> SHA1 ハッシュ ストリーム (オンザフライで計算し、ストリームの終了時に埋め込まれたストリーム ハッシュと比較) -> AES 復号化ストリーム -> データを処理する...

ソース ストリーム全体を処理する前にバッファリングすることはできません。これは数ギガバイトになる可能性があるためです。すべてオンザフライで行う必要があります。ソース ストリームはシークできません。現在、SHA1 ストリームは最後の 20 バイトをバッファに読み込んでいて、すべてが壊れており、この動作を制御する方法を知りません。

ソース ストリームと SHA1 ストリームの間にラッパー ストリームを挿入し、ソース ストリームを 4096 バイトのチャンクで AES ラッパーに提示するローリング バッファ (?) を実装し、ストリームの終わりを 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  ]
4

1 に答える 1

1

先に 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; }
        }
    }
于 2012-03-21T17:14:05.890 に答える