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.

AeadClient.cs 4.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. using Shadowsocks.Protocol.Shadowsocks.Crypto;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO.Pipelines;
  5. using System.Net;
  6. using System.Runtime.InteropServices;
  7. using System.Security.Cryptography;
  8. using System.Threading.Tasks;
  9. namespace Shadowsocks.Protocol.Shadowsocks
  10. {
  11. public class AeadClient : IStreamClient
  12. {
  13. private CryptoParameter cryptoParameter;
  14. private readonly byte[] mainKey;
  15. /// <summary>
  16. /// ss-subkey
  17. /// </summary>
  18. private static ReadOnlySpan<byte> _ssSubKeyInfo => new byte[]
  19. {
  20. 0x73, 0x73, 0x2d, 0x73, 0x75, 0x62, 0x6b, 0x65, 0x79
  21. };
  22. public AeadClient(CryptoParameter parameter, string password)
  23. {
  24. cryptoParameter = parameter;
  25. mainKey = CryptoUtils.SSKDF(password, parameter.KeySize);
  26. if (!parameter.IsAead)
  27. throw new NotSupportedException($"Unsupported method.");
  28. }
  29. public AeadClient(CryptoParameter parameter, byte[] key)
  30. {
  31. cryptoParameter = parameter;
  32. mainKey = key;
  33. }
  34. public Task Connect(EndPoint destination, IDuplexPipe client, IDuplexPipe server) =>
  35. // destination is ignored, this is just a converter
  36. Task.WhenAll(ConvertUplink(client, server), ConvertDownlink(client, server));
  37. public async Task ConvertUplink(IDuplexPipe client, IDuplexPipe server)
  38. {
  39. using var up = cryptoParameter.GetCrypto();
  40. var pmp = new ProtocolMessagePipe(server);
  41. var salt = new SaltMessage(16, true);
  42. await pmp.WriteAsync(salt);
  43. var key = new byte[cryptoParameter.KeySize];
  44. HKDF.DeriveKey(HashAlgorithmName.SHA1, mainKey, key, salt.Salt.Span, _ssSubKeyInfo);
  45. up.Init(key, null);
  46. Memory<byte> nonce = new byte[cryptoParameter.NonceSize];
  47. nonce.Span.Fill(0);
  48. // TODO write salt with data
  49. while (true)
  50. {
  51. var result = await client.Input.ReadAsync();
  52. if (result.IsCanceled || result.IsCompleted) return;
  53. // TODO compress into one chunk when possible
  54. foreach (var item in result.Buffer)
  55. {
  56. foreach (var i in SplitBigChunk(item))
  57. {
  58. await pmp.WriteAsync(new AeadBlockMessage(up, nonce, cryptoParameter)
  59. {
  60. // in send routine, Data is readonly
  61. Data = MemoryMarshal.AsMemory(i),
  62. });
  63. }
  64. }
  65. client.Input.AdvanceTo(result.Buffer.End);
  66. }
  67. }
  68. public async Task ConvertDownlink(IDuplexPipe client, IDuplexPipe server)
  69. {
  70. using var down = cryptoParameter.GetCrypto();
  71. var pmp = new ProtocolMessagePipe(server);
  72. var salt = await pmp.ReadAsync(new SaltMessage(cryptoParameter.KeySize));
  73. var key = new byte[cryptoParameter.KeySize];
  74. HKDF.DeriveKey(HashAlgorithmName.SHA1, mainKey, key, salt.Salt.Span, _ssSubKeyInfo);
  75. down.Init(key, null);
  76. Memory<byte> nonce = new byte[cryptoParameter.NonceSize];
  77. nonce.Span.Fill(0);
  78. while (true)
  79. {
  80. try
  81. {
  82. var block = await pmp.ReadAsync(new AeadBlockMessage(down, nonce, cryptoParameter));
  83. await client.Output.WriteAsync(block.Data);
  84. client.Output.Advance(block.Data.Length);
  85. }
  86. catch (FormatException)
  87. {
  88. return;
  89. }
  90. }
  91. }
  92. public List<ReadOnlyMemory<byte>> SplitBigChunk(ReadOnlyMemory<byte> mem)
  93. {
  94. var l = new List<ReadOnlyMemory<byte>>(mem.Length / 0x3fff + 1);
  95. while (mem.Length > 0x3fff)
  96. {
  97. l.Add(mem.Slice(0, 0x3fff));
  98. mem = mem.Slice(0x4000);
  99. }
  100. l.Add(mem);
  101. return l;
  102. }
  103. }
  104. }