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

11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 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. }