RarRijndael.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. #if !NO_CRYPTO
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Security.Cryptography;
  6. using System.Text;
  7. using SharpCompress.Crypto;
  8. namespace SharpCompress.Common.Rar
  9. {
  10. internal class RarRijndael : IDisposable
  11. {
  12. internal const int CRYPTO_BLOCK_SIZE = 16;
  13. private readonly string _password;
  14. private readonly byte[] _salt;
  15. private byte[] _aesInitializationVector;
  16. private RijndaelEngine _rijndael;
  17. private RarRijndael(string password, byte[] salt)
  18. {
  19. _password = password;
  20. _salt = salt;
  21. }
  22. private byte[] ComputeHash(byte[] input)
  23. {
  24. var sha = SHA1.Create();
  25. return sha.ComputeHash(input);
  26. }
  27. private void Initialize()
  28. {
  29. _rijndael = new RijndaelEngine();
  30. _aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE];
  31. int rawLength = 2*_password.Length;
  32. byte[] rawPassword = new byte[rawLength + 8];
  33. byte[] passwordBytes = Encoding.UTF8.GetBytes(_password);
  34. for (int i = 0; i < _password.Length; i++)
  35. {
  36. rawPassword[i*2] = passwordBytes[i];
  37. rawPassword[i*2 + 1] = 0;
  38. }
  39. for (int i = 0; i < _salt.Length; i++)
  40. {
  41. rawPassword[i + rawLength] = _salt[i];
  42. }
  43. const int noOfRounds = (1 << 18);
  44. IList<byte> bytes = new List<byte>();
  45. byte[] digest;
  46. //TODO slow code below, find ways to optimize
  47. for (int i = 0; i < noOfRounds; i++)
  48. {
  49. bytes.AddRange(rawPassword);
  50. bytes.AddRange(new[]
  51. {
  52. (byte) i, (byte) (i >> 8), (byte) (i >> CRYPTO_BLOCK_SIZE)
  53. });
  54. if (i%(noOfRounds/CRYPTO_BLOCK_SIZE) == 0)
  55. {
  56. digest = ComputeHash(bytes.ToArray());
  57. _aesInitializationVector[i/(noOfRounds/CRYPTO_BLOCK_SIZE)] = digest[19];
  58. }
  59. }
  60. digest = ComputeHash(bytes.ToArray());
  61. //slow code ends
  62. byte[] aesKey = new byte[CRYPTO_BLOCK_SIZE];
  63. for (int i = 0; i < 4; i++)
  64. {
  65. for (int j = 0; j < 4; j++)
  66. {
  67. aesKey[i*4 + j] = (byte)
  68. (((digest[i*4]*0x1000000) & 0xff000000 |
  69. (uint) ((digest[i*4 + 1]*0x10000) & 0xff0000) |
  70. (uint) ((digest[i*4 + 2]*0x100) & 0xff00) |
  71. (uint) (digest[i*4 + 3] & 0xff)) >> (j*8));
  72. }
  73. }
  74. _rijndael.Init(false, new KeyParameter(aesKey));
  75. }
  76. public static RarRijndael InitializeFrom(string password, byte[] salt)
  77. {
  78. var rijndael = new RarRijndael(password, salt);
  79. rijndael.Initialize();
  80. return rijndael;
  81. }
  82. public byte[] ProcessBlock(byte[] cipherText)
  83. {
  84. var plainText = new byte[CRYPTO_BLOCK_SIZE];
  85. var decryptedBytes = new List<byte>();
  86. _rijndael.ProcessBlock(cipherText, 0, plainText, 0);
  87. for (int j = 0; j < plainText.Length; j++)
  88. {
  89. decryptedBytes.Add((byte) (plainText[j] ^ _aesInitializationVector[j%16])); //32:114, 33:101
  90. }
  91. for (int j = 0; j < _aesInitializationVector.Length; j++)
  92. {
  93. _aesInitializationVector[j] = cipherText[j];
  94. }
  95. return decryptedBytes.ToArray();
  96. }
  97. public void Dispose()
  98. {
  99. }
  100. }
  101. }
  102. #endif