Guest User

Untitled

a guest
Oct 22nd, 2017
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.77 KB | None | 0 0
  1. /*
  2. * Helper class for XTEA en/decryption of arbitrary data.
  3. *
  4. * Copyright (c) 2017, Henrik Heine
  5. */
  6.  
  7. using System;
  8. using System.IO;
  9. using System.Text;
  10.  
  11. namespace InfectedBytes.Security {
  12. /// <summary>
  13. /// Implementation of the "eXtended Tiny Encryption Algorithm".
  14. /// XTEA is a block cipher designed to correct weaknesses in TEA.
  15. /// It is a 64-bit block Feistel cipher with a 128-bit key and suggested 64 rounds.
  16. /// This algorithm is not as secure as AES or TripleDES, but because of it's small footprint it's a good choise for mobile applications.
  17. /// </summary>
  18. // ReSharper disable once InconsistentNaming
  19. public static class XTEA {
  20. /// <summary>
  21. /// The recommended number of rounds is 32 and not 64, because each iteration performs two Feistel-cipher rounds.
  22. /// </summary>
  23. private const uint Rounds = 32;
  24.  
  25. /// <summary>
  26. /// Encrypts the given data with the provided key. The result is Base64 encoded.
  27. /// </summary>
  28. /// <param name="data">The data to encrypt.</param>
  29. /// <param name="key">The key used for encryption.</param>
  30. /// <returns></returns>
  31. public static string Encrypt(string data, string key) {
  32. var dataBytes = Encoding.Unicode.GetBytes(data);
  33. var keyBytes = Encoding.Unicode.GetBytes(key);
  34. var result = Encrypt(dataBytes, keyBytes);
  35. return Convert.ToBase64String(result);
  36. }
  37.  
  38. /// <summary>
  39. /// Decrypts the given data with the provided key.
  40. /// Throws an exception if the length of the data array is not a multiple of 8.
  41. /// Throws an exception if the decrypted length is longer than the actual array.
  42. /// </summary>
  43. /// <param name="data">The encrypted and Base64 encoded data.</param>
  44. /// <param name="key">The key used for decryption.</param>
  45. /// <returns></returns>
  46. public static string Decrypt(string data, string key) {
  47. var dataBytes = Convert.FromBase64String(data);
  48. var keyBytes = Encoding.Unicode.GetBytes(key);
  49. var result = Decrypt(dataBytes, keyBytes);
  50. return Encoding.Unicode.GetString(result);
  51. }
  52.  
  53. /// <summary>
  54. /// Encrypts the given data with the provided key.
  55. /// </summary>
  56. /// <param name="data">The data to encrypt.</param>
  57. /// <param name="key">The key used for encryption.</param>
  58. /// <returns></returns>
  59. public static byte[] Encrypt(byte[] data, byte[] key) {
  60. var keyBuffer = CreateKey(key);
  61. var blockBuffer = new uint[2];
  62. var result = new byte[NextMultipleOf8(data.Length + 4)];
  63. var lengthBuffer = BitConverter.GetBytes(data.Length);
  64. Array.Copy(lengthBuffer, result, lengthBuffer.Length);
  65. Array.Copy(data, 0, result, lengthBuffer.Length, data.Length);
  66. using(var stream = new MemoryStream(result)) {
  67. using(var writer = new BinaryWriter(stream)) {
  68. for(int i = 0; i < result.Length; i += 8) {
  69. blockBuffer[0] = BitConverter.ToUInt32(result, i);
  70. blockBuffer[1] = BitConverter.ToUInt32(result, i + 4);
  71. Encrypt(Rounds, blockBuffer, keyBuffer);
  72. writer.Write(blockBuffer[0]);
  73. writer.Write(blockBuffer[1]);
  74. }
  75. }
  76. }
  77. return result;
  78. }
  79.  
  80. /// <summary>
  81. /// Decrypts the given data with the provided key.
  82. /// Throws an exception if the length of the data array is not a multiple of 8.
  83. /// Throws an exception if the decrypted length is longer than the actual array.
  84. /// </summary>
  85. /// <param name="data">The encrypted data.</param>
  86. /// <param name="key">The key used for decryption.</param>
  87. /// <returns></returns>
  88. public static byte[] Decrypt(byte[] data, byte[] key) {
  89. if(data.Length % 8 != 0) throw new ArgumentException("Encrypted data length must be a multiple of 8 bytes.");
  90. var keyBuffer = CreateKey(key);
  91. var blockBuffer = new uint[2];
  92. var buffer = new byte[data.Length];
  93. Array.Copy(data, buffer, data.Length);
  94. using(var stream = new MemoryStream(buffer)) {
  95. using(var writer = new BinaryWriter(stream)) {
  96. for(int i = 0; i < buffer.Length; i += 8) {
  97. blockBuffer[0] = BitConverter.ToUInt32(buffer, i);
  98. blockBuffer[1] = BitConverter.ToUInt32(buffer, i + 4);
  99. Decrypt(Rounds, blockBuffer, keyBuffer);
  100. writer.Write(blockBuffer[0]);
  101. writer.Write(blockBuffer[1]);
  102. }
  103. }
  104. }
  105. // verify valid length
  106. var length = BitConverter.ToUInt32(buffer, 0);
  107. if(length > buffer.Length - 4) throw new ArgumentException("Invalid encrypted data");
  108. var result = new byte[length];
  109. Array.Copy(buffer, 4, result, 0, length);
  110. return result;
  111. }
  112.  
  113. private static int NextMultipleOf8(int length) {
  114. // XTEA is a 64-bit block chiffre, therefore our data must be a multiple of 64 bit
  115. return (length + 7) / 8 * 8; // this will give us the next multiple of 8
  116. }
  117.  
  118. /// <summary>
  119. /// Transforms an key of arbitrary length to a 128 bit key.
  120. /// </summary>
  121. /// <param name="key"></param>
  122. /// <returns></returns>
  123. private static uint[] CreateKey(byte[] key) {
  124. // It might be a better idea to just calculate the MD5 hash of the key: var hash = MD5.Create().ComputeHash(key);
  125. // But we don't want to depend on the Cryptography namespace, because it would increase the build size for some Unity3d platforms.
  126. var hash = new byte[16];
  127. for(int i = 0; i < key.Length; i++) {
  128. hash[i % 16] = (byte)((31 * hash[i % 16]) ^ key[i]);
  129. }
  130. for(int i = key.Length; i < hash.Length; i++) { // if key was too short
  131. hash[i] = (byte)(17 * i ^ key[i % key.Length]);
  132. }
  133. return new[] {
  134. BitConverter.ToUInt32(hash, 0), BitConverter.ToUInt32(hash, 4),
  135. BitConverter.ToUInt32(hash, 8), BitConverter.ToUInt32(hash, 12)
  136. };
  137. }
  138.  
  139. #region Block Operations
  140. /// <summary>
  141. /// Performs an inplace encryption of the provided data array.
  142. /// </summary>
  143. /// <param name="rounds">The number of encryption rounds, the recommend value is 32.</param>
  144. /// <param name="v">Data array containing two values.</param>
  145. /// <param name="key">Key array containing 4 values.</param>
  146. private static void Encrypt(uint rounds, uint[] v, uint[] key) {
  147. uint v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
  148. for(uint i = 0; i < rounds; i++) {
  149. v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
  150. sum += delta;
  151. v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
  152. }
  153. v[0] = v0;
  154. v[1] = v1;
  155. }
  156.  
  157. /// <summary>
  158. /// Performs an inplace decryption of the provided data array.
  159. /// </summary>
  160. /// <param name="rounds">The number of encryption rounds, the recommend value is 32.</param>
  161. /// <param name="v">Data array containing two values.</param>
  162. /// <param name="key">Key array containing 4 values.</param>
  163. private static void Decrypt(uint rounds, uint[] v, uint[] key) {
  164. uint v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * rounds;
  165. for(uint i = 0; i < rounds; i++) {
  166. v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
  167. sum -= delta;
  168. v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
  169. }
  170. v[0] = v0;
  171. v[1] = v1;
  172. }
  173. #endregion
  174. }
  175. }
Add Comment
Please, Sign In to add comment