Crypter.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Security.Cryptography;
  5. using System.IO;
  6. namespace FastReport.Utils
  7. {
  8. /// <summary>
  9. /// Contains methods used to crypt/decrypt a data.
  10. /// </summary>
  11. public static class Crypter
  12. {
  13. private const string RIJ = "rij";
  14. private static string FDefaultPassword = typeof(Crypter).FullName;
  15. /// <summary>
  16. /// Sets the password that is used to crypt connection strings stored in a report.
  17. /// </summary>
  18. /// <remarks>
  19. /// See the <see cref="FastReport.Data.DataConnectionBase.ConnectionString"/> property for more details.
  20. /// </remarks>
  21. public static string DefaultPassword
  22. {
  23. set { FDefaultPassword = value; }
  24. }
  25. /// <summary>
  26. /// Crypts a stream using specified password.
  27. /// </summary>
  28. /// <param name="dest">The destination stream that will receive the crypted data.</param>
  29. /// <param name="password">The password.</param>
  30. /// <returns>The stream that you need to write to.</returns>
  31. /// <remarks>
  32. /// Pass the stream you need to write to, to the <b>dest</b> parameter. Write your data to the
  33. /// stream that this method returns. When you close this stream, the <b>dest</b> stream will be
  34. /// closed too and contains the crypted data.
  35. /// </remarks>
  36. public static Stream Encrypt(Stream dest, string password)
  37. {
  38. using (Aes aes = Aes.Create())
  39. {
  40. ICryptoTransform encryptor = null;
  41. using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt")))
  42. {
  43. aes.Padding = PaddingMode.ISO10126;
  44. encryptor = aes.CreateEncryptor(pdb.GetBytes(16), pdb.GetBytes(16));
  45. }
  46. // write "rij" signature
  47. dest.Write(new byte[] { 114, 105, 106 }, 0, 3);
  48. return new CryptoStream(dest, encryptor, CryptoStreamMode.Write);
  49. }
  50. }
  51. /// <summary>
  52. /// Decrypts a stream using specified password.
  53. /// </summary>
  54. /// <param name="source">Stream that contains crypted data.</param>
  55. /// <param name="password">The password.</param>
  56. /// <returns>The stream that contains decrypted data.</returns>
  57. /// <remarks>
  58. /// You should read from the stream that this method returns.
  59. /// </remarks>
  60. public static Stream Decrypt(Stream source, string password)
  61. {
  62. var encrypted = IsStreamEncryptedPrivate(source);
  63. if (encrypted)
  64. return DecryptPrivate(source, password);
  65. source.Position -= 3;
  66. return null;
  67. }
  68. private static Stream DecryptPrivate(Stream source, string password)
  69. {
  70. using (Aes aes = Aes.Create())
  71. {
  72. ICryptoTransform decryptor = null;
  73. using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Encoding.UTF8.GetBytes("Salt")))
  74. {
  75. aes.Padding = PaddingMode.ISO10126;
  76. decryptor = aes.CreateDecryptor(pdb.GetBytes(16), pdb.GetBytes(16));
  77. }
  78. return new CryptoStream(source, decryptor, CryptoStreamMode.Read);
  79. }
  80. }
  81. /// <summary>
  82. /// Checks if the stream contains a crypt signature.
  83. /// </summary>
  84. /// <param name="stream">Stream to check.</param>
  85. /// <returns><b>true</b> if stream is crypted.</returns>
  86. public static bool IsStreamEncrypted(Stream stream)
  87. {
  88. var result = IsStreamEncryptedPrivate(stream);
  89. stream.Position -= 3;
  90. return result;
  91. }
  92. private static bool IsStreamEncryptedPrivate(Stream stream)
  93. {
  94. // check "rij" signature
  95. int byte1 = stream.ReadByte();
  96. int byte2 = stream.ReadByte();
  97. int byte3 = stream.ReadByte();
  98. return byte1 == 114 && byte2 == 105 && byte3 == 106;
  99. }
  100. internal static bool IsStringEncrypted(string data)
  101. {
  102. // check "rij" signature
  103. return data.StartsWith(RIJ);
  104. }
  105. /// <summary>
  106. /// Encrypts the string using the default password.
  107. /// </summary>
  108. /// <param name="data">String to encrypt.</param>
  109. /// <returns>The encrypted string.</returns>
  110. /// <remarks>
  111. /// The password used to encrypt a string can be set via <see cref="DefaultPassword"/> property.
  112. /// You also may use the <see cref="EncryptString(string, string)"/> method if you want to
  113. /// specify another password.
  114. /// </remarks>
  115. public static string EncryptString(string data)
  116. {
  117. return EncryptString(data, FDefaultPassword);
  118. }
  119. /// <summary>
  120. /// Encrypts the string using specified password.
  121. /// </summary>
  122. /// <param name="data">String to encrypt.</param>
  123. /// <param name="password">The password.</param>
  124. /// <returns>The encrypted string.</returns>
  125. public static string EncryptString(string data, string password)
  126. {
  127. if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(password))
  128. return data;
  129. using (MemoryStream stream = new MemoryStream())
  130. {
  131. using (Stream cryptedStream = Encrypt(stream, password))
  132. {
  133. byte[] bytes = Encoding.UTF8.GetBytes(data);
  134. cryptedStream.Write(bytes, 0, bytes.Length);
  135. }
  136. return RIJ + Convert.ToBase64String(stream.ToArray());
  137. }
  138. }
  139. /// <summary>
  140. /// Decrypts the string using the default password.
  141. /// </summary>
  142. /// <param name="data">String to decrypt.</param>
  143. /// <returns>The decrypted string.</returns>
  144. /// <remarks>
  145. /// The password used to decrypt a string can be set via <see cref="DefaultPassword"/> property.
  146. /// You also may use the <see cref="DecryptString(string, string)"/> method if you want to
  147. /// specify another password.
  148. /// </remarks>
  149. public static string DecryptString(string data)
  150. {
  151. return DecryptString(data, FDefaultPassword);
  152. }
  153. /// <summary>
  154. /// Decrypts the string using specified password.
  155. /// </summary>
  156. /// <param name="data">String to decrypt.</param>
  157. /// <param name="password">The password.</param>
  158. /// <returns>The decrypted string.</returns>
  159. public static string DecryptString(string data, string password)
  160. {
  161. if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(password) || !IsStringEncrypted(data))
  162. return data;
  163. data = data.Substring(3);
  164. using (Stream stream = Converter.FromString(typeof(Stream), data) as Stream)
  165. {
  166. using (Stream decryptedStream = Decrypt(stream, password))
  167. {
  168. byte[] bytes = new byte[data.Length];
  169. int bytesRead = 0;
  170. while (bytesRead < bytes.Length)
  171. {
  172. int n = decryptedStream.Read(bytes, bytesRead, bytes.Length - bytesRead);
  173. if (n == 0)
  174. break;
  175. bytesRead += n;
  176. }
  177. return Encoding.UTF8.GetString(bytes, 0, bytesRead);
  178. }
  179. }
  180. }
  181. /// <summary>
  182. /// Computes hash of specified stream. Initial position in stream will be saved.
  183. /// </summary>
  184. /// <param name="input">Initial stream</param>
  185. /// <returns></returns>
  186. public static string ComputeHash(Stream input)
  187. {
  188. byte[] buff = new byte[input.Length];
  189. input.Read(buff, 0, buff.Length);
  190. return ComputeHash(buff);
  191. }
  192. /// <summary>
  193. /// Computes hash of specified array.
  194. /// </summary>
  195. /// <param name="input">Initial array</param>
  196. /// <returns></returns>
  197. public static string ComputeHash(byte[] input)
  198. {
  199. byte[] hash = new Murmur3().ComputeHash(input);
  200. return BitConverter.ToString(hash).Replace("-", String.Empty);
  201. }
  202. /// <summary>
  203. /// Computes hash of specified array.
  204. /// </summary>
  205. /// <param name="input">Initial array</param>
  206. /// <returns></returns>
  207. public static string ComputeHash(string input)
  208. {
  209. return ComputeHash(Encoding.UTF8.GetBytes(input));
  210. }
  211. }
  212. /// <summary>
  213. /// MurmurHash is a non-cryptographic hash function suitable for general hash-based lookup.
  214. /// It was created by Austin Appleby in 2008 and is currently hosted on Github along with its test suite named 'SMHasher'.
  215. /// It also exists in a number of variants, all of which have been released into the public domain.
  216. /// The name comes from two basic operations, multiply (MU) and rotate (R), used in its inner loop.
  217. /// https://en.wikipedia.org/wiki/MurmurHash
  218. /// Implementation of Murmur3 Hash by Adam Horvath
  219. /// http://blog.teamleadnet.com/2012/08/murmurhash3-ultra-fast-hash-algorithm.html
  220. /// </summary>
  221. [CLSCompliantAttribute(true)]
  222. public class Murmur3
  223. {
  224. // 128 bit output, 64 bit platform version
  225. /// <summary>
  226. /// READ_SIZE
  227. /// </summary>
  228. [CLSCompliantAttribute(false)]
  229. public static ulong READ_SIZE = 16;
  230. private const ulong C1 = 0x87c37b91114253d5L;
  231. private const ulong C2 = 0x4cf5ad432745937fL;
  232. private ulong length;
  233. private readonly uint seed = 0; // if want to start with a seed, create a constructor
  234. ulong h1;
  235. ulong h2;
  236. private void MixBody(ulong k1, ulong k2)
  237. {
  238. unchecked
  239. {
  240. h1 ^= MixKey1(k1);
  241. h1 = (h1 << 27) | (h1 >> 37);
  242. h1 += h2;
  243. h1 = h1 * 5 + 0x52dce729;
  244. h2 ^= MixKey2(k2);
  245. h2 = (h2 << 31) | (h2 >> 33);
  246. h2 += h1;
  247. h2 = h2 * 5 + 0x38495ab5;
  248. }
  249. }
  250. private static ulong MixKey1(ulong k1)
  251. {
  252. unchecked
  253. {
  254. k1 *= C1;
  255. k1 = (k1 << 31) | (k1 >> 33);
  256. k1 *= C2;
  257. }
  258. return k1;
  259. }
  260. private static ulong MixKey2(ulong k2)
  261. {
  262. unchecked
  263. {
  264. k2 *= C2;
  265. k2 = (k2 << 33) | (k2 >> 31);
  266. k2 *= C1;
  267. }
  268. return k2;
  269. }
  270. private static ulong MixFinal(ulong k)
  271. {
  272. unchecked
  273. {
  274. // avalanche bits
  275. k ^= k >> 33;
  276. k *= 0xff51afd7ed558ccdL;
  277. k ^= k >> 33;
  278. k *= 0xc4ceb9fe1a85ec53L;
  279. k ^= k >> 33;
  280. }
  281. return k;
  282. }
  283. /// <summary>
  284. /// ComputeHash function
  285. /// </summary>
  286. /// <param name="bb"></param>
  287. /// <returns></returns>
  288. public byte[] ComputeHash(byte[] bb)
  289. {
  290. ProcessBytes(bb);
  291. return Hash;
  292. }
  293. private void ProcessBytes(byte[] bb)
  294. {
  295. h1 = seed;
  296. this.length = 0L;
  297. int pos = 0;
  298. int npos;
  299. ulong remaining = (ulong)bb.Length;
  300. // read 128 bits, 16 bytes, 2 longs in eacy cycle
  301. while (remaining >= READ_SIZE) unchecked
  302. {
  303. npos = pos;
  304. ulong k1 = (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
  305. pos += 8;
  306. npos = pos;
  307. ulong k2 = (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
  308. pos += 8;
  309. length += READ_SIZE;
  310. remaining -= READ_SIZE;
  311. MixBody(k1, k2);
  312. }
  313. // if the input MOD 16 != 0
  314. if (remaining > 0)
  315. ProcessBytesRemaining(bb, remaining, pos);
  316. }
  317. private void ProcessBytesRemaining(byte[] bb, ulong remaining, int pos)
  318. {
  319. ulong k1 = 0;
  320. ulong k2 = 0;
  321. length += remaining;
  322. // little endian (x86) processing
  323. unchecked
  324. {
  325. switch (remaining)
  326. {
  327. case 15:
  328. k2 ^= (ulong)bb[pos + 14] << 48; // fall through
  329. goto case 14;
  330. case 14:
  331. k2 ^= (ulong)bb[pos + 13] << 40; // fall through
  332. goto case 13;
  333. case 13:
  334. k2 ^= (ulong)bb[pos + 12] << 32; // fall through
  335. goto case 12;
  336. case 12:
  337. k2 ^= (ulong)bb[pos + 11] << 24; // fall through
  338. goto case 11;
  339. case 11:
  340. k2 ^= (ulong)bb[pos + 10] << 16; // fall through
  341. goto case 10;
  342. case 10:
  343. k2 ^= (ulong)bb[pos + 9] << 8; // fall through
  344. goto case 9;
  345. case 9:
  346. k2 ^= (ulong)bb[pos + 8]; // fall through
  347. goto case 8;
  348. case 8:
  349. int npos = pos;
  350. k1 ^= (uint)(bb[npos++] | bb[npos++] << 8 | bb[npos++] << 16 | bb[npos++] << 24);
  351. break;
  352. case 7:
  353. k1 ^= (ulong)bb[pos + 6] << 48; // fall through
  354. goto case 6;
  355. case 6:
  356. k1 ^= (ulong)bb[pos + 5] << 40; // fall through
  357. goto case 5;
  358. case 5:
  359. k1 ^= (ulong)bb[pos + 4] << 32; // fall through
  360. goto case 4;
  361. case 4:
  362. k1 ^= (ulong)bb[pos + 3] << 24; // fall through
  363. goto case 3;
  364. case 3:
  365. k1 ^= (ulong)bb[pos + 2] << 16; // fall through
  366. goto case 2;
  367. case 2:
  368. k1 ^= (ulong)bb[pos + 1] << 8; // fall through
  369. goto case 1;
  370. case 1:
  371. k1 ^= (ulong)bb[pos]; // fall through
  372. break;
  373. default:
  374. throw new Exception("Something went wrong with remaining bytes calculation.");
  375. }
  376. h1 ^= MixKey1(k1);
  377. h2 ^= MixKey2(k2);
  378. }
  379. }
  380. /// <summary>
  381. /// Gets the Hash
  382. /// </summary>
  383. public byte[] Hash
  384. {
  385. get
  386. {
  387. unchecked
  388. {
  389. h1 ^= length;
  390. h2 ^= length;
  391. h1 += h2;
  392. h2 += h1;
  393. h1 = Murmur3.MixFinal(h1);
  394. h2 = Murmur3.MixFinal(h2);
  395. h1 += h2;
  396. h2 += h1;
  397. }
  398. byte[] hash = new byte[Murmur3.READ_SIZE];
  399. Array.Copy(BitConverter.GetBytes(h1), 0, hash, 0, 8);
  400. Array.Copy(BitConverter.GetBytes(h2), 0, hash, 8, 8);
  401. return hash;
  402. }
  403. }
  404. }
  405. }