diff --git a/shadowsocks/crypto/aead.py b/shadowsocks/crypto/aead.py index 1ca06f2..23c0b36 100644 --- a/shadowsocks/crypto/aead.py +++ b/shadowsocks/crypto/aead.py @@ -14,11 +14,12 @@ from __future__ import absolute_import, division, print_function, \ with_statement -from ctypes import create_string_buffer +from ctypes import c_int, create_string_buffer, byref, c_void_p import hashlib from struct import pack, unpack +from shadowsocks.crypto import util from shadowsocks.crypto import hkdf from shadowsocks.common import ord, chr @@ -42,22 +43,58 @@ CIPHER_NONCE_LEN = { 'aes-128-gcm': 12, 'aes-192-gcm': 12, 'aes-256-gcm': 12, + 'aes-128-ocb': 12, # requires openssl 1.1 + 'aes-192-ocb': 12, + 'aes-256-ocb': 12, 'chacha20-poly1305': 12, 'chacha20-ietf-poly1305': 12, 'xchacha20-ietf-poly1305': 24, + 'sodium:aes-256-gcm': 12, } CIPHER_TAG_LEN = { 'aes-128-gcm': 16, 'aes-192-gcm': 16, 'aes-256-gcm': 16, + 'aes-128-ocb': 16, # requires openssl 1.1 + 'aes-192-ocb': 16, + 'aes-256-ocb': 16, 'chacha20-poly1305': 16, 'chacha20-ietf-poly1305': 16, 'xchacha20-ietf-poly1305': 16, + 'sodium:aes-256-gcm': 16, } SUBKEY_INFO = b"ss-subkey" +libsodium = None +sodium_loaded = False + + +def load_sodium(): + """ + Load libsodium helpers for nonce increment + :return: None + """ + global libsodium, sodium_loaded + + libsodium = util.find_library('sodium', 'sodium_increment', + 'libsodium') + if libsodium is None: + return + + if libsodium.sodium_init() < 0: + libsodium = None + return + + libsodium.sodium_increment.restype = c_void_p + libsodium.sodium_increment.argtypes = ( + c_void_p, c_int + ) + + sodium_loaded = True + return + def nonce_increment(nonce, nlen): """ @@ -119,12 +156,28 @@ class AeadCryptoBase(object): self.encrypt_once = self.aead_encrypt self.decrypt_once = self.aead_decrypt + # load libsodium for nonce increment + if not sodium_loaded: + load_sodium() + + def nonce_increment(self): + """ + AEAD ciphers need nonce to be unique per key + TODO: cache and check unique + :return: None + """ + global libsodium, sodium_loaded + if sodium_loaded: + libsodium.sodium_increment(byref(self._nonce), c_int(self._nlen)) + else: + nonce_increment(self._nonce, self._nlen) + def cipher_ctx_init(self): """ Increase nonce to make it unique for the same key - :return: void + :return: None """ - nonce_increment(self._nonce, self._nlen) + self.nonce_increment() # print("".join("%02x" % ord(b) for b in self._nonce)) def aead_encrypt(self, data): @@ -132,7 +185,7 @@ class AeadCryptoBase(object): Encrypt data with authenticate tag :param data: plain text - :return: cipher text with tag + :return: str [payload][tag] cipher text with tag """ raise Exception("Must implement aead_encrypt method") @@ -141,23 +194,21 @@ class AeadCryptoBase(object): Encrypt a chunk for TCP chunks :param data: str - :return: (str, int) + :return: str [len][tag][payload][tag] """ plen = len(data) - l = AEAD_CHUNK_SIZE_LEN + plen + self._tlen * 2 + # l = AEAD_CHUNK_SIZE_LEN + plen + self._tlen * 2 # network byte order - ctext = self.aead_encrypt(pack("!H", plen & AEAD_CHUNK_SIZE_MASK)) - if len(ctext) != AEAD_CHUNK_SIZE_LEN + self._tlen: + ctext = [self.aead_encrypt(pack("!H", plen & AEAD_CHUNK_SIZE_MASK))] + if len(ctext[0]) != AEAD_CHUNK_SIZE_LEN + self._tlen: + raise Exception("size length invalid") + + ctext.append(self.aead_encrypt(data)) + if len(ctext[1]) != plen + self._tlen: raise Exception("data length invalid") - self.cipher_ctx_init() - ctext += self.aead_encrypt(data) - if len(ctext) != l: - raise Exception("data length invalid") - - self.cipher_ctx_init() - return ctext, l + return b''.join(ctext) def encrypt(self, data): """ @@ -169,25 +220,24 @@ class AeadCryptoBase(object): """ plen = len(data) if plen <= AEAD_CHUNK_SIZE_MASK: - ctext, _ = self.encrypt_chunk(data) + ctext = self.encrypt_chunk(data) return ctext - ctext, clen = b"", 0 + ctext = [] while plen > 0: mlen = plen if plen < AEAD_CHUNK_SIZE_MASK \ else AEAD_CHUNK_SIZE_MASK - r, l = self.encrypt_chunk(data[:mlen]) - ctext += r - clen += l + c = self.encrypt_chunk(data[:mlen]) + ctext.append(c) data = data[mlen:] plen -= mlen - return ctext + return b''.join(ctext) def aead_decrypt(self, data): """ Decrypt data and authenticate tag - :param data: str cipher text with tag + :param data: str [len][tag][payload][tag] cipher text with tag :return: str plain text """ raise Exception("Must implement aead_decrypt method") @@ -196,7 +246,7 @@ class AeadCryptoBase(object): """ Decrypt chunk size - :param data: str encrypted msg + :param data: str [size][tag] encrypted chunk payload len :return: (int, str) msg length and remaining encrypted data """ if self._chunk['mlen'] > 0: @@ -213,7 +263,6 @@ class AeadCryptoBase(object): if plen & AEAD_CHUNK_SIZE_MASK != plen or plen <= 0: raise Exception('Invalid message length') - self.cipher_ctx_init() return plen, data[hlen:] def decrypt_chunk_payload(self, plen, data): @@ -221,7 +270,7 @@ class AeadCryptoBase(object): Decrypted encrypted msg payload :param plen: int payload length - :param data: str encrypted data + :param data: str [payload][tag][[len][tag]....] encrypted data :return: (str, str) plain text and remaining encrypted data """ data = self._chunk['data'] + data @@ -237,15 +286,13 @@ class AeadCryptoBase(object): if len(plaintext) != plen: raise Exception("plaintext length invalid") - self.cipher_ctx_init() - return plaintext, data[plen + self._tlen:] def decrypt_chunk(self, data): """ Decrypt a TCP chunk - :param data: str encrypted msg + :param data: str [len][tag][payload][tag][[len][tag]...] encrypted msg :return: (str, str) decrypted msg and remaining encrypted data """ plen, data = self.decrypt_chunk_size(data) @@ -261,11 +308,13 @@ class AeadCryptoBase(object): :param data: str :return: str """ - ptext, left = self.decrypt_chunk(data) + ptext = [] + pnext, left = self.decrypt_chunk(data) + ptext.append(pnext) while len(left) > 0: pnext, left = self.decrypt_chunk(left) - ptext += pnext - return ptext + ptext.append(pnext) + return b''.join(ptext) def test_nonce_increment(): diff --git a/shadowsocks/crypto/openssl.py b/shadowsocks/crypto/openssl.py index 50cdc22..45e152e 100644 --- a/shadowsocks/crypto/openssl.py +++ b/shadowsocks/crypto/openssl.py @@ -29,14 +29,18 @@ __all__ = ['ciphers'] libcrypto = None loaded = False +libsodium = None +buf = None buf_size = 2048 +ctx_cleanup = None + CIPHER_ENC_UNCHANGED = -1 def load_openssl(): - global loaded, libcrypto, buf, ctx_cleanup + global loaded, libcrypto, libsodium, buf, ctx_cleanup libcrypto = util.find_library(('crypto', 'eay32'), 'EVP_get_cipherbyname', @@ -140,10 +144,13 @@ class OpenSSLAeadCrypto(OpenSSLCryptoBase, AeadCryptoBase): super(OpenSSLAeadCrypto, self).__init__(cipher_name) AeadCryptoBase.__init__(self, cipher_name, key, iv, op) + key_ptr = c_char_p(self._skey) r = libcrypto.EVP_CipherInit_ex( self._ctx, - self._cipher, None, - None, None, c_int(op) + self._cipher, + None, + key_ptr, None, + c_int(op) ) if not r: self.clean() @@ -165,21 +172,19 @@ class OpenSSLAeadCrypto(OpenSSLCryptoBase, AeadCryptoBase): Need init cipher context after EVP_CipherFinal_ex to reuse context :return: void """ - key_ptr = c_char_p(self._skey) iv_ptr = c_char_p(self._nonce.raw) - r = libcrypto.EVP_CipherInit_ex( self._ctx, - None, None, - key_ptr, iv_ptr, + None, + None, + None, iv_ptr, c_int(CIPHER_ENC_UNCHANGED) ) if not r: self.clean() raise Exception('can not initialize cipher context') - nonce_increment(self._nonce, self._nlen) - # print("".join("%02x" % ord(b) for b in self._nonce)) + AeadCryptoBase.nonce_increment(self) def set_tag(self, tag): """ @@ -225,7 +230,7 @@ class OpenSSLAeadCrypto(OpenSSLCryptoBase, AeadCryptoBase): ) if not r: # print(self._nonce.raw, r, cipher_out_len) - raise Exception('Verify data failed') + raise Exception('Finalize cipher failed') return buf.raw[:cipher_out_len.value] def aead_encrypt(self, data): @@ -236,6 +241,7 @@ class OpenSSLAeadCrypto(OpenSSLCryptoBase, AeadCryptoBase): :return: cipher text with tag """ ctext = self.update(data) + self.final() + self.get_tag() + self.cipher_ctx_init() return ctext def aead_decrypt(self, data): @@ -251,6 +257,7 @@ class OpenSSLAeadCrypto(OpenSSLCryptoBase, AeadCryptoBase): self.set_tag(data[clen - self._tlen:]) plaintext = self.update(data[:clen - self._tlen]) + self.final() + self.cipher_ctx_init() return plaintext @@ -305,6 +312,7 @@ ciphers = { def run_method(method): + print(method, ': [stream]', 32) cipher = OpenSSLStreamCrypto(method, b'k' * 32, b'i' * 16, 1) decipher = OpenSSLStreamCrypto(method, b'k' * 32, b'i' * 16, 0) @@ -313,6 +321,13 @@ def run_method(method): def run_aead_method(method, key_len=16): + print(method, ': [payload][tag]', key_len) + cipher = libcrypto.EVP_get_cipherbyname(method) + if not cipher: + cipher = load_cipher(method) + if not cipher: + print('cipher not avaiable, please upgrade openssl') + return key_len = int(key_len) cipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 1) decipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 0) @@ -320,18 +335,46 @@ def run_aead_method(method, key_len=16): util.run_cipher(cipher, decipher) +def run_aead_method_chunk(method, key_len=16): + + print(method, ': chunk([size][tag][payload][tag]', key_len) + cipher = libcrypto.EVP_get_cipherbyname(method) + if not cipher: + cipher = load_cipher(method) + if not cipher: + print('cipher not avaiable, please upgrade openssl') + return + key_len = int(key_len) + cipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 1) + decipher = OpenSSLAeadCrypto(method, b'k' * key_len, b'i' * key_len, 0) + + cipher.encrypt_once = cipher.encrypt + decipher.decrypt_once = decipher.decrypt + util.run_cipher(cipher, decipher) + + def test_aes_128_cfb(): run_method('aes-128-cfb') def test_aes_gcm(bits=128): method = "aes-{0}-gcm".format(bits) - print(method, int(bits / 8)) run_aead_method(method, bits / 8) -def test_aes_256_gcm(): - test_aes_gcm(256) +def test_aes_ocb(bits=128): + method = "aes-{0}-ocb".format(bits) + run_aead_method(method, bits / 8) + + +def test_aes_gcm_chunk(bits=128): + method = "aes-{0}-gcm".format(bits) + run_aead_method_chunk(method, bits / 8) + + +def test_aes_ocb_chunk(bits=128): + method = "aes-{0}-ocb".format(bits) + run_aead_method_chunk(method, bits / 8) def test_aes_256_cfb(): @@ -360,7 +403,16 @@ def test_rc4(): if __name__ == '__main__': test_aes_128_cfb() + test_aes_256_cfb() test_aes_gcm(128) test_aes_gcm(192) test_aes_gcm(256) - test_aes_256_gcm() + test_aes_gcm_chunk(128) + test_aes_gcm_chunk(192) + test_aes_gcm_chunk(256) + test_aes_ocb(128) + test_aes_ocb(192) + test_aes_ocb(256) + test_aes_ocb_chunk(128) + test_aes_ocb_chunk(192) + test_aes_ocb_chunk(256) diff --git a/shadowsocks/crypto/sodium.py b/shadowsocks/crypto/sodium.py index 48a459f..75ea83a 100644 --- a/shadowsocks/crypto/sodium.py +++ b/shadowsocks/crypto/sodium.py @@ -21,6 +21,7 @@ from ctypes import c_char_p, c_int, c_ulonglong, byref, c_ulong, \ create_string_buffer, c_void_p from shadowsocks.crypto import util +from shadowsocks.crypto import aead from shadowsocks.crypto.aead import AeadCryptoBase __all__ = ['ciphers'] @@ -28,6 +29,7 @@ __all__ = ['ciphers'] libsodium = None loaded = False +buf = None buf_size = 2048 # for salsa20 and chacha20 and chacha20-ietf @@ -37,10 +39,20 @@ BLOCK_SIZE = 64 def load_libsodium(): global loaded, libsodium, buf - libsodium = util.find_library('sodium', 'crypto_stream_salsa20_xor_ic', - 'libsodium') - if libsodium is None: - raise Exception('libsodium not found') + if not aead.sodium_loaded: + aead.load_sodium() + + if aead.sodium_loaded: + libsodium = aead.libsodium + else: + print('load libsodium again') + libsodium = util.find_library('sodium', 'crypto_stream_salsa20_xor_ic', + 'libsodium') + if libsodium is None: + raise Exception('libsodium not found') + + if libsodium.sodium_init() < 0: + raise Exception('libsodium init failed') libsodium.crypto_stream_salsa20_xor_ic.restype = c_int libsodium.crypto_stream_salsa20_xor_ic.argtypes = (c_void_p, c_char_p, @@ -116,10 +128,26 @@ def load_libsodium(): c_char_p, c_char_p ) - libsodium.sodium_increment.restype = c_void_p - libsodium.sodium_increment.argtypes = ( - c_void_p, c_int - ) + # aes-256-gcm, same api structure as above + libsodium.crypto_aead_aes256gcm_is_available.restype = c_int + + if libsodium.crypto_aead_aes256gcm_is_available(): + libsodium.crypto_aead_aes256gcm_encrypt.restype = c_int + libsodium.crypto_aead_aes256gcm_encrypt.argtypes = ( + c_void_p, c_void_p, + c_char_p, c_ulonglong, + c_char_p, c_ulonglong, + c_char_p, + c_char_p, c_char_p + ) + libsodium.crypto_aead_aes256gcm_decrypt.restype = c_int + libsodium.crypto_aead_aes256gcm_decrypt.argtypes = ( + c_void_p, c_void_p, + c_char_p, + c_char_p, c_ulonglong, + c_char_p, c_ulonglong, + c_char_p, c_char_p + ) buf = create_string_buffer(buf_size) loaded = True @@ -192,11 +220,15 @@ class SodiumAeadCrypto(AeadCryptoBase): crypto_aead_xchacha20poly1305_ietf_decrypt else: raise Exception('Unknown cipher') + elif cipher_name == 'sodium:aes-256-gcm': + if hasattr(libsodium, 'crypto_aead_aes256gcm_encrypt'): + self.encryptor = libsodium.crypto_aead_aes256gcm_encrypt + self.decryptor = libsodium.crypto_aead_aes256gcm_decrypt else: raise Exception('Unknown cipher') def cipher_ctx_init(self): - + global libsodium libsodium.sodium_increment(byref(self._nonce), c_int(self._nlen)) # print("".join("%02x" % ord(b) for b in self._nonce)) @@ -216,6 +248,7 @@ class SodiumAeadCrypto(AeadCryptoBase): if cipher_out_len.value != plen + self._tlen: raise Exception("Encrypt failed") + self.cipher_ctx_init() return buf.raw[:cipher_out_len.value] def aead_decrypt(self, data): @@ -238,6 +271,7 @@ class SodiumAeadCrypto(AeadCryptoBase): if cipher_out_len.value != clen - self._tlen: raise Exception("Encrypt failed") + self.cipher_ctx_init() return buf.raw[:cipher_out_len.value] @@ -248,10 +282,13 @@ ciphers = { 'chacha20-poly1305': (32, 32, SodiumAeadCrypto), 'chacha20-ietf-poly1305': (32, 32, SodiumAeadCrypto), 'xchacha20-ietf-poly1305': (32, 32, SodiumAeadCrypto), + 'sodium:aes-256-gcm': (32, 32, SodiumAeadCrypto), } def test_salsa20(): + + print("Test salsa20") cipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 1) decipher = SodiumCrypto('salsa20', b'k' * 32, b'i' * 16, 0) @@ -260,6 +297,7 @@ def test_salsa20(): def test_chacha20(): + print("Test chacha20") cipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 1) decipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 0) @@ -268,6 +306,7 @@ def test_chacha20(): def test_chacha20_ietf(): + print("Test chacha20-ietf") cipher = SodiumCrypto('chacha20-ietf', b'k' * 32, b'i' * 16, 1) decipher = SodiumCrypto('chacha20-ietf', b'k' * 32, b'i' * 16, 0) @@ -276,7 +315,7 @@ def test_chacha20_ietf(): def test_chacha20_poly1305(): - print("Test chacha20-poly1305") + print("Test chacha20-poly1305 [payload][tag]") cipher = SodiumAeadCrypto('chacha20-poly1305', b'k' * 32, b'i' * 32, 1) decipher = SodiumAeadCrypto('chacha20-poly1305', @@ -285,9 +324,23 @@ def test_chacha20_poly1305(): util.run_cipher(cipher, decipher) +def test_chacha20_poly1305_chunk(): + + print("Test chacha20-poly1305 chunk [size][tag][payload][tag]") + cipher = SodiumAeadCrypto('chacha20-poly1305', + b'k' * 32, b'i' * 32, 1) + decipher = SodiumAeadCrypto('chacha20-poly1305', + b'k' * 32, b'i' * 32, 0) + + cipher.encrypt_once = cipher.encrypt + decipher.decrypt_once = decipher.decrypt + + util.run_cipher(cipher, decipher) + + def test_chacha20_ietf_poly1305(): - print("Test chacha20-ietf-poly1305") + print("Test chacha20-ietf-poly1305 [payload][tag]") cipher = SodiumAeadCrypto('chacha20-ietf-poly1305', b'k' * 32, b'i' * 32, 1) decipher = SodiumAeadCrypto('chacha20-ietf-poly1305', @@ -296,9 +349,53 @@ def test_chacha20_ietf_poly1305(): util.run_cipher(cipher, decipher) +def test_chacha20_ietf_poly1305_chunk(): + + print("Test chacha20-ietf-poly1305 chunk [size][tag][payload][tag]") + cipher = SodiumAeadCrypto('chacha20-ietf-poly1305', + b'k' * 32, b'i' * 32, 1) + decipher = SodiumAeadCrypto('chacha20-ietf-poly1305', + b'k' * 32, b'i' * 32, 0) + + cipher.encrypt_once = cipher.encrypt + decipher.decrypt_once = decipher.decrypt + + util.run_cipher(cipher, decipher) + + +def test_aes_256_gcm(): + + print("Test sodium:aes-256-gcm [payload][tag]") + cipher = SodiumAeadCrypto('sodium:aes-256-gcm', + b'k' * 32, b'i' * 32, 1) + decipher = SodiumAeadCrypto('sodium:aes-256-gcm', + b'k' * 32, b'i' * 32, 0) + + util.run_cipher(cipher, decipher) + + +def test_aes_256_gcm_chunk(): + + print("Test sodium:aes-256-gcm chunk [size][tag][payload][tag]") + cipher = SodiumAeadCrypto('sodium:aes-256-gcm', + b'k' * 32, b'i' * 32, 1) + decipher = SodiumAeadCrypto('sodium:aes-256-gcm', + b'k' * 32, b'i' * 32, 0) + + cipher.encrypt_once = cipher.encrypt + decipher.decrypt_once = decipher.decrypt + + util.run_cipher(cipher, decipher) + + if __name__ == '__main__': test_chacha20() test_salsa20() test_chacha20_ietf() test_chacha20_poly1305() + test_chacha20_poly1305_chunk() test_chacha20_ietf_poly1305() + test_chacha20_ietf_poly1305_chunk() + test_aes_256_gcm() + test_aes_256_gcm_chunk() + diff --git a/shadowsocks/crypto/util.py b/shadowsocks/crypto/util.py index bb23197..de389b9 100644 --- a/shadowsocks/crypto/util.py +++ b/shadowsocks/crypto/util.py @@ -114,25 +114,27 @@ def run_cipher(cipher, decipher): rounds = 1 * 1024 plain = urandom(block_size * rounds) - results = [] + cipher_results = [] pos = 0 print('test start') start = time.time() while pos < len(plain): l = random.randint(100, 32768) - c = cipher.encrypt(plain[pos:pos + l]) - results.append(c) + # print(pos, l) + c = cipher.encrypt_once(plain[pos:pos + l]) + cipher_results.append(c) pos += l pos = 0 - c = b''.join(results) - results = [] - while pos < len(c): - l = random.randint(100, 32768) - results.append(decipher.decrypt(c[pos:pos + l])) + # c = b''.join(cipher_results) + plain_results = [] + for c in cipher_results: + # l = random.randint(100, 32768) + l = len(c) + plain_results.append(decipher.decrypt_once(c)) pos += l end = time.time() print('speed: %d bytes/s' % (block_size * rounds / (end - start))) - assert b''.join(results) == plain + assert b''.join(plain_results) == plain def test_find_library(): diff --git a/shadowsocks/shell.py b/shadowsocks/shell.py index ce04c1d..1a7322c 100644 --- a/shadowsocks/shell.py +++ b/shadowsocks/shell.py @@ -362,18 +362,21 @@ Proxy options: -l LOCAL_PORT local port, default: 1080 -k PASSWORD password -m METHOD encryption method, default: aes-256-cfb - supported method: - chacha20-poly1305, chacha20-ietf-poly1305, - *xchacha20-ietf-poly1305, - aes-128-gcm, aes-192-gcm, aes-256-gcm, - aes-128-cfb, aes-192-cfb, aes-256-cfb, - es-128-ctr, aes-192-ctr, aes-256-ctr, - camellia-128-cfb, camellia-192-cfb, - camellia-256-cfb, - salsa20, chacha20, chacha20-ietf, - bf-cfb, cast5-cfb, des-cfb, idea-cfb, - rc2-cfb, seed-cfb, - rc4, rc4-md5, table. + Sodium: + chacha20-poly1305, chacha20-ietf-poly1305, + *xchacha20-ietf-poly1305, + sodium:aes-256-gcm, + salsa20, chacha20, chacha20-ietf. + OpenSSL:(* v1.1) + *aes-128-ocb, *aes-192-ocb, *aes-256-ocb, + aes-128-gcm, aes-192-gcm, aes-256-gcm, + aes-128-cfb, aes-192-cfb, aes-256-cfb, + aes-128-ctr, aes-192-ctr, aes-256-ctr, + camellia-128-cfb, camellia-192-cfb, + camellia-256-cfb, + bf-cfb, cast5-cfb, des-cfb, idea-cfb, + rc2-cfb, seed-cfb, + rc4, rc4-md5, table. -t TIMEOUT timeout in seconds, default: 300 -a ONE_TIME_AUTH one time auth --fast-open use TCP_FASTOPEN, requires Linux 3.7+ @@ -404,18 +407,21 @@ Proxy options: -p SERVER_PORT server port, default: 8388 -k PASSWORD password -m METHOD encryption method, default: aes-256-cfb - supported method: - chacha20-poly1305, chacha20-ietf-poly1305, - *xchacha20-ietf-poly1305, - aes-128-gcm, aes-192-gcm, aes-256-gcm, - aes-128-cfb, aes-192-cfb, aes-256-cfb, - es-128-ctr, aes-192-ctr, aes-256-ctr, - camellia-128-cfb, camellia-192-cfb, - camellia-256-cfb, - salsa20, chacha20, chacha20-ietf, - bf-cfb, cast5-cfb, des-cfb, idea-cfb, - rc2-cfb, seed-cfb, - rc4, rc4-md5, table. + Sodium: + chacha20-poly1305, chacha20-ietf-poly1305, + *xchacha20-ietf-poly1305, + sodium:aes-256-gcm, + salsa20, chacha20, chacha20-ietf. + OpenSSL:(* v1.1) + *aes-128-ocb, *aes-192-ocb, *aes-256-ocb, + aes-128-gcm, aes-192-gcm, aes-256-gcm, + aes-128-cfb, aes-192-cfb, aes-256-cfb, + aes-128-ctr, aes-192-ctr, aes-256-ctr, + camellia-128-cfb, camellia-192-cfb, + camellia-256-cfb, + bf-cfb, cast5-cfb, des-cfb, idea-cfb, + rc2-cfb, seed-cfb, + rc4, rc4-md5, table. -t TIMEOUT timeout in seconds, default: 300 -a ONE_TIME_AUTH one time auth --fast-open use TCP_FASTOPEN, requires Linux 3.7+