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.

StreamOpenSSLEncryptor.cs 5.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using Shadowsocks.Encryption.Exception;
  5. namespace Shadowsocks.Encryption.Stream
  6. {
  7. public class StreamOpenSSLEncryptor
  8. : StreamEncryptor, IDisposable
  9. {
  10. const int CIPHER_RC4 = 1;
  11. const int CIPHER_AES = 2;
  12. const int CIPHER_CAMELLIA = 3;
  13. const int CIPHER_BLOWFISH = 4;
  14. const int CIPHER_CHACHA20_IETF = 5;
  15. private IntPtr _encryptCtx = IntPtr.Zero;
  16. private IntPtr _decryptCtx = IntPtr.Zero;
  17. public StreamOpenSSLEncryptor(string method, string password)
  18. : base(method, password)
  19. {
  20. }
  21. // XXX: name=RC4,blkSz=1,keyLen=16,ivLen=0, do NOT pass IV to it
  22. private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
  23. {
  24. { "aes-128-cfb", new EncryptorInfo("AES-128-CFB", 16, 16, CIPHER_AES) },
  25. { "aes-192-cfb", new EncryptorInfo("AES-192-CFB", 24, 16, CIPHER_AES) },
  26. { "aes-256-cfb", new EncryptorInfo("AES-256-CFB", 32, 16, CIPHER_AES) },
  27. { "aes-128-ctr", new EncryptorInfo("aes-128-ctr", 16, 16, CIPHER_AES) },
  28. { "aes-192-ctr", new EncryptorInfo("aes-192-ctr", 24, 16, CIPHER_AES) },
  29. { "aes-256-ctr", new EncryptorInfo("aes-256-ctr", 32, 16, CIPHER_AES) },
  30. { "bf-cfb", new EncryptorInfo("bf-cfb64", 16, 8, CIPHER_BLOWFISH) },
  31. { "camellia-128-cfb", new EncryptorInfo("CAMELLIA-128-CFB", 16, 16, CIPHER_CAMELLIA) },
  32. { "camellia-192-cfb", new EncryptorInfo("CAMELLIA-192-CFB", 24, 16, CIPHER_CAMELLIA) },
  33. { "camellia-256-cfb", new EncryptorInfo("CAMELLIA-256-CFB", 32, 16, CIPHER_CAMELLIA) },
  34. { "rc4-md5", new EncryptorInfo("RC4", 16, 16, CIPHER_RC4) },
  35. // it's using ivLen=16, not compatible
  36. //{ "chacha20-ietf", new EncryptorInfo("chacha20", 32, 12, CIPHER_CHACHA20_IETF) }
  37. };
  38. public static List<string> SupportedCiphers()
  39. {
  40. return new List<string>(_ciphers.Keys);
  41. }
  42. protected override Dictionary<string, EncryptorInfo> getCiphers()
  43. {
  44. return _ciphers;
  45. }
  46. protected override void initCipher(byte[] iv, bool isEncrypt)
  47. {
  48. base.initCipher(iv, isEncrypt);
  49. IntPtr cipherInfo = OpenSSL.GetCipherInfo(_innerLibName);
  50. if (cipherInfo == IntPtr.Zero) throw new System.Exception("openssl: cipher not found");
  51. IntPtr ctx = OpenSSL.EVP_CIPHER_CTX_new();
  52. if (ctx == IntPtr.Zero) throw new System.Exception("fail to create ctx");
  53. if (isEncrypt)
  54. {
  55. _encryptCtx = ctx;
  56. }
  57. else
  58. {
  59. _decryptCtx = ctx;
  60. }
  61. byte[] realkey;
  62. if (_method == "rc4-md5")
  63. {
  64. byte[] temp = new byte[keyLen + ivLen];
  65. realkey = new byte[keyLen];
  66. Array.Copy(_key, 0, temp, 0, keyLen);
  67. Array.Copy(iv, 0, temp, keyLen, ivLen);
  68. realkey = MbedTLS.MD5(temp);
  69. }
  70. else
  71. {
  72. realkey = _key;
  73. }
  74. var ret = OpenSSL.EVP_CipherInit_ex(ctx, cipherInfo, IntPtr.Zero, null,null,
  75. isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
  76. if (ret != 1) throw new System.Exception("openssl: fail to set key length");
  77. ret = OpenSSL.EVP_CIPHER_CTX_set_key_length(ctx, keyLen);
  78. if (ret != 1) throw new System.Exception("openssl: fail to set key length");
  79. ret = OpenSSL.EVP_CipherInit_ex(ctx, IntPtr.Zero, IntPtr.Zero, realkey,
  80. _method == "rc4-md5" ? null : iv,
  81. isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
  82. if (ret != 1) throw new System.Exception("openssl: cannot set key and iv");
  83. OpenSSL.EVP_CIPHER_CTX_set_padding(ctx, 0);
  84. }
  85. protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
  86. {
  87. // C# could be multi-threaded
  88. if (_disposed)
  89. {
  90. throw new ObjectDisposedException(this.ToString());
  91. }
  92. int outlen = 0;
  93. var ret = OpenSSL.EVP_CipherUpdate(isEncrypt ? _encryptCtx : _decryptCtx,
  94. outbuf, out outlen, buf, length);
  95. if (ret != 1)
  96. throw new CryptoErrorException(String.Format("ret is {0}", ret));
  97. Debug.Assert(outlen == length);
  98. }
  99. #region IDisposable
  100. private bool _disposed;
  101. // instance based lock
  102. private readonly object _lock = new object();
  103. public override void Dispose()
  104. {
  105. Dispose(true);
  106. GC.SuppressFinalize(this);
  107. }
  108. ~StreamOpenSSLEncryptor()
  109. {
  110. Dispose(false);
  111. }
  112. protected virtual void Dispose(bool disposing)
  113. {
  114. lock (_lock)
  115. {
  116. if (_disposed) return;
  117. _disposed = true;
  118. }
  119. if (disposing)
  120. {
  121. // free managed objects
  122. }
  123. // free unmanaged objects
  124. if (_encryptCtx != IntPtr.Zero)
  125. {
  126. OpenSSL.EVP_CIPHER_CTX_free(_encryptCtx);
  127. _encryptCtx = IntPtr.Zero;
  128. }
  129. if (_decryptCtx != IntPtr.Zero)
  130. {
  131. OpenSSL.EVP_CIPHER_CTX_free(_decryptCtx);
  132. _decryptCtx = IntPtr.Zero;
  133. }
  134. }
  135. #endregion
  136. }
  137. }