PkwareTraditionalEncryptionData.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. using System;
  2. using System.Text;
  3. using SharpCompress.Common.Zip.Headers;
  4. using SharpCompress.Compressors.Deflate;
  5. namespace SharpCompress.Common.Zip
  6. {
  7. internal class PkwareTraditionalEncryptionData
  8. {
  9. private static readonly CRC32 CRC32 = new CRC32();
  10. private readonly UInt32[] _keys = {0x12345678, 0x23456789, 0x34567890};
  11. private readonly ArchiveEncoding _archiveEncoding;
  12. private PkwareTraditionalEncryptionData(string password, ArchiveEncoding archiveEncoding)
  13. {
  14. _archiveEncoding = archiveEncoding;
  15. Initialize(password);
  16. }
  17. private byte MagicByte
  18. {
  19. get
  20. {
  21. ushort t = (ushort)((ushort)(_keys[2] & 0xFFFF) | 2);
  22. return (byte)((t * (t ^ 1)) >> 8);
  23. }
  24. }
  25. public static PkwareTraditionalEncryptionData ForRead(string password, ZipFileEntry header,
  26. byte[] encryptionHeader)
  27. {
  28. var encryptor = new PkwareTraditionalEncryptionData(password, header.ArchiveEncoding);
  29. byte[] plainTextHeader = encryptor.Decrypt(encryptionHeader, encryptionHeader.Length);
  30. if (plainTextHeader[11] != (byte)((header.Crc >> 24) & 0xff))
  31. {
  32. if (!FlagUtility.HasFlag(header.Flags, HeaderFlags.UsePostDataDescriptor))
  33. {
  34. throw new CryptographicException("The password did not match.");
  35. }
  36. if (plainTextHeader[11] != (byte)((header.LastModifiedTime >> 8) & 0xff))
  37. {
  38. throw new CryptographicException("The password did not match.");
  39. }
  40. }
  41. return encryptor;
  42. }
  43. public byte[] Decrypt(byte[] cipherText, int length)
  44. {
  45. if (length > cipherText.Length)
  46. {
  47. throw new ArgumentOutOfRangeException(nameof(length),
  48. "Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
  49. }
  50. var plainText = new byte[length];
  51. for (int i = 0; i < length; i++)
  52. {
  53. var c = (byte)(cipherText[i] ^ MagicByte);
  54. UpdateKeys(c);
  55. plainText[i] = c;
  56. }
  57. return plainText;
  58. }
  59. public byte[] Encrypt(byte[] plainText, int length)
  60. {
  61. if (plainText == null)
  62. {
  63. throw new ArgumentNullException("plaintext");
  64. }
  65. if (length > plainText.Length)
  66. {
  67. throw new ArgumentOutOfRangeException(nameof(length),
  68. "Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
  69. }
  70. var cipherText = new byte[length];
  71. for (int i = 0; i < length; i++)
  72. {
  73. byte c = plainText[i];
  74. cipherText[i] = (byte)(plainText[i] ^ MagicByte);
  75. UpdateKeys(c);
  76. }
  77. return cipherText;
  78. }
  79. private void Initialize(string password)
  80. {
  81. byte[] p = StringToByteArray(password);
  82. for (int i = 0; i < password.Length; i++)
  83. {
  84. UpdateKeys(p[i]);
  85. }
  86. }
  87. internal byte[] StringToByteArray(string value)
  88. {
  89. byte[] a = _archiveEncoding.Password.GetBytes(value);
  90. return a;
  91. }
  92. private void UpdateKeys(byte byteValue)
  93. {
  94. _keys[0] = (UInt32)CRC32.ComputeCrc32((int)_keys[0], byteValue);
  95. _keys[1] = _keys[1] + (byte)_keys[0];
  96. _keys[1] = _keys[1] * 0x08088405 + 1;
  97. _keys[2] = (UInt32)CRC32.ComputeCrc32((int)_keys[2], (byte)(_keys[1] >> 24));
  98. }
  99. }
  100. }