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