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