Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ matrix:
env:
- TOXENV=py27-linux_non_root,codecov

- os: linux
python: 2.7
env:
- TOXENV=py27-linux_non_root_old-cryptography,codecov

- os: linux
dist: xenial
python: 3.8
Expand Down
14 changes: 1 addition & 13 deletions scapy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,17 +387,6 @@ def isCryptographyValid():
return _version_checker(cryptography, (1, 7))


def isCryptographyRecent():
"""
Check if the cryptography library is recent (2.0 and later)
"""
try:
import cryptography
except ImportError:
return False
return _version_checker(cryptography, (2, 0))


def isCryptographyAdvanced():
"""
Check if the cryptography library is present, and if it supports X25519,
Expand Down Expand Up @@ -651,8 +640,7 @@ class Conf(ConfClass):
'tftp', 'vrrp', 'vxlan', 'x509', 'zigbee']
contribs = dict()
crypto_valid = isCryptographyValid()
crypto_valid_recent = isCryptographyRecent()
crypto_valid_advanced = crypto_valid_recent and isCryptographyAdvanced()
crypto_valid_advanced = isCryptographyAdvanced()
fancy_prompt = True
auto_crop_tables = True
recv_poll_rate = 0.05
Expand Down
47 changes: 43 additions & 4 deletions scapy/layers/tls/automaton_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,19 @@
from scapy.layers.tls.basefields import _tls_version, _tls_version_options
from scapy.layers.tls.session import tlsSession
from scapy.layers.tls.extensions import TLS_Ext_SupportedGroups, \
TLS_Ext_SupportedVersion_CH, TLS_Ext_SignatureAlgorithms
TLS_Ext_SupportedVersion_CH, TLS_Ext_SignatureAlgorithms, \
TLS_Ext_SupportedVersion_SH
from scapy.layers.tls.handshake import TLSCertificate, TLSCertificateRequest, \
TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, \
TLSEncryptedExtensions, TLSFinished, TLSServerHello, TLSServerHelloDone, \
TLSServerKeyExchange, TLS13Certificate, TLS13ClientHello, TLS13ServerHello
TLSServerKeyExchange, TLS13Certificate, TLS13ClientHello, \
TLS13ServerHello, TLS13HelloRetryRequest
from scapy.layers.tls.handshake_sslv2 import SSLv2ClientHello, \
SSLv2ServerHello, SSLv2ClientMasterKey, SSLv2ServerVerify, \
SSLv2ClientFinished, SSLv2ServerFinished, SSLv2ClientCertificate, \
SSLv2RequestCertificate
from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_CH, \
KeyShareEntry
KeyShareEntry, TLS_Ext_KeyShare_HRR
from scapy.layers.tls.record import TLSAlert, TLSChangeCipherSpec, \
TLSApplicationData
from scapy.layers.tls.crypto.suites import _tls_cipher_suites
Expand Down Expand Up @@ -912,14 +914,51 @@ def tls13_should_handle_ServerHello(self):
self.TLS13_HANDLED_SERVERHELLO)

@ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=2)
def tls13_should_handle_HelloRetryRequest(self):
self.raise_on_packet(TLS13HelloRetryRequest,
self.TLS13_HELLO_RETRY_REQUESTED)

@ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=3)
def tls13_should_handle_AlertMessage_(self):
self.raise_on_packet(TLSAlert,
self.CLOSE_NOTIFY)

@ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=3)
@ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=4)
def tls13_missing_ServerHello(self):
raise self.MISSING_SERVERHELLO()

@ATMT.state()
def TLS13_HELLO_RETRY_REQUESTED(self):
pass

@ATMT.condition(TLS13_HELLO_RETRY_REQUESTED)
def tls13_should_add_ClientHello_Retry(self):
s = self.cur_session
s.tls13_retry = True
# we have to use the legacy, plaintext TLS record here
self.add_record(is_tls13=False)
# We retrieve the group to be used and the selected version from the
# previous message
hrr = s.handshake_messages_parsed[-1]
if isinstance(hrr, TLS13HelloRetryRequest):
pass
ciphersuite = hrr.cipher
if hrr.ext:
for e in hrr.ext:
if isinstance(e, TLS_Ext_KeyShare_HRR):
selected_group = e.selected_group
if isinstance(e, TLS_Ext_SupportedVersion_SH):
selected_version = e.version
if not selected_group or not selected_version:
raise self.CLOSE_NOTIFY()
ext = [TLS_Ext_SupportedVersion_CH(versions=[_tls_version[selected_version]]), # noqa: E501
TLS_Ext_SupportedGroups(groups=[_tls_named_groups[selected_group]]), # noqa: E501
TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=selected_group)]), # noqa: E501
TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsaepss"])]
p = TLS13ClientHello(ciphers=ciphersuite, ext=ext)
self.add_msg(p)
raise self.TLS13_ADDED_CLIENTHELLO()

@ATMT.state()
def TLS13_HANDLED_SERVERHELLO(self):
pass
Expand Down
63 changes: 58 additions & 5 deletions scapy/layers/tls/automaton_srv.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@
from scapy.layers.tls.cert import PrivKeyRSA, PrivKeyECDSA
from scapy.layers.tls.basefields import _tls_version
from scapy.layers.tls.session import tlsSession
from scapy.layers.tls.extensions import TLS_Ext_SupportedVersion_SH
from scapy.layers.tls.crypto.groups import _tls_named_groups
from scapy.layers.tls.extensions import TLS_Ext_SupportedVersion_SH, \
TLS_Ext_SupportedGroups, TLS_Ext_Cookie
from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_SH, \
KeyShareEntry
KeyShareEntry, TLS_Ext_KeyShare_HRR
from scapy.layers.tls.handshake import TLSCertificate, TLSCertificateRequest, \
TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, TLSFinished, \
TLSServerHello, TLSServerHelloDone, TLSServerKeyExchange, \
_ASN1CertAndExt, TLS13ServerHello, TLS13Certificate, TLS13ClientHello, \
TLSEncryptedExtensions
TLSEncryptedExtensions, TLS13HelloRetryRequest
from scapy.layers.tls.handshake_sslv2 import SSLv2ClientCertificate, \
SSLv2ClientFinished, SSLv2ClientHello, SSLv2ClientMasterKey, \
SSLv2RequestCertificate, SSLv2ServerFinished, SSLv2ServerHello, \
Expand Down Expand Up @@ -75,6 +77,8 @@ def parse_args(self, server="127.0.0.1", sport=4433,
client_auth=False,
is_echo_server=True,
max_client_idle_time=60,
curve=None,
cookie=False,
**kargs):

super(TLSServerAutomaton, self).parse_args(mycert=mycert,
Expand All @@ -100,6 +104,11 @@ def parse_args(self, server="127.0.0.1", sport=4433,
self.client_auth = client_auth
self.is_echo_server = is_echo_server
self.max_client_idle_time = max_client_idle_time
self.curve = None
self.cookie = cookie
for (group_id, ng) in _tls_named_groups.items():
if ng == curve:
self.curve = group_id

def vprint_sessioninfo(self):
if self.verbose:
Expand Down Expand Up @@ -498,8 +507,52 @@ def SENT_SERVERFLIGHT2(self):
# TLS 1.3 handshake #
@ATMT.state()
def tls13_HANDLED_CLIENTHELLO(self):
s = self.cur_session
m = s.handshake_messages_parsed[-1]
# Check if we have to send an HelloRetryRequest
# XXX check also with non ECC groups
if self.curve:
# We first look for a KeyShareEntry with same group as self.curve
if not _tls_named_groups[self.curve] in s.tls13_client_pubshares:
# We then check if self.curve was advertised in SupportedGroups
# extension
for e in m.ext:
if isinstance(e, TLS_Ext_SupportedGroups):
if self.curve in e.groups:
# Here, we need to send an HelloRetryRequest
raise self.tls13_PREPARE_HELLORETRYREQUEST()
raise self.tls13_PREPARE_SERVERFLIGHT1()

@ATMT.state()
def tls13_PREPARE_HELLORETRYREQUEST(self):
pass

@ATMT.condition(tls13_PREPARE_HELLORETRYREQUEST)
def tls13_should_add_HelloRetryRequest(self):
self.add_record(is_tls13=False)
if isinstance(self.mykey, PrivKeyRSA):
kx = "RSA"
elif isinstance(self.mykey, PrivKeyECDSA):
kx = "ECDSA"
usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx)
c = usable_suites[0]
ext = [TLS_Ext_SupportedVersion_SH(version="TLS 1.3"),
TLS_Ext_KeyShare_HRR(selected_group=_tls_named_groups[self.curve])] # noqa: E501
if self.cookie:
ext += TLS_Ext_Cookie()
p = TLS13HelloRetryRequest(cipher=c, ext=ext)
self.add_msg(p)
self.flush_records()
raise self.tls13_HANDLED_HELLORETRYREQUEST()

@ATMT.state()
def tls13_HANDLED_HELLORETRYREQUEST(self):
pass

@ATMT.condition(tls13_HANDLED_HELLORETRYREQUEST)
def tls13_should_add_ServerHello_from_HRR(self):
raise self.WAITING_CLIENTFLIGHT1()

@ATMT.state()
def tls13_PREPARE_SERVERFLIGHT1(self):
self.add_record(is_tls13=False)
Expand All @@ -513,8 +566,8 @@ def tls13_should_add_ServerHello(self):
usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx)
c = usable_suites[0]
group = next(iter(self.cur_session.tls13_client_pubshares))
ext = [TLS_Ext_SupportedVersion_SH(version="TLS 1.3")]
ext += TLS_Ext_KeyShare_SH(server_share=KeyShareEntry(group=group))
ext = [TLS_Ext_SupportedVersion_SH(version="TLS 1.3"),
TLS_Ext_KeyShare_SH(server_share=KeyShareEntry(group=group))]

if self.cur_session.sid is not None:
p = TLS13ServerHello(cipher=c, sid=self.cur_session.sid, ext=ext)
Expand Down
38 changes: 11 additions & 27 deletions scapy/layers/tls/cert.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, ec
if conf.crypto_valid_recent:
from cryptography.hazmat.backends.openssl.ec import InvalidSignature


Expand Down Expand Up @@ -331,16 +330,11 @@ def encrypt(self, msg, h="sha256", **kwargs):
@crypto_validator
def verify(self, msg, sig, h="sha256", **kwargs):
# 'sig' should be a DER-encoded signature, as per RFC 3279
if conf.crypto_valid_recent:
try:
self.pubkey.verify(sig, msg, ec.ECDSA(_get_hash(h)))
return True
except InvalidSignature:
return False
else:
verifier = self.pubkey.verifier(sig, ec.ECDSA(_get_hash(h)))
verifier.update(msg)
return verifier.verify()
try:
self.pubkey.verify(sig, msg, ec.ECDSA(_get_hash(h)))
return True
except InvalidSignature:
return False


################
Expand Down Expand Up @@ -540,25 +534,15 @@ def import_from_asn1pkt(self, privkey):
@crypto_validator
def verify(self, msg, sig, h="sha256", **kwargs):
# 'sig' should be a DER-encoded signature, as per RFC 3279
if conf.crypto_valid_recent:
try:
self.pubkey.verify(sig, msg, ec.ECDSA(_get_hash(h)))
return True
except InvalidSignature:
return False
else:
verifier = self.pubkey.verifier(sig, ec.ECDSA(_get_hash(h)))
verifier.update(msg)
return verifier.verify()
try:
self.pubkey.verify(sig, msg, ec.ECDSA(_get_hash(h)))
return True
except InvalidSignature:
return False

@crypto_validator
def sign(self, data, h="sha256", **kwargs):
if conf.crypto_valid_recent:
return self.key.sign(data, ec.ECDSA(_get_hash(h)))
else:
signer = self.key.signer(ec.ECDSA(_get_hash(h)))
signer.update(data)
return signer.finalize()
return self.key.sign(data, ec.ECDSA(_get_hash(h)))


################
Expand Down
37 changes: 35 additions & 2 deletions scapy/layers/tls/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from __future__ import print_function

import os
import struct

from scapy.fields import ByteEnumField, ByteField, EnumField, FieldLenField, \
Expand All @@ -25,6 +26,25 @@
from scapy.config import conf


# Because ServerHello and HelloRetryRequest have the same
# msg_type, the only way to distinguish these message is by
# checking the random_bytes. If the random_bytes are equal to
# SHA256('HelloRetryRequest') then we know this is a
# HelloRetryRequest and the TLS_Ext_KeyShare must be parsed as
# TLS_Ext_KeyShare_HRR and not as TLS_Ext_KeyShare_SH

# from cryptography.hazmat.backends import default_backend
# from cryptography.hazmat.primitives import hashes
# digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
# digest.update(b"HelloRetryRequest")
# _tls_hello_retry_magic = digest.finalize()

_tls_hello_retry_magic = (
b'\xcf!\xadt\xe5\x9aa\x11\xbe\x1d\x8c\x02\x1ee\xb8\x91\xc2\xa2\x11'
b'\x16z\xbb\x8c^\x07\x9e\t\xe2\xc8\xa83\x9c'
)


_tls_ext = {0: "server_name", # RFC 4366
1: "max_fragment_length", # RFC 4366
2: "client_certificate_url", # RFC 4366
Expand Down Expand Up @@ -566,6 +586,12 @@ class TLS_Ext_Cookie(TLS_Ext_Unknown):
XStrLenField("cookie", "",
length_from=lambda pkt: pkt.cookielen)]

def build(self):
fval = self.getfieldval("cookie")
if fval is None or fval == b"":
self.cookie = os.urandom(32)
return TLS_Ext_Unknown.build(self)


_tls_psk_kx_modes = {0: "psk_ke", 1: "psk_dhe_ke"}

Expand Down Expand Up @@ -758,8 +784,15 @@ def m2i(self, pkt, m):
# - TLS_Ext_KeyShare_HRR if message is a ServerHello and
# the client has not provided a sufficient "key_share"
# extension
from scapy.layers.tls.keyexchange_tls13 import _tls_ext_keyshare_cls # noqa: E501
cls = _tls_ext_keyshare_cls.get(pkt.msgtype, TLS_Ext_Unknown)
from scapy.layers.tls.keyexchange_tls13 import (
_tls_ext_keyshare_cls, _tls_ext_keyshare_hrr_cls)
# If SHA-256("HelloRetryRequest") == server_random,
# this message is a HelloRetryRequest
if pkt.random_bytes and \
pkt.random_bytes == _tls_hello_retry_magic:
cls = _tls_ext_keyshare_hrr_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501
else:
cls = _tls_ext_keyshare_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501
elif cls is TLS_Ext_PreSharedKey:
from scapy.layers.tls.keyexchange_tls13 import _tls_ext_presharedkey_cls # noqa: E501
cls = _tls_ext_presharedkey_cls.get(pkt.msgtype, TLS_Ext_Unknown) # noqa: E501
Expand Down
Loading