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 8.1 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 ONETIMEAUTH_FLAG = 0x10;
  12. public const int ADDRTYPE_MASK = 0xF;
  13. public const int ONETIMEAUTH_BYTES = 16;
  14. public const int CRC_BUF_LEN = 128;
  15. public const int CRC_BYTES = 2;
  16. protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
  17. protected Dictionary<string, int[]> ciphers;
  18. private static readonly Dictionary<string, byte[]> CachedKeys = new Dictionary<string, byte[]>();
  19. protected byte[] _encryptIV;
  20. protected byte[] _decryptIV;
  21. protected bool _decryptIVReceived;
  22. protected bool _encryptIVSent;
  23. protected int _encryptIVOffset = 0;
  24. protected int _decryptIVOffset = 0;
  25. protected string _method;
  26. protected int _cipher;
  27. protected int[] _cipherInfo;
  28. protected byte[] _key;
  29. protected int keyLen;
  30. protected int ivLen;
  31. protected byte[] crc_buf;
  32. protected int crc_idx = 0;
  33. public IVEncryptor(string method, string password, bool onetimeauth)
  34. : base(method, password, onetimeauth)
  35. {
  36. InitKey(method, password);
  37. if (OnetimeAuth)
  38. {
  39. crc_buf = new byte[CRC_BUF_LEN];
  40. }
  41. }
  42. protected abstract Dictionary<string, int[]> getCiphers();
  43. protected void InitKey(string method, string password)
  44. {
  45. method = method.ToLower();
  46. _method = method;
  47. string k = method + ":" + password;
  48. ciphers = getCiphers();
  49. _cipherInfo = ciphers[_method];
  50. _cipher = _cipherInfo[2];
  51. if (_cipher == 0)
  52. {
  53. throw new Exception("method not found");
  54. }
  55. keyLen = ciphers[_method][0];
  56. ivLen = ciphers[_method][1];
  57. if (CachedKeys.ContainsKey(k))
  58. {
  59. _key = CachedKeys[k];
  60. }
  61. else
  62. {
  63. byte[] passbuf = Encoding.UTF8.GetBytes(password);
  64. _key = new byte[32];
  65. byte[] iv = new byte[16];
  66. bytesToKey(passbuf, _key);
  67. CachedKeys[k] = _key;
  68. }
  69. }
  70. protected void bytesToKey(byte[] password, byte[] key)
  71. {
  72. byte[] result = new byte[password.Length + 16];
  73. int i = 0;
  74. byte[] md5sum = null;
  75. while (i < key.Length)
  76. {
  77. MD5 md5 = MD5.Create();
  78. if (i == 0)
  79. {
  80. md5sum = md5.ComputeHash(password);
  81. }
  82. else
  83. {
  84. md5sum.CopyTo(result, 0);
  85. password.CopyTo(result, md5sum.Length);
  86. md5sum = md5.ComputeHash(result);
  87. }
  88. md5sum.CopyTo(key, i);
  89. i += md5sum.Length;
  90. }
  91. }
  92. protected static void randBytes(byte[] buf, int length)
  93. {
  94. byte[] temp = new byte[length];
  95. RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider();
  96. rngServiceProvider.GetBytes(temp);
  97. temp.CopyTo(buf, 0);
  98. }
  99. protected virtual void initCipher(byte[] iv, bool isCipher)
  100. {
  101. if (ivLen > 0)
  102. {
  103. if (isCipher)
  104. {
  105. _encryptIV = new byte[ivLen];
  106. Array.Copy(iv, _encryptIV, ivLen);
  107. }
  108. else
  109. {
  110. _decryptIV = new byte[ivLen];
  111. Array.Copy(iv, _decryptIV, ivLen);
  112. }
  113. }
  114. }
  115. protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf);
  116. protected int GetSSHeadLength(byte[] buf, int length)
  117. {
  118. int len = 0;
  119. int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0;
  120. if (atyp == 1)
  121. {
  122. len = 7; // atyp (1 bytes) + ipv4 (4 bytes) + port (2 bytes)
  123. }
  124. else if (atyp == 3 && length > 1)
  125. {
  126. int nameLen = buf[1];
  127. len = 4 + nameLen; // atyp (1 bytes) + name length (1 bytes) + name (n bytes) + port (2 bytes)
  128. }
  129. else if (atyp == 4)
  130. {
  131. len = 19; // atyp (1 bytes) + ipv6 (16 bytes) + port (2 bytes)
  132. }
  133. if (len == 0 || len > length)
  134. throw new Exception($"invalid header with addr type {atyp}");
  135. return len;
  136. }
  137. public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
  138. {
  139. if (!_encryptIVSent)
  140. {
  141. _encryptIVSent = true;
  142. randBytes(outbuf, ivLen);
  143. initCipher(outbuf, true);
  144. outlength = length + ivLen;
  145. lock (tempbuf)
  146. {
  147. if (OnetimeAuth)
  148. {
  149. lock(crc_buf)
  150. {
  151. int headLen = GetSSHeadLength(buf, length);
  152. int data_len = length - headLen;
  153. Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES, data_len);
  154. buf[0] |= ONETIMEAUTH_FLAG;
  155. byte[] auth = new byte[ONETIMEAUTH_BYTES];
  156. Sodium.ss_onetimeauth(auth, buf, headLen, _encryptIV, ivLen, _key, keyLen);
  157. Buffer.BlockCopy(auth, 0, buf, headLen, ONETIMEAUTH_BYTES);
  158. int buf_offset = headLen + ONETIMEAUTH_BYTES;
  159. int rc = Sodium.ss_gen_crc(buf, ref buf_offset, ref data_len, crc_buf, ref crc_idx, buf.Length);
  160. if (rc != 0)
  161. throw new Exception("failed to generate crc");
  162. length = headLen + ONETIMEAUTH_BYTES + data_len;
  163. }
  164. }
  165. cipherUpdate(true, length, buf, tempbuf);
  166. outlength = length + ivLen;
  167. Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length);
  168. }
  169. }
  170. else
  171. {
  172. if (OnetimeAuth)
  173. {
  174. lock(crc_buf)
  175. {
  176. int buf_offset = 0;
  177. int rc = Sodium.ss_gen_crc(buf, ref buf_offset, ref length, crc_buf, ref crc_idx, buf.Length);
  178. if (rc != 0)
  179. throw new Exception("failed to generate crc");
  180. }
  181. }
  182. outlength = length;
  183. cipherUpdate(true, length, buf, outbuf);
  184. }
  185. }
  186. public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
  187. {
  188. if (!_decryptIVReceived)
  189. {
  190. _decryptIVReceived = true;
  191. initCipher(buf, false);
  192. outlength = length - ivLen;
  193. lock (tempbuf)
  194. {
  195. // C# could be multi-threaded
  196. Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen);
  197. cipherUpdate(false, length - ivLen, tempbuf, outbuf);
  198. }
  199. }
  200. else
  201. {
  202. outlength = length;
  203. cipherUpdate(false, length, buf, outbuf);
  204. }
  205. }
  206. }
  207. }