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
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: python
dist: bionic # OpenSSL 1.1.1
cache:
directories:
- $HOME/.cache/pip
Expand All @@ -13,7 +14,7 @@ jobs:
- TOXENV=py27-linux_root,codecov

- os: linux
python: pypy
python: pypy2
env:
- TOXENV=pypy-linux_root,codecov

Expand Down
4 changes: 2 additions & 2 deletions scapy/automaton.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import threading
from scapy.config import conf
from scapy.utils import do_graph
from scapy.error import log_interactive, warning
from scapy.error import log_runtime, warning
from scapy.plist import PacketList
from scapy.data import MTU
from scapy.supersocket import SuperSocket
Expand Down Expand Up @@ -698,7 +698,7 @@ class CommandMessage(AutomatonException):
# Services
def debug(self, lvl, msg):
if self.debug_level >= lvl:
log_interactive.debug(msg)
log_runtime.debug(msg)

def send(self, pkt):
if self.state.state in self.interception_points:
Expand Down
78 changes: 75 additions & 3 deletions scapy/layers/tls/automaton_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
TLSCertificateVerify, TLSClientHello, TLSClientKeyExchange, \
TLSEncryptedExtensions, TLSFinished, TLSServerHello, TLSServerHelloDone, \
TLSServerKeyExchange, TLS13Certificate, TLS13ClientHello, \
TLS13ServerHello, TLS13HelloRetryRequest
TLS13ServerHello, TLS13HelloRetryRequest, TLS13CertificateRequest, \
_ASN1CertAndExt
from scapy.layers.tls.handshake_sslv2 import SSLv2ClientHello, \
SSLv2ServerHello, SSLv2ClientMasterKey, SSLv2ServerVerify, \
SSLv2ClientFinished, SSLv2ServerFinished, SSLv2ClientCertificate, \
Expand Down Expand Up @@ -874,7 +875,11 @@ def tls13_should_add_ClientHello(self):
self.client_hello.ext = ext
p = self.client_hello
else:
p = TLS13ClientHello(ciphers=self.ciphersuite, ext=ext)
if self.ciphersuite is None:
c = 0x1301
else:
c = self.ciphersuite
p = TLS13ClientHello(ciphers=c, ext=ext)
self.add_msg(p)
raise self.TLS13_ADDED_CLIENTHELLO()

Expand Down Expand Up @@ -915,6 +920,10 @@ def tls13_should_handle_ServerHello(self):

@ATMT.condition(TLS13_RECEIVED_SERVERFLIGHT1, prio=2)
def tls13_should_handle_HelloRetryRequest(self):
"""
XXX We should check the ServerHello attributes for discrepancies with
our own ClientHello.
"""
self.raise_on_packet(TLS13HelloRetryRequest,
self.TLS13_HELLO_RETRY_REQUESTED)

Expand Down Expand Up @@ -986,10 +995,27 @@ def tls13_missing_encryptedExtension(self):
def TLS13_HANDLED_ENCRYPTEDEXTENSIONS(self):
pass

@ATMT.condition(TLS13_HANDLED_ENCRYPTEDEXTENSIONS, prio=1)
def tls13_should_handle_certificateRequest_from_encryptedExtensions(self):
"""
XXX We should check the CertificateRequest attributes for discrepancies
with the cipher suite, etc.
"""
self.raise_on_packet(TLS13CertificateRequest,
self.TLS13_HANDLED_CERTIFICATEREQUEST)

@ATMT.condition(TLS13_HANDLED_ENCRYPTEDEXTENSIONS, prio=2)
def tls13_should_handle_certificate_from_encryptedExtensions(self):
self.tls13_should_handle_Certificate()

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

@ATMT.condition(TLS13_HANDLED_CERTIFICATEREQUEST, prio=1)
def tls13_should_handle_Certificate_from_CertificateRequest(self):
return self.tls13_should_handle_Certificate()

def tls13_should_handle_Certificate(self):
self.raise_on_packet(TLS13Certificate,
self.TLS13_HANDLED_CERTIFICATE)
Expand Down Expand Up @@ -1025,7 +1051,53 @@ def TLS13_HANDLED_FINISHED(self):
def TLS13_PREPARE_CLIENTFLIGHT2(self):
self.add_record(is_tls13=True)

@ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2)
@ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2, prio=1)
def tls13_should_add_ClientCertificate(self):
"""
If the server sent a CertificateRequest, we send a Certificate message.
If no certificate is available, an empty Certificate message is sent:
- this is a SHOULD in RFC 4346 (Section 7.4.6)
- this is a MUST in RFC 5246 (Section 7.4.6)

XXX We may want to add a complete chain.
"""
hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
if TLS13CertificateRequest not in hs_msg:
raise self.TLS13_ADDED_CLIENTCERTIFICATE()
# return
certs = []
if self.mycert:
certs += _ASN1CertAndExt(cert=self.mycert)

self.add_msg(TLS13Certificate(certs=certs))
raise self.TLS13_ADDED_CLIENTCERTIFICATE()

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

@ATMT.condition(TLS13_ADDED_CLIENTCERTIFICATE, prio=1)
def tls13_should_add_ClientCertificateVerify(self):
"""
XXX Section 7.4.7.1 of RFC 5246 states that the CertificateVerify
message is only sent following a client certificate that has signing
capability (i.e. not those containing fixed DH params).
We should verify that before adding the message. We should also handle
the case when the Certificate message was empty.
"""
hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
if (TLS13CertificateRequest not in hs_msg or
self.mycert is None or
self.mykey is None):
return self.tls13_should_add_ClientFinished()
self.add_msg(TLSCertificateVerify())
raise self.TLS13_ADDED_CERTIFICATEVERIFY()

@ATMT.state()
def TLS13_ADDED_CERTIFICATEVERIFY(self):
return self.tls13_should_add_ClientFinished()

@ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2, prio=2)
def tls13_should_add_ClientFinished(self):
self.add_msg(TLSFinished())
raise self.TLS13_ADDED_CLIENTFINISHED()
Expand Down
86 changes: 84 additions & 2 deletions scapy/layers/tls/automaton_srv.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@
from scapy.layers.tls.session import tlsSession
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
TLS_Ext_SupportedGroups, TLS_Ext_Cookie, \
TLS_Ext_SignatureAlgorithms
from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_KeyShare_SH, \
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, TLS13HelloRetryRequest
TLSEncryptedExtensions, TLS13HelloRetryRequest, TLS13CertificateRequest
from scapy.layers.tls.handshake_sslv2 import SSLv2ClientCertificate, \
SSLv2ClientFinished, SSLv2ClientHello, SSLv2ClientMasterKey, \
SSLv2RequestCertificate, SSLv2ServerFinished, SSLv2ServerHello, \
Expand Down Expand Up @@ -507,6 +508,10 @@ def SENT_SERVERFLIGHT2(self):
# TLS 1.3 handshake #
@ATMT.state()
def tls13_HANDLED_CLIENTHELLO(self):
"""
Check if we have to send an HelloRetryRequest
XXX check also with non ECC groups
"""
s = self.cur_session
m = s.handshake_messages_parsed[-1]
# Check if we have to send an HelloRetryRequest
Expand Down Expand Up @@ -592,6 +597,10 @@ def tls13_ADDED_ENCRYPTEDEXTENSIONS(self):

@ATMT.condition(tls13_ADDED_ENCRYPTEDEXTENSIONS)
def tls13_should_add_CertificateRequest(self):
if self.client_auth:
ext = [TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsaepss"])]
p = TLS13CertificateRequest(ext=ext)
self.add_msg(p)
raise self.tls13_ADDED_CERTIFICATEREQUEST()

@ATMT.state()
Expand Down Expand Up @@ -632,6 +641,27 @@ def tls13_ADDED_SERVERFINISHED(self):
@ATMT.condition(tls13_ADDED_SERVERFINISHED)
def tls13_should_send_ServerFlight1(self):
self.flush_records()
raise self.tls13_HANDLED_SERVERFLIGHT1()

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

@ATMT.condition(tls13_HANDLED_SERVERFLIGHT1, prio=1)
def tls13_should_handle_ChangeCipherSpec(self):
self.raise_on_packet(TLSChangeCipherSpec,
self.tls13_HANDLED_CHANGECIPHERSPEC)

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

@ATMT.condition(tls13_HANDLED_SERVERFLIGHT1, prio=2)
def tls13_missing_ChangeCipherSpec(self):
raise self.tls13_WAITING_CLIENTFLIGHT2()

@ATMT.condition(tls13_HANDLED_CHANGECIPHERSPEC)
def tls13_should_wait_ClientFlight2(self):
raise self.tls13_WAITING_CLIENTFLIGHT2()

@ATMT.state()
Expand All @@ -644,10 +674,62 @@ def tls13_RECEIVED_CLIENTFLIGHT2(self):
pass

@ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=1)
def tls13_should_handle_ClientFlight2(self):
self.raise_on_packet(TLS13Certificate,
self.TLS13_HANDLED_CLIENTCERTIFICATE)

@ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=2)
def tls13_no_ClientCertificate(self):
if self.client_auth:
raise self.TLS13_MISSING_CLIENTCERTIFICATE()
self.raise_on_packet(TLSFinished,
self.TLS13_HANDLED_CLIENTFINISHED)

# RFC8446, section 4.4.2.4 :
# "If the client does not send any certificates (i.e., it sends an empty
# Certificate message), the server MAY at its discretion either
# continue the handshake without client authentication or abort the
# handshake with a "certificate_required" alert."
# Here, we abort the handshake.
@ATMT.state()
def TLS13_HANDLED_CLIENTCERTIFICATE(self):
if self.client_auth:
self.vprint("Received client certificate chain...")
if isinstance(self.cur_pkt, TLS13Certificate):
if self.cur_pkt.certslen == 0:
self.vprint("but it's empty !")
raise self.TLS13_MISSING_CLIENTCERTIFICATE()

@ATMT.condition(TLS13_HANDLED_CLIENTCERTIFICATE)
def tls13_should_handle_ClientCertificateVerify(self):
self.raise_on_packet(TLSCertificateVerify,
self.TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY)

@ATMT.condition(TLS13_HANDLED_CLIENTCERTIFICATE, prio=2)
def tls13_no_Client_CertificateVerify(self):
if self.client_auth:
raise self.TLS13_MISSING_CLIENTCERTIFICATE()
raise self.TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY()

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

@ATMT.condition(TLS13_HANDLED_CLIENT_CERTIFICATEVERIFY)
def tls13_should_handle_ClientFinished(self):
self.raise_on_packet(TLSFinished,
self.TLS13_HANDLED_CLIENTFINISHED)

@ATMT.state()
def TLS13_MISSING_CLIENTCERTIFICATE(self):
self.vprint("Missing ClientCertificate!")
self.add_record()
self.add_msg(TLSAlert(level=2, descr=0x74))
self.flush_records()
self.vprint("Sending TLSAlert 116")
self.socket.close()
raise self.WAITING_CLIENT()

@ATMT.state()
def TLS13_HANDLED_CLIENTFINISHED(self):
self.vprint("TLS handshake completed!")
Expand Down
6 changes: 5 additions & 1 deletion scapy/layers/tls/handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ def tls_session_update(self, msg_str):

s = self.tls_session
s.advertised_tls_version = self.version
# This ClientHello could be a 1.3 one. Let's store the sid
# in all cases
if self.sidlen and self.sidlen > 0:
s.sid = self.sid
self.random_bytes = msg_str[10:38]
s.client_random = (struct.pack('!I', self.gmt_unix_time) +
self.random_bytes)
Expand All @@ -307,7 +311,6 @@ def tls_session_update(self, msg_str):
for e in self.ext:
if isinstance(e, TLS_Ext_SupportedVersion_CH):
s.advertised_tls_version = e.versions[0]

if isinstance(e, TLS_Ext_SignatureAlgorithms):
s.advertised_sig_algs = e.sig_algs

Expand Down Expand Up @@ -1056,6 +1059,7 @@ class TLS13CertificateRequest(_TLSHandshake):
_ExtensionsField("ext", None,
length_from=lambda pkt: pkt.msglen -
pkt.cert_req_ctxt_len - 3)]

###############################################################################
# ServerHelloDone #
###############################################################################
Expand Down
2 changes: 1 addition & 1 deletion scapy/layers/tls/keyexchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class _TLSSignature(_GenericTLSSessionInheritance):
#XXX 'sig_alg' should be set in __init__ depending on the context.
"""
name = "TLS Digital Signature"
fields_desc = [SigAndHashAlgField("sig_alg", 0x0401, _tls_hash_sig),
fields_desc = [SigAndHashAlgField("sig_alg", 0x0804, _tls_hash_sig),
SigLenField("sig_len", None, fmt="!H",
length_of="sig_val"),
SigValField("sig_val", None,
Expand Down
2 changes: 2 additions & 0 deletions scapy/layers/tls/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ def getfield(self, pkt, s):
if (((pkt.tls_session.tls_version or 0x0303) > 0x0200) and
hasattr(pkt, "type") and pkt.type == 23):
return ret, [TLSApplicationData(data=b"")]
elif hasattr(pkt, "type") and pkt.type == 20:
return ret, [TLSChangeCipherSpec()]
else:
return ret, [Raw(load=b"")]

Expand Down
14 changes: 14 additions & 0 deletions scapy/layers/tls/record_tls13.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,30 @@ def pre_dissect(self, s):
return s


class TLSInnerChangeCipherSpec(_GenericTLSSessionInheritance):
__slots__ = ["type"]
name = "TLS Inner Plaintext (CCS)"
fields_desc = [_TLSMsgListField("msg", [], length_from=lambda x: 1)]

def __init__(self, _pkt=None, *args, **kwargs):
self.type = 0x14
super(TLSInnerChangeCipherSpec, self).__init__(_pkt, *args, **kwargs)


class _TLSInnerPlaintextField(PacketField):
def __init__(self, name, default, *args, **kargs):
super(_TLSInnerPlaintextField, self).__init__(name,
default,
TLSInnerPlaintext)

def m2i(self, pkt, m):
if pkt.type == 0x14:
return TLSInnerChangeCipherSpec(m, tls_session=pkt.tls_session)
return self.cls(m, tls_session=pkt.tls_session)

def getfield(self, pkt, s):
if pkt.type == 0x14:
return super(_TLSInnerPlaintextField, self).getfield(pkt, s)
tag_len = pkt.tls_session.rcs.mac_len
frag_len = pkt.len - tag_len
if frag_len < 1:
Expand Down
16 changes: 9 additions & 7 deletions scapy/tools/UTscapy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
Unit testing infrastructure for Scapy
"""

from __future__ import absolute_import
from __future__ import print_function
import sys
import bz2
import copy
import code
import getopt
import glob
import importlib
import hashlib
import copy
import code
import bz2
import importlib
import json
import logging
import os.path
import sys
import time
import traceback
import warnings
Expand Down Expand Up @@ -284,7 +285,6 @@ def parse_config_file(config_path, verb=3):
}

"""
import json
with open(config_path) as config_file:
data = json.load(config_file)
if verb > 2:
Expand Down Expand Up @@ -857,6 +857,8 @@ def resolve_testfiles(TESTFILES):

def main():
argv = sys.argv[1:]
logger = logging.getLogger("scapy")
logger.addHandler(logging.StreamHandler())
ignore_globals = list(six.moves.builtins.__dict__)

# Parse arguments
Expand Down
Loading