SSL 1.3 maybe

This commit is contained in:
emdee 2022-11-03 02:51:14 +00:00
parent 3e093a1a41
commit 5ff9bd0680
2 changed files with 255 additions and 39 deletions

View File

@ -13,6 +13,7 @@ import traceback
from time import sleep
from threading import Thread
from random import shuffle
from errno import errorcode
from OpenSSL import SSL
import warnings
@ -38,15 +39,42 @@ import wrapper.toxencryptsave as tox_encrypt_save
global LOG
LOG = logging.getLogger('app.'+'ts')
class SyniToxError(Exception): pass
class SyniToxError(BaseException): pass
NAME = 'SyniTox'
SSL_TOR_RANGE = '172.'
# possible CA locations picks the first one
lCAs = ['/etc/ssl/cacert.pem',
# debian
'/etc/ssl/certs/']
lCAs = [# debian and gentoo
'/etc/ssl/certs/',
]
lCAfs = SSL._CERTIFICATE_FILE_LOCATIONS
# openssl ciphers -s -v|grep 1.3 > /tmp/v1.3
lOPENSSL_13_CIPHERS = ['TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256']
lOPENSSL_12_CIPHERS = ['ECDHE-ECDSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES256-GCM-SHA384',
'DHE-RSA-AES256-GCM-SHA384',
'ECDHE-ECDSA-CHACHA20-POLY1305',
'ECDHE-RSA-CHACHA20-POLY1305',
'DHE-RSA-CHACHA20-POLY1305',
'ECDHE-ECDSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES128-GCM-SHA256',
'DHE-RSA-AES128-GCM-SHA256',
'ECDHE-ECDSA-AES256-SHA384',
'ECDHE-RSA-AES256-SHA384',
'DHE-RSA-AES256-SHA256',
'ECDHE-ECDSA-AES128-SHA256',
'ECDHE-RSA-AES128-SHA256',
'DHE-RSA-AES128-SHA256',
'AES256-GCM-SHA384',
'AES128-GCM-SHA256',
'AES256-SHA256',
'AES128-SHA256'
]
bot_toxname = 'SyniTox'
iSocks5ErrorMax = 5
iSocks5Error = 0
# tox.py can be called by callbacks
def LOG_ERROR(a): print('EROR> '+a)
@ -62,8 +90,8 @@ def LOG_TRACE(a):
if bVERBOSE: print('TRAC> '+a)
# https://wiki.python.org/moin/SSL
def ssl_verify_cb(HOST, override=False):
assert HOST
def ssl_verify_cb(host, override=False):
assert host
# wrapps host
def ssl_verify(*args):
"""
@ -71,28 +99,33 @@ def ssl_verify_cb(HOST, override=False):
should return true if verification passes and false otherwise
"""
LOG.debug(f"ssl_verify {len(args)} {args}")
# app.ts WARNING SSL error: ([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],) # on .onion - fair enough
if override: return True
ssl_conn, x509, error_num, depth, return_code = args
if error_num != 0:
LOG.warn(f"ssl_verify error_num={error_num} {errorcode.get(error_num)}")
return False
if depth != 0:
# don't validate names of root certificates
return True
if x509.get_subject().commonName == HOST:
if x509.get_subject().commonName == host:
return True
LOG.warn(f"ssl_verify {x509.get_subject().commonName} {HOST}")
# allow matching subdomains
have , want = x509.get_subject().commonName, HOST
have , want = x509.get_subject().commonName, host
if len(have.split('.')) == len(want.split('.')) and len(want.split('.')) > 2:
if have.split('.')[1:] == want.split('.')[1:]:
LOG.warn(f"ssl_verify accepting {x509.get_subject().commonName} for {host}")
return True
return False
return ssl_verify
class SyniTox(Tox):
def __init__(self,
@ -183,38 +216,61 @@ class SyniTox(Tox):
def start_ssl(self, HOST):
if not self._ssl_context:
if HOST.endswith('.onion'):
try:
OP_NO_TLSv1_3 = SSL._lib.SSL_OP_NO_TLSv1_3
except AttributeError:
if self._oArgs.irc_ssl == 'tlsv1.3':
LOG.warning("SSL._lib.SSL_OP_NO_TLSv1_3 is not supported")
LOG.warning("Downgrading SSL to tlsv1.2 ")
self._oArgs.irc_ssl = 'tlsv1.2'
else:
LOG.debug("SSL._lib.SSL_OP_NO_TLSv1_3 is not supported")
else:
LOG.debug("SSL._lib.SSL_OP_NO_TLSv1_3 is supported")
if self._oArgs.irc_connect.endswith('.onion') or \
self._oArgs.irc_connect.startswith(SSL_TOR_RANGE):
override = True
else:
override = False
# TLSv1_3_METHOD does not exist
context = SSL.Context(SSL.TLSv1_2_METHOD)
# SSL.OP_NO_TLSv1_1 is allowed
context.set_options(SSL.OP_NO_SSLv2|SSL.OP_NO_SSLv3|SSL.OP_NO_TLSv1)
# this maybe necessary even for a 1.3 site to get the handshake
# in pyOpenSSL - or was it a protocol downgrade attack?
#? context.set_cipher_list("DEFAULT:SECLEVEL=1")
# im getting SSL error: ([('SSL routines', 'tls_construct_client_hello', 'no protocols available')],)
# if I use tlsv1.3 or tlsv1.2 without this on a tlsv1.3 capacble site
if self._oArgs.irc_pem:
key = self._oArgs.irc_pem
assert os.path.exists(key), key
val = SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT
LOG.info('Using keyfile: %s' % self._oArgs.irc_pem)
if False:
if True:
key = self._oArgs.irc_pem.replace('.pem', '.crt')
context.use_certificate_file(key, filetype=SSL.FILETYPE_PEM)
if True:
# key = self._oArgs.irc_pem.replace('.pem', '.key')
key = self._oArgs.irc_pem.replace('.pem', '.key')
assert os.path.exists(key), key
context.use_privatekey_file(key, filetype=SSL.FILETYPE_PEM)
else:
val = SSL.VERIFY_PEER
context.set_verify(val, ssl_verify_cb(HOST, override))
assert os.path.exists(self._oArgs.irc_ca), self._oArgs.irc_ca
if os.path.isdir(self._oArgs.irc_ca):
context.load_verify_locations(capath=self._oArgs.irc_ca)
else:
context.load_verify_locations(cafile=self._oArgs.irc_ca)
if False:
pass
elif self._oArgs.irc_ssl == 'tls1.2':
if self._oArgs.irc_cafile:
# context.load_verify_locations(capath=self._oArgs.irc_ca)
context.load_verify_locations(self._oArgs.irc_cafile, capath=self._oArgs.irc_cadir)
elif self._oArgs.irc_cadir:
context.load_verify_locations(None, capath=self._oArgs.irc_cadir)
if self._oArgs.irc_ssl == 'tlsv1.1':
context.set_min_proto_version(SSL.TLS1_1_VERSION)
elif self._oArgs.irc_ssl == 'tlsv1.2':
context.set_cipher_list(bytes(' '.join(lOPENSSL_12_CIPHERS), 'UTF-8'))
context.set_min_proto_version(SSL.TLS1_2_VERSION)
elif self._oArgs.irc_ssl == 'tls1.3':
elif self._oArgs.irc_ssl == 'tlsv1.3':
#? context.set_cipher_list(bytes(' '.join(lOPENSSL_13_CIPHERS), 'UTF-8'))
context.set_min_proto_version(SSL.TLS1_3_VERSION)
self._ssl_context = context
@ -432,13 +488,35 @@ class SyniTox(Tox):
self.callback_friend_request(None)
self.callback_friend_request(None)
def diagnose_ciphers(self, irc):
cipher_name = irc.get_cipher_name()
LOG.info(f"cipher_name={irc.get_cipher_name()}")
LOG.debug(f"get_cipher_list={irc.get_cipher_list()}")
cipher_list=irc.get_cipher_list()
for ci in lOPENSSL_13_CIPHERS:
if ci in cipher_list: LOG.info(f"server supports v1.3 cipher {ci}")
cipher_name = irc.get_cipher_name()
LOG.info(f"cipher_name={irc.get_cipher_name()}")
if self._oArgs.irc_ssl == 'tlsv1.2':
assert cipher_name in lOPENSSL_12_CIPHERS, cipher_name
elif self._oArgs.irc_ssl == 'tlsv1.3':
assert cipher_name in lOPENSSL_13_CIPHERS, cipher_name
for cert in irc.get_peer_cert_chain():
# x509 objects - just want the /CN
LOG.debug(f"{cert.get_subject()} {cert.get_issuer()}")
assert irc.get_protocol_version_name().lower() == \
self._oArgs.irc_ssl, \
irc.get_protocol_version_name().lower()
def irc_init(self):
global iSocks5Error
if not self.bRouted(): return
nick = self._oArgs.irc_nick
realname = self._oArgs.irc_name
ident = self._oArgs.irc_ident
LOG.info(f"irc_init proxy={self._oArgs.proxy_type}")
LOG.info(f"irc_init proxy={self._oArgs.proxy_type} SSL={self._oArgs.irc_ssl}")
try:
if self._oArgs.proxy_type == 2:
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5,
@ -474,27 +552,37 @@ class SyniTox(Tox):
irc.set_tlsext_host_name(None)
else:
irc.set_tlsext_host_name(bytes(self._oArgs.irc_host, 'UTF-8'))
irc.set_connect_state()
#? irc.set_connect_state()
while True:
try:
irc.do_handshake()
except SSl.WantReadError:
except SSL.WantReadError:
rd,_,_ = select.select([irc], [], [], irc.gettimeout())
if not rd:
raise socket.timeout('timeout')
continue
except SSl.Error as e:
except SSL.Error as e:
raise
break
for cert in irc.get_peer_cert_chain():
print(f"{cert.get_subject} {cert.get_issuer}")
self.diagnose_ciphers(irc)
else:
irc.connect((ip, self._oArgs.irc_port))
LOG.info(f"IRC {'SSL ' if self._oArgs.irc_ssl else ''} connected ")
except wrapper_tests.socks.Socks5Error as e:
if len(e.args[0]) == 2 and e.args[0][0] ==2:
iSocks5Error += 1
if iSocks5Error >= iSocks5ErrorMax:
raise SyniToxError(f"{e.args}")
if len(e.args[0]) == 2 and e.args[0][0] == 2:
LOG.warn(f"Socks5Error: do you have Tor SafeSocks set? {e.args[0]}")
elif len(e.args[0]) == 2 and e.args[0][0] == 5:
# (5, 'Connection refused')
LOG.warn(f"Socks5Error: do you have Tor running? {e.args[0]}")
raise SyniToxError(f"{e.args}")
elif len(e.args[0]) == 2 and e.args[0][0] in [1, 6]:
# (6, 'TTL expired'), 1, ('general SOCKS server failure')
# Missing mapping for virtual address '172.17.140.117'. Refusing.
LOG.warn(f"Socks5Error: {e.args[0]}")
return
else:
LOG.error(f"Socks5Error: {e.args}")
@ -502,16 +590,18 @@ class SyniTox(Tox):
except socket.timeout as e:
LOG.warn(f"socket error: {e.args}")
return
except ( ConnectionRefusedError) as e:
raise SyniToxError(f"{e.args}")
except ( SSL.Error, ) as e:
iSocks5Error += 1
if iSocks5Error >= iSocks5ErrorMax:
raise SyniToxError(f"{e.args}")
LOG.warn(f"SSL error: {e.args}")
return
except (SSL.SysCallError, ) as e:
LOG.warn(f"SSLSyscall error: {e.args}")
LOG.warn(traceback.format_exc())
return
except wrapper_tests.socks.Socks5Error as e:
# (2, 'connection not allowed by ruleset')
raise
except Exception as e:
LOG.warn(f"Error: {e}")
LOG.warn(traceback.format_exc())
@ -524,6 +614,7 @@ class SyniTox(Tox):
self._oArgs.irc_ident,
self._oArgs.irc_host,
self._oArgs.irc_name), 'UTF-8'))
# OSError: [Errno 9] Bad file descriptor
def dht_init(self):
if not self.bRouted(): return
@ -673,7 +764,11 @@ class SyniTox(Tox):
else:
LOG.error("you must provide a password to register")
raise RuntimeError("you must provide a password to register")
try:
self.irc.send(bytes('JOIN %s\r\n' % self._oArgs.irc_chan, 'UTF-8'))
except BrokenPipeError:
raise SyniToxError('BrokenPipeError')
# put off init_groups until you have joined IRC
self.init_groups()
# Make sure we are in
@ -1013,7 +1108,10 @@ def oArgparse(lArgv):
for elt in lCAs:
if os.path.exists(elt):
CAcs.append(elt)
break
CAfs = []
for elt in lCAfs:
if os.path.exists(elt):
CAfs.append(elt)
parser.add_argument('--log_level', type=int, default=10)
parser.add_argument('--bot_name', type=str, default=bot_toxname)
@ -1038,9 +1136,12 @@ def oArgparse(lArgv):
#
parser.add_argument('--irc_ssl', type=str, default='',
help="TLS version; empty is no SSL",
choices=['', 'tls1.2', 'tls1.3'])
parser.add_argument('--irc_ca', type=str,
help="Certificate Authority file or directory",
choices=['', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'])
parser.add_argument('--irc_cafile', type=str,
help="Certificate Authority file",
default=CAfs[0])
parser.add_argument('--irc_cadir', type=str,
help="Certificate Authority directory",
default=CAcs[0])
parser.add_argument('--irc_pem', type=str, default='',
help="Certificate and key as pem; use openssl req -x509 -nodes -newkey rsa:2048")
@ -1100,6 +1201,10 @@ def main(lArgs=None):
assert oTOX_OARGS.irc_host or oTOX_OARGS.irc_connect
if not oTOX_OARGS.irc_connect:
oTOX_OARGS.irc_connect = oTOX_OARGS.irc_host
if oTOX_OARGS.irc_cadir:
assert os.path.isdir(oTOX_OARGS.irc_cadir)
if oTOX_OARGS.irc_cafile:
assert os.path.isfile(oTOX_OARGS.irc_cafile)
global oTOX_OPTIONS
oTOX_OPTIONS = oToxygenToxOptions(oTOX_OARGS)
ts.vSetupLogging(oTOX_OARGS)

111
tox-irc-sync_test.bash Normal file
View File

@ -0,0 +1,111 @@
#!/bin/bash
#export LD_LIBRARY_PATH=/usr/local/lib
#export TOXCORE_LIBS=/mnt/linuxPen19/var/local/src/c-toxcore/_build
export TOXCORE_LIBS=/mnt/o/var/local/src/tox_profile/libs
export PYTHONPATH=/mnt/o/var/local/src/toxygen_wrapper.git/
[ -f /usr/local/bin/usr_local_tput.bash ] && \
. /usr/local/bin/usr_local_tput.bash || {
DBUG() { echo DEBUG $* ; }
INFO() { echo INFO $* ; }
WARN() { echo WARN $* ; }
ERROR() { echo ERROR $* ; }
}
TLS=2
a=`openssl ciphers -s -v|grep -c v1.3`
if [ "$a" -lt 3 ] ; then
WARN no SSSL TLSv1.3 ciphers available to the client.
TLS=2
fi
declare -a RARGS
RARGS=(
--log_level 10
)
[ -n "$socks_proxy" ] && \
RARGS+=(
--proxy_type 2
--proxy_port 9050
--proxy_host 127.0.0.1
)
declare -a LARGS
LARGS=(
--irc_host irc.oftc.net
--irc_port 7000
--irc_ssl ""
--irc_ident SyniTox
--irc_name SyniTox
--irc_nick SyniTox
--irc_pass password
)
DBUG $?
if [ $# -eq 0 -o "$1" = 1 ] ; then
INFO No SSL
python3 tox-irc-sync.py "${LARGS[@]}" "${RARGS[@]}" "$@"
DBUG $?
fi
CIPHER_DOWNGRADE_OVER_TOR="
Nmap scan report for irc.oftc.net (130.239.18.116)
Host is up (0.26s latency).
Other addresses for irc.oftc.net (not scanned): (null)
rDNS record for 130.239.18.116: solenoid.acc.umu.se
PORT STATE SERVICE
6697/tcp open ircs-u
| ssl-enum-ciphers:
| TLSv1.0:
| ciphers:
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 2048) - A
| compressors:
| cipher preference: indeterminate
| cipher preference error: Too few ciphers supported
|_ least strength: A
"
# I know that site does v1.3 3 ciphers
if [ $# -eq 0 -o "$1" = 2 ] ; then
nmap --script ssl-enum-ciphers --proxies socks4://127.0.0.1:9050 -p 6697 irc.oftc.net
# oftcnet6xg6roj6d7id4y4cu6dchysacqj2ldgea73qzdagufflqxrid.onion
# irc.oftc.net
LARGS=(
--irc_host irc.oftc.net
--irc_port 6697
--irc_ssl tlsv1.$TLS
--irc_ident SyniTox
--irc_name SyniTox
--irc_nick SyniTox
--irc_pass password
--irc_pem $HOME/.config/ssl/irc.oftc.net/SyniTox.pem
# E178E7B9BD9E540278118193AD2C84DEF9B35E85
--irc_fp $HOME/.config/ssl/irc.oftc.net/SyniTox.fp
--irc_cadir '/etc/ssl/certs'
--irc_cafile /etc/ssl/cacert.pem
)
DBUG $?
fi
if [ $# -eq 0 -o "$1" = 2 ] ; then
INFO SSL
python3 tox-irc-sync.py "${LARGS[@]}" "${RARGS[@]}" "$@"
fi
ip=oftcnet6xg6roj6d7id4y4cu6dchysacqj2ldgea73qzdagufflqxrid.onion
if [ $# -eq 0 -o "$1" = 3 ] ; then
nmap --script ssl-enum-ciphers --proxies socks4://127.0.0.1:9050 -p 6697 $ip
INFO Onion
python3 tox-irc-sync.py "${LARGS[@]}" --irc_connect $ip "${RARGS[@]}" "$@"
DBUG $?
fi
ip=`tor-resolve -4 $ip`
if [ $? -eq 0 -a -n "$ip" ] && [ $# -eq 0 -o "$1" = 4 ] ; then
nmap --script ssl-enum-ciphers --proxies socks4://127.0.0.1:9050 -p 6697 $ip
INFO IP $ip
python3 tox-irc-sync.py "${LARGS[@]}" --irc_connect $ip "${RARGS[@]}" "$@"
DBUG $?
fi