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