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.

AEADOpenSSLEncryptor.cs 7.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. using System;
  2. using System.Collections.Generic;
  3. using Shadowsocks.Encryption.Exception;
  4. namespace Shadowsocks.Encryption.AEAD
  5. {
  6. public class AEADOpenSSLEncryptor
  7. : AEADEncryptor, IDisposable
  8. {
  9. const int CIPHER_AES = 1;
  10. const int CIPHER_CHACHA20IETFPOLY1305 = 2;
  11. private byte[] _opensslEncSubkey;
  12. private byte[] _opensslDecSubkey;
  13. private IntPtr _encryptCtx = IntPtr.Zero;
  14. private IntPtr _decryptCtx = IntPtr.Zero;
  15. private IntPtr _cipherInfoPtr = IntPtr.Zero;
  16. public AEADOpenSSLEncryptor(string method, string password)
  17. : base(method, password)
  18. {
  19. _opensslEncSubkey = new byte[keyLen];
  20. _opensslDecSubkey = new byte[keyLen];
  21. }
  22. private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
  23. {
  24. {"aes-128-gcm", new EncryptorInfo("aes-128-gcm", 16, 16, 12, 16, CIPHER_AES)},
  25. {"aes-192-gcm", new EncryptorInfo("aes-192-gcm", 24, 24, 12, 16, CIPHER_AES)},
  26. {"aes-256-gcm", new EncryptorInfo("aes-256-gcm", 32, 32, 12, 16, CIPHER_AES)},
  27. {"chacha20-ietf-poly1305", new EncryptorInfo("chacha20-poly1305", 32, 32, 12, 16, CIPHER_CHACHA20IETFPOLY1305)}
  28. };
  29. public static List<string> SupportedCiphers()
  30. {
  31. return new List<string>(_ciphers.Keys);
  32. }
  33. protected override Dictionary<string, EncryptorInfo> getCiphers()
  34. {
  35. return _ciphers;
  36. }
  37. public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
  38. {
  39. base.InitCipher(salt, isEncrypt, isUdp);
  40. _cipherInfoPtr = OpenSSL.GetCipherInfo(_innerLibName);
  41. if (_cipherInfoPtr == IntPtr.Zero) throw new System.Exception("openssl: cipher not found");
  42. IntPtr ctx = OpenSSL.EVP_CIPHER_CTX_new();
  43. if (ctx == IntPtr.Zero) throw new System.Exception("openssl: fail to create ctx");
  44. if (isEncrypt)
  45. {
  46. _encryptCtx = ctx;
  47. }
  48. else
  49. {
  50. _decryptCtx = ctx;
  51. }
  52. DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt, _Masterkey,
  53. isEncrypt ? _opensslEncSubkey : _opensslDecSubkey);
  54. var ret = OpenSSL.EVP_CipherInit_ex(ctx, _cipherInfoPtr, IntPtr.Zero, null, null,
  55. isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
  56. if (ret != 1) throw new System.Exception("openssl: fail to init ctx");
  57. ret = OpenSSL.EVP_CIPHER_CTX_set_key_length(ctx, keyLen);
  58. if (ret != 1) throw new System.Exception("openssl: fail to set key length");
  59. ret = OpenSSL.EVP_CIPHER_CTX_ctrl(ctx, OpenSSL.EVP_CTRL_AEAD_SET_IVLEN,
  60. nonceLen, IntPtr.Zero);
  61. if (ret != 1) throw new System.Exception("openssl: fail to set AEAD nonce length");
  62. ret = OpenSSL.EVP_CipherInit_ex(ctx, IntPtr.Zero, IntPtr.Zero,
  63. isEncrypt ? _opensslEncSubkey : _opensslDecSubkey,
  64. null,
  65. isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
  66. if (ret != 1) throw new System.Exception("openssl: cannot set key");
  67. OpenSSL.EVP_CIPHER_CTX_set_padding(ctx, 0);
  68. }
  69. public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
  70. {
  71. OpenSSL.SetCtxNonce(_encryptCtx, _encNonce, true);
  72. // buf: all plaintext
  73. // outbuf: ciphertext + tag
  74. int ret;
  75. int tmpLen = 0;
  76. clen = 0;
  77. var tagBuf = new byte[tagLen];
  78. ret = OpenSSL.EVP_CipherUpdate(_encryptCtx, ciphertext, out tmpLen,
  79. plaintext, (int) plen);
  80. if (ret != 1) throw new CryptoErrorException("openssl: fail to encrypt AEAD");
  81. clen += (uint) tmpLen;
  82. // For AEAD cipher, it should not output anything
  83. ret = OpenSSL.EVP_CipherFinal_ex(_encryptCtx, ciphertext, ref tmpLen);
  84. if (ret != 1) throw new CryptoErrorException("openssl: fail to finalize AEAD");
  85. if (tmpLen > 0)
  86. {
  87. throw new System.Exception("openssl: fail to finish AEAD");
  88. }
  89. OpenSSL.AEADGetTag(_encryptCtx, tagBuf, tagLen);
  90. Array.Copy(tagBuf, 0, ciphertext, clen, tagLen);
  91. clen += (uint) tagLen;
  92. }
  93. public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
  94. {
  95. OpenSSL.SetCtxNonce(_decryptCtx, _decNonce, false);
  96. // buf: ciphertext + tag
  97. // outbuf: plaintext
  98. int ret;
  99. int tmpLen = 0;
  100. plen = 0;
  101. // split tag
  102. byte[] tagbuf = new byte[tagLen];
  103. Array.Copy(ciphertext, (int) (clen - tagLen), tagbuf, 0, tagLen);
  104. OpenSSL.AEADSetTag(_decryptCtx, tagbuf, tagLen);
  105. ret = OpenSSL.EVP_CipherUpdate(_decryptCtx,
  106. plaintext, out tmpLen, ciphertext, (int) (clen - tagLen));
  107. if (ret != 1) throw new CryptoErrorException("openssl: fail to decrypt AEAD");
  108. plen += (uint) tmpLen;
  109. // For AEAD cipher, it should not output anything
  110. ret = OpenSSL.EVP_CipherFinal_ex(_decryptCtx, plaintext, ref tmpLen);
  111. if (ret <= 0)
  112. {
  113. // If this is not successful authenticated
  114. throw new CryptoErrorException(String.Format("ret is {0}", ret));
  115. }
  116. if (tmpLen > 0)
  117. {
  118. throw new System.Exception("openssl: fail to finish AEAD");
  119. }
  120. }
  121. #region IDisposable
  122. private bool _disposed;
  123. // instance based lock
  124. private readonly object _lock = new object();
  125. public override void Dispose()
  126. {
  127. Dispose(true);
  128. GC.SuppressFinalize(this);
  129. }
  130. ~AEADOpenSSLEncryptor()
  131. {
  132. Dispose(false);
  133. }
  134. protected virtual void Dispose(bool disposing)
  135. {
  136. lock (_lock)
  137. {
  138. if (_disposed) return;
  139. _disposed = true;
  140. }
  141. if (disposing)
  142. {
  143. // free managed objects
  144. }
  145. // free unmanaged objects
  146. if (_encryptCtx != IntPtr.Zero)
  147. {
  148. OpenSSL.EVP_CIPHER_CTX_free(_encryptCtx);
  149. _encryptCtx = IntPtr.Zero;
  150. }
  151. if (_decryptCtx != IntPtr.Zero)
  152. {
  153. OpenSSL.EVP_CIPHER_CTX_free(_decryptCtx);
  154. _decryptCtx = IntPtr.Zero;
  155. }
  156. }
  157. #endregion
  158. }
  159. }