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 10 kB

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