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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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-cfb", 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. Array.Copy(_key, 0, temp, 0, keyLen);
  66. Array.Copy(iv, 0, temp, keyLen, ivLen);
  67. realkey = MbedTLS.MD5(temp);
  68. }
  69. else
  70. {
  71. realkey = _key;
  72. }
  73. var ret = OpenSSL.EVP_CipherInit_ex(ctx, cipherInfo, IntPtr.Zero, null,null,
  74. isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
  75. if (ret != 1) throw new System.Exception("openssl: fail to set key length");
  76. ret = OpenSSL.EVP_CIPHER_CTX_set_key_length(ctx, keyLen);
  77. if (ret != 1) throw new System.Exception("openssl: fail to set key length");
  78. ret = OpenSSL.EVP_CipherInit_ex(ctx, IntPtr.Zero, IntPtr.Zero, realkey,
  79. _method == "rc4-md5" ? null : iv,
  80. isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
  81. if (ret != 1) throw new System.Exception("openssl: cannot set key and iv");
  82. OpenSSL.EVP_CIPHER_CTX_set_padding(ctx, 0);
  83. }
  84. protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
  85. {
  86. // C# could be multi-threaded
  87. if (_disposed)
  88. {
  89. throw new ObjectDisposedException(this.ToString());
  90. }
  91. int outlen = 0;
  92. var ret = OpenSSL.EVP_CipherUpdate(isEncrypt ? _encryptCtx : _decryptCtx,
  93. outbuf, out outlen, buf, length);
  94. if (ret != 1)
  95. throw new CryptoErrorException(String.Format("ret is {0}", ret));
  96. Debug.Assert(outlen == length);
  97. }
  98. #region IDisposable
  99. private bool _disposed;
  100. // instance based lock
  101. private readonly object _lock = new object();
  102. public override void Dispose()
  103. {
  104. Dispose(true);
  105. GC.SuppressFinalize(this);
  106. }
  107. ~StreamOpenSSLEncryptor()
  108. {
  109. Dispose(false);
  110. }
  111. protected virtual void Dispose(bool disposing)
  112. {
  113. lock (_lock)
  114. {
  115. if (_disposed) return;
  116. _disposed = true;
  117. }
  118. if (disposing)
  119. {
  120. // free managed objects
  121. }
  122. // free unmanaged objects
  123. if (_encryptCtx != IntPtr.Zero)
  124. {
  125. OpenSSL.EVP_CIPHER_CTX_free(_encryptCtx);
  126. _encryptCtx = IntPtr.Zero;
  127. }
  128. if (_decryptCtx != IntPtr.Zero)
  129. {
  130. OpenSSL.EVP_CIPHER_CTX_free(_decryptCtx);
  131. _decryptCtx = IntPtr.Zero;
  132. }
  133. }
  134. #endregion
  135. }
  136. }