ZipAESStream.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. using System;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. namespace ICSharpCode.SharpZipLib.Encryption
  5. {
  6. /// <summary>
  7. /// Encrypts and decrypts AES ZIP
  8. /// </summary>
  9. /// <remarks>
  10. /// Based on information from http://www.winzip.com/aes_info.htm
  11. /// and http://www.gladman.me.uk/cryptography_technology/fileencrypt/
  12. /// </remarks>
  13. internal class ZipAESStream : CryptoStream
  14. {
  15. /// <summary>
  16. /// Constructor
  17. /// </summary>
  18. /// <param name="stream">The stream on which to perform the cryptographic transformation.</param>
  19. /// <param name="transform">Instance of ZipAESTransform</param>
  20. /// <param name="mode">Read or Write</param>
  21. public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode)
  22. : base(stream, transform, mode)
  23. {
  24. _stream = stream;
  25. _transform = transform;
  26. _slideBuffer = new byte[1024];
  27. _blockAndAuth = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH;
  28. // mode:
  29. // CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method.
  30. // Write bypasses this stream and uses the Transform directly.
  31. if (mode != CryptoStreamMode.Read) {
  32. throw new Exception("ZipAESStream only for read");
  33. }
  34. }
  35. // The final n bytes of the AES stream contain the Auth Code.
  36. private const int AUTH_CODE_LENGTH = 10;
  37. private Stream _stream;
  38. private ZipAESTransform _transform;
  39. private byte[] _slideBuffer;
  40. private int _slideBufStartPos;
  41. private int _slideBufFreePos;
  42. // Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32.
  43. private const int CRYPTO_BLOCK_SIZE = 16;
  44. private int _blockAndAuth;
  45. /// <summary>
  46. /// Reads a sequence of bytes from the current CryptoStream into buffer,
  47. /// and advances the position within the stream by the number of bytes read.
  48. /// </summary>
  49. public override int Read(byte[] buffer, int offset, int count)
  50. {
  51. int nBytes = 0;
  52. while (nBytes < count) {
  53. // Calculate buffer quantities vs read-ahead size, and check for sufficient free space
  54. int byteCount = _slideBufFreePos - _slideBufStartPos;
  55. // Need to handle final block and Auth Code specially, but don't know total data length.
  56. // Maintain a read-ahead equal to the length of (crypto block + Auth Code).
  57. // When that runs out we can detect these final sections.
  58. int lengthToRead = _blockAndAuth - byteCount;
  59. if (_slideBuffer.Length - _slideBufFreePos < lengthToRead) {
  60. // Shift the data to the beginning of the buffer
  61. int iTo = 0;
  62. for (int iFrom = _slideBufStartPos; iFrom < _slideBufFreePos; iFrom++, iTo++) {
  63. _slideBuffer[iTo] = _slideBuffer[iFrom];
  64. }
  65. _slideBufFreePos -= _slideBufStartPos; // Note the -=
  66. _slideBufStartPos = 0;
  67. }
  68. int obtained = _stream.Read(_slideBuffer, _slideBufFreePos, lengthToRead);
  69. _slideBufFreePos += obtained;
  70. // Recalculate how much data we now have
  71. byteCount = _slideBufFreePos - _slideBufStartPos;
  72. if (byteCount >= _blockAndAuth) {
  73. // At least a 16 byte block and an auth code remains.
  74. _transform.TransformBlock(_slideBuffer,
  75. _slideBufStartPos,
  76. CRYPTO_BLOCK_SIZE,
  77. buffer,
  78. offset);
  79. nBytes += CRYPTO_BLOCK_SIZE;
  80. offset += CRYPTO_BLOCK_SIZE;
  81. _slideBufStartPos += CRYPTO_BLOCK_SIZE;
  82. } else {
  83. // Last round.
  84. if (byteCount > AUTH_CODE_LENGTH) {
  85. // At least one byte of data plus auth code
  86. int finalBlock = byteCount - AUTH_CODE_LENGTH;
  87. _transform.TransformBlock(_slideBuffer,
  88. _slideBufStartPos,
  89. finalBlock,
  90. buffer,
  91. offset);
  92. nBytes += finalBlock;
  93. _slideBufStartPos += finalBlock;
  94. } else if (byteCount < AUTH_CODE_LENGTH)
  95. throw new Exception("Internal error missed auth code"); // Coding bug
  96. // Final block done. Check Auth code.
  97. byte[] calcAuthCode = _transform.GetAuthCode();
  98. for (int i = 0; i < AUTH_CODE_LENGTH; i++) {
  99. if (calcAuthCode[i] != _slideBuffer[_slideBufStartPos + i]) {
  100. throw new Exception("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n"
  101. + "The file may be damaged.");
  102. }
  103. }
  104. break; // Reached the auth code
  105. }
  106. }
  107. return nBytes;
  108. }
  109. /// <summary>
  110. /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
  111. /// </summary>
  112. /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream. </param>
  113. /// <param name="offset">The byte offset in buffer at which to begin copying bytes to the current stream. </param>
  114. /// <param name="count">The number of bytes to be written to the current stream. </param>
  115. public override void Write(byte[] buffer, int offset, int count)
  116. {
  117. // ZipAESStream is used for reading but not for writing. Writing uses the ZipAESTransform directly.
  118. throw new NotImplementedException();
  119. }
  120. }
  121. }