You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

IVEncryptor.cs 9.8 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Security.Cryptography;
  4. using System.Text;
  5. using System.Net;
  6. namespace Shadowsocks.Encryption
  7. {
  8. public abstract class IVEncryptor
  9. : EncryptorBase
  10. {
  11. public const int MAX_KEY_LENGTH = 64;
  12. public const int MAX_IV_LENGTH = 16;
  13. public const int ONETIMEAUTH_FLAG = 0x10;
  14. public const int ADDRTYPE_MASK = 0xF;
  15. public const int ONETIMEAUTH_BYTES = 16;
  16. public const int ONETIMEAUTH_KEYBYTES = 32;
  17. public const int HASH_BUF_LEN = 128;
  18. public const int HASH_BYTES = 4;
  19. protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
  20. protected Dictionary<string, int[]> ciphers;
  21. private static readonly Dictionary<string, byte[]> CachedKeys = new Dictionary<string, byte[]>();
  22. protected byte[] _encryptIV;
  23. protected byte[] _decryptIV;
  24. protected bool _decryptIVReceived;
  25. protected bool _encryptIVSent;
  26. protected int _encryptIVOffset = 0;
  27. protected int _decryptIVOffset = 0;
  28. protected string _method;
  29. protected int _cipher;
  30. protected int[] _cipherInfo;
  31. protected byte[] _key;
  32. protected int keyLen;
  33. protected int ivLen;
  34. protected byte[] hash_buf;
  35. protected int hash_idx = 0;
  36. public IVEncryptor(string method, string password, bool onetimeauth)
  37. : base(method, password, onetimeauth)
  38. {
  39. InitKey(method, password);
  40. if (OnetimeAuth)
  41. {
  42. hash_buf = new byte[HASH_BUF_LEN];
  43. }
  44. }
  45. protected abstract Dictionary<string, int[]> getCiphers();
  46. protected void InitKey(string method, string password)
  47. {
  48. method = method.ToLower();
  49. _method = method;
  50. string k = method + ":" + password;
  51. ciphers = getCiphers();
  52. _cipherInfo = ciphers[_method];
  53. _cipher = _cipherInfo[2];
  54. if (_cipher == 0)
  55. {
  56. throw new Exception("method not found");
  57. }
  58. keyLen = ciphers[_method][0];
  59. ivLen = ciphers[_method][1];
  60. if (CachedKeys.ContainsKey(k))
  61. {
  62. _key = CachedKeys[k];
  63. }
  64. else
  65. {
  66. byte[] passbuf = Encoding.UTF8.GetBytes(password);
  67. _key = new byte[32];
  68. byte[] iv = new byte[16];
  69. bytesToKey(passbuf, _key);
  70. CachedKeys[k] = _key;
  71. }
  72. }
  73. protected void bytesToKey(byte[] password, byte[] key)
  74. {
  75. byte[] result = new byte[password.Length + 16];
  76. int i = 0;
  77. byte[] md5sum = null;
  78. while (i < key.Length)
  79. {
  80. MD5 md5 = MD5.Create();
  81. if (i == 0)
  82. {
  83. md5sum = md5.ComputeHash(password);
  84. }
  85. else
  86. {
  87. md5sum.CopyTo(result, 0);
  88. password.CopyTo(result, md5sum.Length);
  89. md5sum = md5.ComputeHash(result);
  90. }
  91. md5sum.CopyTo(key, i);
  92. i += md5sum.Length;
  93. }
  94. }
  95. protected static void randBytes(byte[] buf, int length)
  96. {
  97. byte[] temp = new byte[length];
  98. RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider();
  99. rngServiceProvider.GetBytes(temp);
  100. temp.CopyTo(buf, 0);
  101. }
  102. protected virtual void initCipher(byte[] iv, bool isCipher)
  103. {
  104. if (ivLen > 0)
  105. {
  106. if (isCipher)
  107. {
  108. _encryptIV = new byte[ivLen];
  109. Array.Copy(iv, _encryptIV, ivLen);
  110. }
  111. else
  112. {
  113. _decryptIV = new byte[ivLen];
  114. Array.Copy(iv, _decryptIV, ivLen);
  115. }
  116. }
  117. }
  118. protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf);
  119. protected int ss_headlen(byte[] buf, int length)
  120. {
  121. int len = 0;
  122. int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0;
  123. if (atyp == 1)
  124. {
  125. len = 7; // atyp (1 bytes) + ipv4 (4 bytes) + port (2 bytes)
  126. }
  127. else if (atyp == 3 && length > 1)
  128. {
  129. int nameLen = buf[1];
  130. len = 4 + nameLen; // atyp (1 bytes) + name length (1 bytes) + name (n bytes) + port (2 bytes)
  131. }
  132. else if (atyp == 4)
  133. {
  134. len = 19; // atyp (1 bytes) + ipv6 (16 bytes) + port (2 bytes)
  135. }
  136. if (len == 0 || len > length)
  137. throw new Exception($"invalid header with addr type {atyp}");
  138. return len;
  139. }
  140. protected int ss_onetimeauth(byte[] auth,
  141. byte[] msg, int msg_len,
  142. byte[] iv, int iv_len,
  143. byte[] key, int key_len)
  144. {
  145. byte[] auth_key = new byte[ONETIMEAUTH_KEYBYTES];
  146. byte[] auth_bytes = new byte[MAX_IV_LENGTH + MAX_KEY_LENGTH];
  147. Buffer.BlockCopy(iv, 0, auth_bytes, 0, ivLen);
  148. Buffer.BlockCopy(key, 0, auth_bytes, ivLen, key_len);
  149. Sodium.crypto_generichash(auth_key, ONETIMEAUTH_KEYBYTES, auth_bytes, (ulong)(iv_len + key_len), null, 0);
  150. return Sodium.crypto_onetimeauth(auth, msg, (ulong)msg_len, auth_key);
  151. }
  152. protected void ss_gen_hash(byte[] in_buf, ref int in_offset, ref int in_len,
  153. byte[] hash_buf, ref int hash_idx, int buf_size)
  154. {
  155. int i, j;
  156. int offset = in_offset;
  157. int blen = in_len;
  158. int cidx = hash_idx;
  159. int size = (blen / HASH_BUF_LEN + 1) * HASH_BYTES + blen;
  160. if (buf_size < (size + offset))
  161. throw new Exception("failed to generate hash: buffer size insufficient");
  162. byte[] hash = new byte[HASH_BYTES];
  163. for (i = 0, j = offset; i < blen; i++, j++)
  164. {
  165. if (cidx == HASH_BUF_LEN)
  166. {
  167. Sodium.crypto_generichash(hash, HASH_BYTES, hash_buf, HASH_BUF_LEN, null, 0);
  168. Buffer.BlockCopy(in_buf, j, in_buf, j + HASH_BYTES, blen - i);
  169. Buffer.BlockCopy(hash, 0, in_buf, j, HASH_BYTES);
  170. j += HASH_BYTES; cidx = 0;
  171. }
  172. hash_buf[cidx] = in_buf[j];
  173. cidx++;
  174. }
  175. in_offset = j;
  176. in_len = j - offset;
  177. hash_idx = cidx;
  178. }
  179. public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
  180. {
  181. if (!_encryptIVSent)
  182. {
  183. _encryptIVSent = true;
  184. randBytes(outbuf, ivLen);
  185. initCipher(outbuf, true);
  186. outlength = length + ivLen;
  187. lock (tempbuf)
  188. {
  189. if (OnetimeAuth)
  190. {
  191. lock (hash_buf)
  192. {
  193. int headLen = ss_headlen(buf, length);
  194. int data_len = length - headLen;
  195. Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES, data_len);
  196. buf[0] |= ONETIMEAUTH_FLAG;
  197. byte[] auth = new byte[ONETIMEAUTH_BYTES];
  198. ss_onetimeauth(auth, buf, headLen, _encryptIV, ivLen, _key, keyLen);
  199. Buffer.BlockCopy(auth, 0, buf, headLen, ONETIMEAUTH_BYTES);
  200. int buf_offset = headLen + ONETIMEAUTH_BYTES;
  201. ss_gen_hash(buf, ref buf_offset, ref data_len, hash_buf, ref hash_idx, buf.Length);
  202. length = headLen + ONETIMEAUTH_BYTES + data_len;
  203. }
  204. }
  205. cipherUpdate(true, length, buf, tempbuf);
  206. outlength = length + ivLen;
  207. Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length);
  208. }
  209. }
  210. else
  211. {
  212. if (OnetimeAuth)
  213. {
  214. lock (hash_buf)
  215. {
  216. int buf_offset = 0;
  217. ss_gen_hash(buf, ref buf_offset, ref length, hash_buf, ref hash_idx, buf.Length);
  218. }
  219. }
  220. outlength = length;
  221. cipherUpdate(true, length, buf, outbuf);
  222. }
  223. }
  224. public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
  225. {
  226. if (!_decryptIVReceived)
  227. {
  228. _decryptIVReceived = true;
  229. initCipher(buf, false);
  230. outlength = length - ivLen;
  231. lock (tempbuf)
  232. {
  233. // C# could be multi-threaded
  234. Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen);
  235. cipherUpdate(false, length - ivLen, tempbuf, outbuf);
  236. }
  237. }
  238. else
  239. {
  240. outlength = length;
  241. cipherUpdate(false, length, buf, outbuf);
  242. }
  243. }
  244. }
  245. }