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.

StreamChachaNaClCrypto.cs 2.8 kB

5 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using NaCl.Core;
  5. namespace Shadowsocks.Crypto.Stream
  6. {
  7. public class StreamChachaNaClCrypto : StreamCrypto
  8. {
  9. const int BlockSize = 64;
  10. // tcp is stream, which can split into chunks at unexpected position...
  11. // so we need some special handling, as we can't read all data before encrypt
  12. // we did it in AEADEncryptor.cs for AEAD, it can operate at block level
  13. // but we need do it ourselves in stream cipher.
  14. // when new data arrive, put it on correct offset
  15. // and update it, ignore other data, get it in correct offset...
  16. readonly byte[] chachaBuf = new byte[MaxInputSize + BlockSize];
  17. // the 'correct offset', always in 0~BlockSize range, so input data always fit into buffer
  18. int remain = 0;
  19. // increase counter manually...
  20. int ic = 0;
  21. public StreamChachaNaClCrypto(string method, string password) : base(method, password)
  22. {
  23. }
  24. protected override int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher)
  25. {
  26. return CipherUpdate(cipher, plain, false);
  27. }
  28. protected override int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher)
  29. {
  30. return CipherUpdate(plain, cipher, true);
  31. }
  32. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  33. private int CipherUpdate(ReadOnlySpan<byte> i, Span<byte> o, bool enc)
  34. {
  35. // about performance problem:
  36. // as a part of hacking for streaming, we need manually increase IC
  37. // so we need new Chacha20
  38. // and to get correct position, copy paste array everytime is required
  39. // NaCl.Core has no int Encrypt(ReadOnlySpan<byte>,Span<byte>)...
  40. int len = i.Length;
  41. int pad = remain;
  42. i.CopyTo(chachaBuf.AsSpan(pad));
  43. var chacha = new ChaCha20(key, ic);
  44. var p = enc ? chacha.Encrypt(chachaBuf, iv) : chacha.Decrypt(chachaBuf, iv);
  45. p.AsSpan(pad, len).CopyTo(o);
  46. pad += len;
  47. ic += pad / BlockSize;
  48. remain = pad % BlockSize;
  49. return len;
  50. }
  51. #region Cipher Info
  52. private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo>
  53. {
  54. { "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) },
  55. };
  56. public static Dictionary<string, CipherInfo> SupportedCiphers()
  57. {
  58. return _ciphers;
  59. }
  60. protected override Dictionary<string, CipherInfo> GetCiphers()
  61. {
  62. return _ciphers;
  63. }
  64. #endregion
  65. }
  66. }