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.

AEADEncryptor.cs 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. using NLog;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Net;
  6. using System.Text;
  7. using Shadowsocks.Encryption.CircularBuffer;
  8. using Shadowsocks.Controller;
  9. using Shadowsocks.Encryption.Exception;
  10. using Shadowsocks.Encryption.Stream;
  11. namespace Shadowsocks.Encryption.AEAD
  12. {
  13. public abstract class AEADEncryptor : EncryptorBase
  14. {
  15. private static Logger logger = LogManager.GetCurrentClassLogger();
  16. // We are using the same saltLen and keyLen
  17. private const string Info = "ss-subkey";
  18. private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info);
  19. // for UDP only
  20. protected static byte[] _udpTmpBuf = new byte[65536];
  21. // every connection should create its own buffer
  22. private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2);
  23. // private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2);
  24. public const int CHUNK_LEN_BYTES = 2;
  25. public const uint CHUNK_LEN_MASK = 0x3FFFu;
  26. protected Dictionary<string, CipherInfo> ciphers;
  27. protected string _method;
  28. protected CipherFamily _cipher;
  29. protected CipherInfo CipherInfo;
  30. protected static byte[] masterKey = null;
  31. protected byte[] sessionKey;
  32. protected int keyLen;
  33. protected int saltLen;
  34. protected int tagLen;
  35. protected int nonceLen;
  36. protected byte[] salt;
  37. protected object _nonceIncrementLock = new object();
  38. protected byte[] nonce;
  39. // Is first packet
  40. protected bool saltReady;
  41. // Is first chunk(tcp request)
  42. protected bool tcpRequestSent;
  43. public AEADEncryptor(string method, string password)
  44. : base(method, password)
  45. {
  46. InitEncryptorInfo(method);
  47. InitKey(password);
  48. // Initialize all-zero nonce for each connection
  49. nonce = new byte[nonceLen];
  50. nonce = new byte[nonceLen];
  51. }
  52. protected abstract Dictionary<string, CipherInfo> getCiphers();
  53. protected void InitEncryptorInfo(string method)
  54. {
  55. method = method.ToLower();
  56. _method = method;
  57. ciphers = getCiphers();
  58. CipherInfo = ciphers[_method];
  59. _cipher = CipherInfo.Type;
  60. var parameter = (AEADCipherParameter)CipherInfo.CipherParameter;
  61. keyLen = parameter.KeySize;
  62. saltLen = parameter.SaltSize;
  63. tagLen = parameter.TagSize;
  64. nonceLen = parameter.NonceSize;
  65. }
  66. protected void InitKey(string password)
  67. {
  68. byte[] passbuf = Encoding.UTF8.GetBytes(password);
  69. // init master key
  70. if (masterKey == null) masterKey = new byte[keyLen];
  71. if (masterKey.Length != keyLen) Array.Resize(ref masterKey, keyLen);
  72. DeriveKey(passbuf, masterKey, keyLen);
  73. // init session key
  74. if (sessionKey == null) sessionKey = new byte[keyLen];
  75. }
  76. public void DeriveKey(byte[] password, byte[] key, int keylen)
  77. {
  78. StreamEncryptor.LegacyDeriveKey(password, key, keylen);
  79. }
  80. public void DeriveSessionKey(byte[] salt, byte[] masterKey, byte[] sessionKey)
  81. {
  82. CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0);
  83. }
  84. protected void IncrementNonce()
  85. {
  86. CryptoUtils.SodiumIncrement(nonce);
  87. }
  88. public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
  89. {
  90. this.salt = new byte[saltLen];
  91. Array.Copy(salt, this.salt, saltLen);
  92. logger.Dump("Salt", salt, saltLen);
  93. DeriveSessionKey(salt, masterKey, sessionKey);
  94. }
  95. /// <summary>
  96. ///
  97. /// </summary>
  98. /// <param name="plaintext">Input, plain text</param>
  99. /// <param name="plen">plaintext.Length</param>
  100. /// <param name="ciphertext">Output, allocated by caller, tag space included,
  101. /// length = plaintext.Length + tagLen, [enc][tag] order</param>
  102. /// <param name="clen">Should be same as ciphertext.Length</param>
  103. public abstract void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen);
  104. public abstract int CipherEncrypt(Span<byte> plain, Span<byte> cipher);
  105. public abstract int CipherDecrypt(Span<byte> plain, Span<byte> cipher);
  106. // plain -> cipher + tag
  107. [Obsolete]
  108. public abstract byte[] CipherEncrypt2(byte[] plain);
  109. // cipher + tag -> plain
  110. [Obsolete]
  111. public abstract byte[] CipherDecrypt2(byte[] cipher);
  112. public (Memory<byte>, Memory<byte>) GetCipherTextAndTagMem(byte[] cipher)
  113. {
  114. var mc = cipher.AsMemory();
  115. var clen = mc.Length - tagLen;
  116. var c = mc.Slice(0, clen);
  117. var t = mc.Slice(clen);
  118. return (c, t);
  119. }
  120. public (byte[], byte[]) GetCipherTextAndTag(byte[] cipher)
  121. {
  122. var (c, t) = GetCipherTextAndTagMem(cipher);
  123. return (c.ToArray(), t.ToArray());
  124. }
  125. /// <summary>
  126. ///
  127. /// </summary>
  128. /// <param name="ciphertext">Cipher text in [enc][tag] order</param>
  129. /// <param name="clen">ciphertext.Length</param>
  130. /// <param name="plaintext">Output plain text may with additional data allocated by caller</param>
  131. /// <param name="plen">Output, should be used plain text length</param>
  132. public abstract void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen);
  133. #region TCP
  134. public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
  135. {
  136. Debug.Assert(buffer != null, "_encCircularBuffer != null");
  137. buffer.Put(buf, 0, length);
  138. outlength = 0;
  139. logger.Debug("---Start Encryption");
  140. if (!saltReady)
  141. {
  142. saltReady = true;
  143. // Generate salt
  144. byte[] saltBytes = RNG.GetBytes(saltLen);
  145. InitCipher(saltBytes, true, false);
  146. Array.Copy(saltBytes, 0, outbuf, 0, saltLen);
  147. outlength = saltLen;
  148. logger.Debug($"_encryptSaltSent outlength {outlength}");
  149. }
  150. if (!tcpRequestSent)
  151. {
  152. tcpRequestSent = true;
  153. // The first TCP request
  154. byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES];
  155. byte[] addrBytes = buffer.Get(AddressBufferLength);
  156. int encAddrBufLength = ChunkEncrypt(addrBytes, encAddrBufBytes);
  157. // ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength);
  158. Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES);
  159. Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength);
  160. outlength += encAddrBufLength;
  161. logger.Debug($"_tcpRequestSent outlength {outlength}");
  162. }
  163. // handle other chunks
  164. while (true)
  165. {
  166. uint bufSize = (uint)buffer.Size;
  167. if (bufSize <= 0) return;
  168. var chunklength = (int)Math.Min(bufSize, CHUNK_LEN_MASK);
  169. byte[] chunkBytes = buffer.Get(chunklength);
  170. byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + CHUNK_LEN_BYTES];
  171. int encChunkLength = ChunkEncrypt(chunkBytes, encChunkBytes);
  172. // ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength);
  173. Debug.Assert(encChunkLength == chunklength + tagLen * 2 + CHUNK_LEN_BYTES);
  174. Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength);
  175. outlength += encChunkLength;
  176. logger.Debug("chunks enc outlength " + outlength);
  177. // check if we have enough space for outbuf
  178. if (outlength + TCPHandler.ChunkOverheadSize > TCPHandler.BufferSize)
  179. {
  180. logger.Debug("enc outbuf almost full, giving up");
  181. return;
  182. }
  183. bufSize = (uint)buffer.Size;
  184. if (bufSize <= 0)
  185. {
  186. logger.Debug("No more data to encrypt, leaving");
  187. return;
  188. }
  189. }
  190. }
  191. public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
  192. {
  193. Debug.Assert(buffer != null, "_decCircularBuffer != null");
  194. int bufSize;
  195. outlength = 0;
  196. // drop all into buffer
  197. buffer.Put(buf, 0, length);
  198. logger.Debug("---Start Decryption");
  199. if (!saltReady)
  200. {
  201. bufSize = buffer.Size;
  202. // check if we get the leading salt
  203. if (bufSize <= saltLen)
  204. {
  205. // need more
  206. return;
  207. }
  208. saltReady = true;
  209. byte[] salt = buffer.Get(saltLen);
  210. InitCipher(salt, false, false);
  211. logger.Debug("get salt len " + saltLen);
  212. }
  213. // handle chunks
  214. while (true)
  215. {
  216. bufSize = buffer.Size;
  217. // check if we have any data
  218. if (bufSize <= 0)
  219. {
  220. logger.Debug("No data in _decCircularBuffer");
  221. return;
  222. }
  223. // first get chunk length
  224. if (bufSize <= CHUNK_LEN_BYTES + tagLen)
  225. {
  226. // so we only have chunk length and its tag?
  227. return;
  228. }
  229. #region Chunk Decryption
  230. byte[] encLenBytes = buffer.Peek(CHUNK_LEN_BYTES + tagLen);
  231. // try to dec chunk len
  232. byte[] decChunkLenBytes = CipherDecrypt2(encLenBytes);
  233. ushort chunkLen = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(decChunkLenBytes, 0));
  234. if (chunkLen > CHUNK_LEN_MASK)
  235. {
  236. // we get invalid chunk
  237. logger.Error($"Invalid chunk length: {chunkLen}");
  238. throw new CryptoErrorException();
  239. }
  240. logger.Debug("Get the real chunk len:" + chunkLen);
  241. bufSize = buffer.Size;
  242. if (bufSize < CHUNK_LEN_BYTES + tagLen /* we haven't remove them */+ chunkLen + tagLen)
  243. {
  244. logger.Debug("No more data to decrypt one chunk");
  245. return;
  246. }
  247. IncrementNonce();
  248. // we have enough data to decrypt one chunk
  249. // drop chunk len and its tag from buffer
  250. buffer.Skip(CHUNK_LEN_BYTES + tagLen);
  251. byte[] encChunkBytes = buffer.Get(chunkLen + tagLen);
  252. byte[] decChunkBytes = CipherDecrypt2(encChunkBytes);
  253. IncrementNonce();
  254. #endregion
  255. // output to outbuf
  256. decChunkBytes.CopyTo(outbuf, outlength);
  257. // Buffer.BlockCopy(decChunkBytes, 0, outbuf, outlength, (int)decChunkLen);
  258. outlength += decChunkBytes.Length;
  259. logger.Debug("aead dec outlength " + outlength);
  260. if (outlength + 100 > TCPHandler.BufferSize)
  261. {
  262. logger.Debug("dec outbuf almost full, giving up");
  263. return;
  264. }
  265. bufSize = buffer.Size;
  266. // check if we already done all of them
  267. if (bufSize <= 0)
  268. {
  269. logger.Debug("No data in _decCircularBuffer, already all done");
  270. return;
  271. }
  272. }
  273. }
  274. #endregion
  275. #region UDP
  276. /// <summary>
  277. /// Perform AEAD UDP packet encryption
  278. /// </summary>
  279. /// payload => [salt][encrypted payload][tag]
  280. /// <param name="buf"></param>
  281. /// <param name="length"></param>
  282. /// <param name="outbuf"></param>
  283. /// <param name="outlength"></param>
  284. public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength)
  285. {
  286. // Generate salt
  287. //RNG.GetBytes(outbuf, saltLen);
  288. RNG.GetSpan(outbuf.AsSpan().Slice(0, saltLen));
  289. InitCipher(outbuf, true, true);
  290. //uint olen = 0;
  291. lock (_udpTmpBuf)
  292. {
  293. //cipherEncrypt(buf, (uint)length, _udpTmpBuf, ref olen);
  294. var plain = buf.AsSpan().Slice(0, length).ToArray(); // mmp
  295. var cipher = CipherEncrypt2(plain);
  296. //Debug.Assert(olen == length + tagLen);
  297. Buffer.BlockCopy(cipher, 0, outbuf, saltLen, length + tagLen);
  298. //Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, saltLen, (int)olen);
  299. outlength = saltLen + cipher.Length;
  300. }
  301. }
  302. public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength)
  303. {
  304. InitCipher(buf, false, true);
  305. //uint olen = 0;
  306. lock (_udpTmpBuf)
  307. {
  308. // copy remaining data to first pos
  309. Buffer.BlockCopy(buf, saltLen, buf, 0, length - saltLen);
  310. byte[] b = buf.AsSpan().Slice(0, length - saltLen).ToArray();
  311. byte[] o = CipherDecrypt2(b);
  312. //cipherDecrypt(buf, (uint)(length - saltLen), _udpTmpBuf, ref olen);
  313. Buffer.BlockCopy(o, 0, outbuf, 0, o.Length);
  314. outlength = o.Length;
  315. }
  316. }
  317. #endregion
  318. private int ChunkEncrypt(Span<byte> plain, Span<byte> cipher)
  319. {
  320. if (plain.Length > CHUNK_LEN_MASK)
  321. {
  322. logger.Error("enc chunk too big");
  323. throw new CryptoErrorException();
  324. }
  325. byte[] lenbuf = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)plain.Length));
  326. int cipherLenSize = CipherEncrypt(lenbuf, cipher);
  327. IncrementNonce();
  328. int cipherDataSize = CipherEncrypt(plain, cipher.Slice(cipherLenSize));
  329. IncrementNonce();
  330. return cipherLenSize + cipherDataSize;
  331. }
  332. }
  333. }