diff --git a/.travis.yml b/.travis.yml index f4cd9a21150b9136eddb5fd2b171f76f2b1dd090..b94ac0364757ed0ede87f9be732074f97ea9cc33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,13 +36,6 @@ matrix: env: - SCAPY_SUDO=sudo SCAPY_USE_PCAPDNET=yes SCAPY_COVERAGE=yes - - os: linux - sudo: required - python: 2.7 - env: - - TRAVIS_SUDO=sudo - - TEST_COMBINED_MODES=yes - - os: osx language: generic env: diff --git a/.travis/test.sh b/.travis/test.sh index 18917fcfb4c560ca597acfba81f3de39875b6d3d..64ba5b1fad648301e00673297a4fc27b346afe06 100644 --- a/.travis/test.sh +++ b/.travis/test.sh @@ -77,8 +77,8 @@ do done # Run unit tests with openssl if we have root privileges -if [ "$TRAVIS_OS_NAME" = "linux" ] && [ ! -z $SCAPY_USE_PCAPDNET ] && [ ! -z $TRAVIS_SUDO ] +if [ "$TRAVIS_OS_NAME" = "linux" ] && [ ! -z $SCAPY_USE_PCAPDNET ] && [ ! -z $SCAPY_SUDO ] then - $TRAVIS_SUDO ./run_tests_tls_netaccess || exit $? + $SCAPY_SUDO tls/run_tests_tls_netaccess || exit $? fi diff --git a/scapy/config.py b/scapy/config.py index 2a2bbc07dcaa56eacab68414284a63b85593cc16..e46ffeadc2f0dc97eaaf05426445cc471bae8839 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -308,69 +308,37 @@ def _prompt_changer(attr,val): sys.ps1 = prompt class Conf(ConfClass): - """ - This object contains the configuration of scapy. - _session - Filename where the session will be saved. - _interactive_shell - If "ipython", use IPython as shell. Default is Python. - _stealth - If 1, prevent any unwanted packet to go out (ARP, DNS, ...). - _checkIPID - If 0, don't check that IPID matches between IP sent and ICMP IP - citation received. If 1, check that they either are equal or byte - swapped equals (bug in some IP stacks). If 2, strictly check that - they are equals. - _checkIPinIP - If True, check that IP-in-IP layers match. If False, do not check IP - layers that encapsulates another IP layer. - _checkIPsrc - If 1, check IP src in IP and ICMP IP citation match (bug in some NAT - stacks). - _check_TCPerror_seqack - If 1, also check that TCP seq and ack match the ones in ICMP citation. - _iff - Select the output interface for srp() and sendp(). Default is "eth0". - _verb - Level of verbosity, from 0 (almost mute) to 3 (verbose). - _promisc - Default mode for listening socket (to get answers if you spoof on a - lan). - _sniff_promisc - Default mode for sniff(). - _filter - BPF filter added to every sniffing socket to exclude traffic from - analysis. - _histfile - History file. - _padding - Include padding in desassembled packets. - _except_filter - BPF filter for packets to ignore. - _debug_match - If 1, store received packet that are not matched into debug.recv. - _route - Holds the Scapy routing table and provides methods to manipulate it. - _warning_threshold - Set the time threshold between warnings from the same place. - _ASN1_default_codec - Codec used by default for ASN1 objects. - _mib - Holds MIB direct access dictionary. - _resolve - Holds a list of fields for which resolution should be done. - _noenum - Holds a list of enum fields for which conversion to string should NOT - be done. - _AS_resolver - Choose the AS resolver class to use. - _extensions_paths - Path or list of paths where extensions are to be looked for. - _contribs - A dict which can be used by contrib layers to store local configuration. - _debug_tls - When 1, print some TLS session secrets when they are computed. - """ + """This object contains the configuration of Scapy. +session : filename where the session will be saved +interactive_shell : If set to "ipython", use IPython as shell. Default: Python +stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) +checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received + if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks) + if 2, strictly checks that they are equals +checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) +checkIPinIP: if True, checks that IP-in-IP layers match. If False, do not + check IP layers that encapsulates another IP layer +check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation +iff : selects the default output interface for srp() and sendp(). default:"eth0") +verb : level of verbosity, from 0 (almost mute) to 3 (verbose) +promisc : default mode for listening socket (to get answers if you spoof on a lan) +sniff_promisc : default mode for sniff() +filter : bpf filter added to every sniffing socket to exclude traffic from analysis +histfile : history file +padding : includes padding in disassembled packets +except_filter : BPF filter for packets to ignore +debug_match : when 1, store received packet that are not matched into debug.recv +route : holds the Scapy routing table and provides methods to manipulate it +warning_threshold : how much time between warnings from the same place +ASN1_default_codec: Codec used by default for ASN1 objects +mib : holds MIB direct access dictionary +resolve : holds list of fields for which resolution should be done +noenum : holds list of enum fields for which conversion to string should NOT be done +AS_resolver: choose the AS resolver class to use +extensions_paths: path or list of paths where extensions are to be looked for +contribs : a dict which can be used by contrib layers to store local configuration +debug_tls:When 1, print some TLS session secrets when they are computed. +""" version = VERSION session = "" interactive = False diff --git a/scapy/layers/tls/__init__.py b/scapy/layers/tls/__init__.py index bcc292ba4fe4040c67ed12dbe07531adafdadd23..5bc6d76b89e9920c763d7b1959ee9900c1a5df4c 100644 --- a/scapy/layers/tls/__init__.py +++ b/scapy/layers/tls/__init__.py @@ -8,15 +8,7 @@ Tools for handling TLS sessions and digital certificates. Prerequisites: - - You need to 'pip install ecdsa' for the module to be loaded. - - - We rely on pycrypto for several computations, however the last packaged - version does not provide AEAD support. If you don't need it, just do - 'pip install pycrypto'. If however you need GCM & CCM support, do - curl -sL https://github.com/dlitz/pycrypto/archive/v2.7a1.tar.gz | tar xz - cd pycrypto-2.7a1 - python setup.py build - sudo python setup.py install + - You may need to 'pip install cryptography' for the module to be loaded. Main features: @@ -26,22 +18,23 @@ Main features: - RSA & ECDSA keys sign/verify methods. - TLS records and sublayers (handshake...) parsing/building. Works with - versions TLS 1.0, 1.1 and 1.2. SSLv3 should be mostly ok. This may be - enhanced by a TLS context. For instance, if scapy reads a ServerHello - with version TLS 1.2 and a cipher suite using AES, it will assume the - presence of IVs prepending the data. See test/tls.uts for real examples. + versions SSLv3, TLS 1.0, 1.1 and 1.2. This may be enhanced by a TLS + context. For instance, if scapy reads a ServerHello with version TLS 1.2 + and a cipher suite using AES, it will assume the presence of IVs + prepending the data. See test/tls.uts for real examples. - TLS encryption/decryption capabilities with the usual ciphersuites. Once - again, the TLS context enables scapy to transparently send/receive - protected data if it learnt the session secrets. Note that if scapy acts as - one side of the handshake (e.g. reads all server-related packets and builds - all client-related packets), it will indeed compute the session secrets. + again, the TLS context enables scapy to transparently send/receive + protected data if it learnt the session secrets. Note that if scapy acts + as one side of the handshake (e.g. reads all server-related packets and + builds all client-related packets), it will indeed compute the session + secrets. - TLS client & server basic automatons, provided for testing and tweaking - purposes. These make for a very primitive TLS stack. + purposes. These make for a very primitive TLS stack. - Additionally, a basic test PKI (key + certificate for a CA, a client and - a server) is provided in tls/examples/pki_test. + a server) is provided in tls/examples/pki_test. Unit tests: @@ -57,46 +50,67 @@ Unit tests: TODO list (may it be carved away by good souls): - - Enrich the automatons. The client should be able to receive data at any - time, and to send as much data as wanted from stdin (for now, only one - predefined data message may be sent following the handshake). The server - should stay online even after the first client leaves. Then we could look - at more complicated behaviours like renegotiation and resumption. - We might get some help from tintinweb/scapy-ssl_tls. + - Features to add (or wait for) in the cryptography library: + + - no limitation on FFDH generator size; + (remove line 88 in cryptography/hazmat/primitives/asymmetric/dh.py) + + - CCM and CHACHA20-POLY1305 ciphers; + + - ECDH curves (x25519 and x448) from RFC 7748; + + - FFDH groups from RFC 7919; + + - the so-called 'tls' hash used with SSLv3 and TLS 1.0; + + - the simple DES algorithm; + + - the compressed EC point format. + + + - About the automatons: + + - Enrich the automatons. The client should be able to receive data at + any time, and to send as much data as wanted from stdin (for now, + only one predefined data message may be sent following the + handshake). The server should stay online even after the first client + leaves. Then we could look at more complicated behaviours like + renegotiation and resumption. We might get some help from + tintinweb/scapy-ssl_tls. + + - Add some examples which illustrate how the automatons could be used. + Typically, we could showcase this with Heartbleed. - - Add some examples which illustrate how the automatons could be used. - Typically, we could showcase this with Heartbleed. + - Split up parts of the automaton, e.g. when our server builds the + ServerHello, Certificate, ServerKeyExchange and ServerHelloDone in + the same should_REPLY_TO_CH method. - - Split up parts of the automaton, e.g. when our server builds the - ServerHello, Certificate, ServerKeyExchange and ServerHelloDone in the - same should_REPLY_TO_CH method. + - Make the automatons tests more robust and less consuming. - - Make the automatons tests more robust and less consuming. + - Allow the server to store both one RSA key and one ECDSA key, and + select the right one to use according to the ClientHello suites. - - Allow for the server to store simultaneously one RSA key and one ECDSA - key, and select the right one to use according to the ClientHello suites. + - Find a way to shutdown the automatons sockets properly without + simultaneously breaking the unit tests. - - Find a way to shutdown the automatons sockets properly without - simultaneously breaking the unit tests. - - Switch from pycrypto to python-cryptography, once it provides proper - AEAD support. See if we could get CHACHA20-POLY1305 in the process. + - Miscellaneous: - - Check FFDH and ECDH parameters at SKE/CKE reception. + - Implement TLS 1.3 structures and automatons. :D - - Go through the kx_algs and see what may be commented out without risk. + - Implement SSLv2 structures and automatons. xD - - Define the OCSPStatus packet. + - Mostly unused features : DSS, fixed DH, SRP, IDEA, char2 curves... - - Define several Certificate Transparency objects. + - Check FFDH and ECDH parameters at SKE/CKE reception. - - Enhance PSK support. + - Go through the kx_algs and see what may be commented out. - - Mostly unused features : DSS, fixed DH, SRP, IDEA, KRB5, char2 curves... + - Define the OCSPStatus packet. - - Implement SSLv2 structures and automatons. xD + - Define several Certificate Transparency objects. - - Implement TLS 1.3 structures and automatons. :D + - Enhance PSK and session ticket support. """ from scapy.config import conf diff --git a/scapy/layers/tls/automaton.py b/scapy/layers/tls/automaton.py index 9c1ce68896b39e75ce6d91388e8e620e91bd8ebc..f31c1f1b03c7358ba8671823a1e71d74acc21caf 100644 --- a/scapy/layers/tls/automaton.py +++ b/scapy/layers/tls/automaton.py @@ -829,9 +829,10 @@ class TLSServerAutomaton(Automaton): if self.cur_pkt.comp and 1 in self.cur_pkt.comp: comp = 1 - # Should do more than that to decide which version to return self.cur_session.advertised_tls_version = self.cur_pkt.version - v = self.cur_session.advertised_tls_version + self.cur_session.tls_version = self.cur_pkt.version + #XXX there should be some checks on this version from the ClientHello + v = self.cur_session.tls_version print "\nVersion: " + _tls_version[v] print "Cipher suite: " + _tls_cipher_suites[c] diff --git a/scapy/layers/tls/basefields.py b/scapy/layers/tls/basefields.py index c480d8869813d6ce8ccc94723b64e5cbaad85e1e..cfa98d6e832cccfaf93e7197898fca4d0bc5b1fa 100644 --- a/scapy/layers/tls/basefields.py +++ b/scapy/layers/tls/basefields.py @@ -155,10 +155,11 @@ class _TLSPadField(StrField): def getfield(self, pkt, s): if pkt.tls_session.consider_read_padding(): - # We get the length from the first byte of s which + # We get the length from the last byte of s which # is either the first byte of padding or the padding - # length field itself is padding length is 0 - l = ord(s[0]) + # length field itself is padding length is 0. + # This should work with SSLv3 and also TLS versions. + l = ord(s[-1]) return s[l:], self.m2i(pkt, s[:l]) return s, None diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py index eea1cbc1d51ebfb41b5d73fec848da6f2931c0a1..a3eed47e4d57d8fadabd75197dafe9b9c9a6498c 100644 --- a/scapy/layers/tls/cert.py +++ b/scapy/layers/tls/cert.py @@ -8,8 +8,6 @@ High-level methods for PKI objects (X.509 certificates, CRLs, asymmetric keys). Supports both RSA and ECDSA objects. -This module relies on python-crypto and python-ecdsa. - The classes below are wrappers for the ASN.1 objects defined in x509.py. By collecting their attributes, we bypass the ASN.1 structure, hence there is no direct method for exporting a new full DER-encoded version @@ -107,6 +105,7 @@ class _PKIObj(object): def __init__(self, frmt, der, pem): # Note that changing attributes of the _PKIObj does not update these # values (e.g. modifying k.modulus does not change k.der). + #XXX use __setattr__ for this self.frmt = frmt self.der = der self.pem = pem @@ -174,18 +173,16 @@ class _PubKeyFactory(_PKIObjMaker): """ Metaclass for PubKey creation. It casts the appropriate class on the fly, then fills in - the appropriate attributes with updateWith() submethod. + the appropriate attributes with import_from_asn1pkt() submethod. """ def __call__(cls, key_path): - # First, we deal with the exceptional RSA KEA call. + # First, we deal with the exceptional RSA 'kx export' call. if type(key_path) is tuple: - e, m, mLen = key_path obj = type.__call__(cls) + obj.__class__ = PubKeyRSA obj.frmt = "tuple" - obj.modulus = m - obj.modulusLen = mLen - obj.pubExp = e + obj.import_from_tuple(key_path) return obj # Now for the usual calls, key_path may be the path to either: @@ -198,11 +195,11 @@ class _PubKeyFactory(_PKIObjMaker): pubkey = spki.subjectPublicKey if isinstance(pubkey, RSAPublicKey): obj.__class__ = PubKeyRSA - obj.updateWith(pubkey) + obj.import_from_asn1pkt(pubkey) elif isinstance(pubkey, ECDSAPublicKey): obj.__class__ = PubKeyECDSA try: - obj.updateWith(spki) + obj.import_from_asn1pkt(obj.der) except ImportError: pass else: @@ -212,7 +209,7 @@ class _PubKeyFactory(_PKIObjMaker): try: pubkey = RSAPublicKey(obj.der) obj.__class__ = PubKeyRSA - obj.updateWith(pubkey) + obj.import_from_asn1pkt(pubkey) marker = "RSA PUBLIC KEY" except: # We cannot import an ECDSA public key without curve knowledge @@ -236,12 +233,7 @@ class PubKey(object): sigAlg = tbsCert.signature h = hash_by_oid[sigAlg.algorithm.val] sigVal = str(cert.signatureValue) - sigdec = None - if ecdsa_support: - sigdec = ecdsa.util.sigdecode_der - return self.verify(str(tbsCert), sigVal, h=h, - t='pkcs', - sigdecode=sigdec) + return self.verify(str(tbsCert), sigVal, h=h, t='pkcs') class PubKeyRSA(_PKIObj, PubKey, _EncryptAndVerifyRSA): @@ -250,60 +242,59 @@ class PubKeyRSA(_PKIObj, PubKey, _EncryptAndVerifyRSA): Use the 'key' attribute to access original object. """ @crypto_validator - def updateWith(self, pubkey): + def import_from_tuple(self, tup): + # this is rarely used + e, m, mLen = tup + if isinstance(m, str): + self.modulus = pkcs_os2ip(m) + else: + self.modulus = m + self.modulusLen = mLen + if isinstance(e, str): + self.pubExp = pkcs_os2ip(e) + else: + self.pubExp = e + pubNum = rsa.RSAPublicNumbers(n=self.modulus, e=self.pubExp) + self.pubkey = pubNum.public_key(default_backend()) + self.pem = self.pubkey.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + self.der = pem2der(self.pem) + + @crypto_validator + def import_from_asn1pkt(self, pubkey): self.modulus = pubkey.modulus.val self.modulusLen = len(binrepr(pubkey.modulus.val)) self.pubExp = pubkey.publicExponent.val - self.key = rsa.RSAPublicNumbers( - n=self.modulus, - e=self.pubExp - ).public_key(default_backend()) + pubNum = rsa.RSAPublicNumbers(n=self.modulus, e=self.pubExp) + self.pubkey = pubNum.public_key(default_backend()) def encrypt(self, msg, t=None, h=None, mgf=None, L=None): # no ECDSA encryption support, hence no ECDSA specific keywords here return _EncryptAndVerifyRSA.encrypt(self, msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, h=None, - t=None, mgf=None, sLen=None, - sigdecode=None): + t=None, mgf=None, sLen=None): return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h, t=t, mgf=mgf, sLen=sLen) class PubKeyECDSA(_PKIObj, PubKey): """ - Wrapper for ECDSA keys based on VerifyingKey from ecdsa library. + Wrapper for ECDSA keys based on the cryptography library. Use the 'key' attribute to access original object. """ - @ecdsa_exception - def updateWith(self, spki): - # For now we use from_der() or from_string() methods, - # which do not offer support for compressed points. - #XXX Try using the from_public_point() method. - try: - self.key = ecdsa.VerifyingKey.from_der(str(spki)) - # from_der() raises an exception on explicit curves - except: - s = spki.subjectPublicKey.val_readable[1:] - p = spki.signatureAlgorithm.parameters - c = import_curve(p.fieldID.prime.val, - p.curve.a.val, - p.curve.b.val, - p.base.val, - p.order.val) - self.key = ecdsa.VerifyingKey.from_string(s, c) - @ecdsa_exception + def import_from_asn1pkt(self, pubkey): + # XXX does the cryptography lib support explicit curves? + # check also for compressed points + self.pubkey = serialization.load_der_public_key(pubkey, + backend=default_backend()) def encrypt(self, msg, t=None, h=None, mgf=None, L=None): - # python-ecdsa does not support encryption + # cryptography lib does not support ECDSA encryption raise Exception("No ECDSA encryption support") - @ecdsa_exception def verify(self, msg, sig, h=None, - t=None, mgf=None, sLen=None, - sigdecode=None): - if sigdecode is None: - sigdecode = ecdsa.util.sigdecode_string - try: - return self.key.verify(sig, msg, hashfunc=mapHashFunc(h), - sigdecode=sigdecode) - except ecdsa.keys.BadSignatureError: - return False + t=None, mgf=None, sLen=None): + # 'sig' should be a DER-encoded signature, as per RFC 3279 + verifier = self.pubkey.verifier(sig, ec.ECDSA(mapHashFunc(h))) + verifier.update(msg) + return verifier.verify() ################ @@ -314,7 +305,7 @@ class _PrivKeyFactory(_PKIObjMaker): """ Metaclass for PrivKey creation. It casts the appropriate class on the fly, then fills in - the appropriate attributes with updateWith() submethod. + the appropriate attributes with import_from_asn1pkt() submethod. """ def __call__(cls, key_path): """ @@ -351,7 +342,7 @@ class _PrivKeyFactory(_PKIObjMaker): except: raise Exception("Unable to import private key") try: - obj.updateWith(privkey) + obj.import_from_asn1pkt(privkey) except ImportError: pass @@ -383,17 +374,11 @@ class PrivKey(object): to both PrivKeyRSA and PrivKeyECDSA, the sign() methods of the subclasses accept any argument, be it from the RSA or ECDSA world, and then they keep the ones they're interested in. - Here, t will be passed eventually to pkcs1._DecryptAndSignRSA.sign(), - while sigencode will be passed to ecdsa.keys.SigningKey.sign(). + Here, t will be passed eventually to pkcs1._DecryptAndSignRSA.sign(). """ sigAlg = tbsCert.signature h = h or hash_by_oid[sigAlg.algorithm.val] - sigenc = None - if ECDSA_SUPPORT: - sigenc = ecdsa.util.sigencode_der - sigVal = self.sign(str(tbsCert), h=h, - t='pkcs', - sigencode=sigenc) + sigVal = self.sign(str(tbsCert), h=h, t='pkcs') c = X509_Cert() c.tbsCertificate = tbsCert c.signatureAlgorithm = sigAlg @@ -410,13 +395,7 @@ class PrivKey(object): sigAlg = tbsCert.signature h = hash_by_oid[sigAlg.algorithm.val] sigVal = str(cert.signatureValue) - if not ecdsa_support: - print "No ecdsa support. Signature could not be verified." - return False - else: - return self.verify(str(tbsCert), sigVal, h=h, - t='pkcs', - sigdecode=ecdsa.util.sigdecode_der) + return self.verify(str(tbsCert), sigVal, h=h, t='pkcs') class PrivKeyRSA(_PKIObj, PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA): @@ -425,7 +404,7 @@ class PrivKeyRSA(_PKIObj, PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA): Use the 'key' attribute to access original object. """ @crypto_validator - def updateWith(self, privkey): + def import_from_asn1pkt(self, privkey): self.modulus = privkey.modulus.val self.modulusLen = len(binrepr(privkey.modulus.val)) self.pubExp = privkey.publicExponent.val @@ -440,15 +419,14 @@ class PrivKeyRSA(_PKIObj, PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA): dmq1=self.exponent2, iqmp=self.coefficient, public_numbers=rsa.RSAPublicNumbers(n=self.modulus, e=self.pubExp), ).private_key(default_backend()) + self.pubkey = self.key.public_key() def verify(self, msg, sig, h=None, - t=None, mgf=None, sLen=None, - sigdecode=None): + t=None, mgf=None, sLen=None): # Let's copy this from PubKeyRSA instead of adding another baseclass :) return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h, t=t, mgf=mgf, sLen=sLen) def sign(self, data, h=None, - t=None, mgf=None, sLen=None, - k=None, entropy=None, sigencode=None): + t=None, mgf=None, sLen=None): return _DecryptAndSignRSA.sign(self, data, h=h, t=t, mgf=mgf, sLen=sLen) @@ -458,25 +436,20 @@ class PrivKeyECDSA(_PKIObj, PrivKey): Wrapper for ECDSA keys based on SigningKey from ecdsa library. Use the 'key' attribute to access original object. """ - @ecdsa_exception - def updateWith(self, privkey): - self.privKey = pkcs_os2ip(privkey.privateKey.val) - self.key = ecdsa.SigningKey.from_der(str(privkey)) - self.vkey = self.key.get_verifying_key() - @ecdsa_exception - def verify(self, msg, sig, h=None, - t=None, mgf=None, sLen=None, - sigdecode=None): - return self.vkey.verify(sig, msg, hashfunc=mapHashFunc(h), - sigdecode=sigdecode) - @ecdsa_exception - def sign(self, data, h=None, - t=None, mgf=None, sLen=None, - k=None, entropy=None, sigencode=None): - if sigencode is None: - sigencode = ecdsa.util.sigencode_string - return self.key.sign(data, hashfunc=mapHashFunc(h), - k=k, entropy=entropy, sigencode=sigencode) + def import_from_asn1pkt(self, privkey): + self.key = serialization.load_der_private_key(str(privkey), + None, + backend=default_backend()) + self.pubkey = self.key.public_key() + def verify(self, msg, sig, h=None, **kwargs): + # 'sig' should be a DER-encoded signature, as per RFC 3279 + verifier = self.pubkey.verifier(sig, ec.ECDSA(mapHashFunc(h))) + verifier.update(msg) + return verifier.verify() + def sign(self, data, h=None, **kwargs): + signer = self.key.signer(ec.ECDSA(mapHashFunc(h))) + signer.update(data) + return signer.finalize() ################ @@ -496,7 +469,7 @@ class _CertMaker(_PKIObjMaker): cert = X509_Cert(obj.der) except: raise Exception("Unable to import certificate") - obj.updateWith(cert) + obj.import_from_asn1pkt(cert) return obj @@ -507,7 +480,7 @@ class Cert(_PKIObj): """ __metaclass__ = _CertMaker - def updateWith(self, cert): + def import_from_asn1pkt(self, cert): error_msg = "Unable to import certificate" self.x509Cert = cert @@ -591,11 +564,9 @@ class Cert(_PKIObj): return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L) def verify(self, msg, sig, h=None, - t=None, mgf=None, sLen=None, - sigdecode=None): + t=None, mgf=None, sLen=None): return self.pubKey.verify(msg, sig, h=h, - t=t, mgf=mgf, sLen=sLen, - sigdecode=sigdecode) + t=t, mgf=mgf, sLen=sLen) def remainingDays(self, now=None): """ @@ -698,7 +669,7 @@ class _CRLMaker(_PKIObjMaker): crl = X509_CRL(obj.der) except: raise Exception("Unable to import CRL") - obj.updateWith(crl) + obj.import_from_asn1pkt(crl) return obj @@ -709,7 +680,7 @@ class CRL(_PKIObj): """ __metaclass__ = _CRLMaker - def updateWith(self, crl): + def import_from_asn1pkt(self, crl): error_msg = "Unable to import CRL" self.x509CRL = crl diff --git a/scapy/layers/tls/crypto/all.py b/scapy/layers/tls/crypto/all.py index 18c1bba54d9de2bbe7240c89de1a8c281075680f..e8192a55dcec9a67aa4c0b027f6fa16a5b40c2c7 100644 --- a/scapy/layers/tls/crypto/all.py +++ b/scapy/layers/tls/crypto/all.py @@ -7,7 +7,9 @@ Aggregate some TLS crypto objects. """ -from scapy.layers.tls.crypto.ecdh import * +# XXX This line should be removed once standard FFDH groups have been +# registered in the cryptography library. from scapy.layers.tls.crypto.ffdh import * + from scapy.layers.tls.crypto.suites import * diff --git a/scapy/layers/tls/crypto/camellia.py b/scapy/layers/tls/crypto/camellia.py deleted file mode 100644 index c9f70a5ccb7b411ab9b270f1a01825fb38a2ce49..0000000000000000000000000000000000000000 --- a/scapy/layers/tls/crypto/camellia.py +++ /dev/null @@ -1,376 +0,0 @@ -## This file is part of Scapy -## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard -## 2015, 2016 Maxence Tury -## This program is published under a GPLv2 license - -""" -Pure Python implementation of Camellia based on RFC 3713. -""" - -# Note: the only rationales for this implementation are that Camellia is -# not yet available in python-crypto at the time of writing and -# the fact that the module is not expected to be used on huge -# volume of traffic, but mainly for TLS handshakes --arno - -from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip - - -def _left_rot_op(x, xbitlen, bitrot): # left rotation operation, i.e. '<<<' - """ - Performs left rotation operation of 'xbitrot' bit on 'x' of bit length - 'xbitlen', i.e. if x is taken as 32 bit value, _left_rot_op(x, 32, 12) - is basically w <<< 12 in the notation of - """ - bitrot %= xbitlen - mask = (1 << xbitlen) - 1 - x &= mask - return ((x << bitrot) | (x >> (xbitlen - bitrot))) & mask - -_MASK8 = 0xff -_MASK32 = 0xffffffff -_MASK64 = 0xffffffffffffffff -_MASK128 = 0xffffffffffffffffffffffffffffffff - -_Sigma1 = 0xA09E667F3BCC908B -_Sigma2 = 0xB67AE8584CAA73B2 -_Sigma3 = 0xC6EF372FE94F82BE -_Sigma4 = 0x54FF53A5F1D36F1C -_Sigma5 = 0x10E527FADE682D1D -_Sigma6 = 0xB05688C2B3E6C1FD - -_SBOX1 = [112, 130, 44, 236, 179, 39, 192, 229, - 228, 133, 87, 53, 234, 12, 174, 65, - 35, 239, 107, 147, 69, 25, 165, 33, - 237, 14, 79, 78, 29, 101, 146, 189, - 134, 184, 175, 143, 124, 235, 31, 206, - 62, 48, 220, 95, 94, 197, 11, 26, - 166, 225, 57, 202, 213, 71, 93, 61, - 217, 1, 90, 214, 81, 86, 108, 77, - 139, 13, 154, 102, 251, 204, 176, 45, - 116, 18, 43, 32, 240, 177, 132, 153, - 223, 76, 203, 194, 52, 126, 118, 5, - 109, 183, 169, 49, 209, 23, 4, 215, - 20, 88, 58, 97, 222, 27, 17, 28, - 50, 15, 156, 22, 83, 24, 242, 34, - 254, 68, 207, 178, 195, 181, 122, 145, - 36, 8, 232, 168, 96, 252, 105, 80, - 170, 208, 160, 125, 161, 137, 98, 151, - 84, 91, 30, 149, 224, 255, 100, 210, - 16, 196, 0, 72, 163, 247, 117, 219, - 138, 3, 230, 218, 9, 63, 221, 148, - 135, 92, 131, 2, 205, 74, 144, 51, - 115, 103, 246, 243, 157, 127, 191, 226, - 82, 155, 216, 38, 200, 55, 198, 59, - 129, 150, 111, 75, 19, 190, 99, 46, - 233, 121, 167, 140, 159, 110, 188, 142, - 41, 245, 249, 182, 47, 253, 180, 89, - 120, 152, 6, 106, 231, 70, 113, 186, - 212, 37, 171, 66, 136, 162, 141, 250, - 114, 7, 185, 85, 248, 238, 172, 10, - 54, 73, 42, 104, 60, 56, 241, 164, - 64, 40, 211, 123, 187, 201, 67, 193, - 21, 227, 173, 244, 119, 199, 128, 158] - -_SBOX2 = map(lambda x: _left_rot_op(x,8,1), _SBOX1) - -_SBOX3 = map(lambda x: _left_rot_op(x,8,7), _SBOX1) - -_SBOX4 = [] -for k in range(len(_SBOX1)): - _SBOX4.append(_SBOX1[_left_rot_op(k,8,1)]) - -def _F(F_IN, KE): # Section 2.4.1 of RFC 3713 - x = (F_IN ^ KE) & _MASK64 - t1 = x >> 56 & _MASK8 - t2 = (x >> 48) & _MASK8 - t3 = (x >> 40) & _MASK8 - t4 = (x >> 32) & _MASK8 - t5 = (x >> 24) & _MASK8 - t6 = (x >> 16) & _MASK8 - t7 = (x >> 8) & _MASK8 - t8 = x & _MASK8 - t1 = _SBOX1[t1] - t2 = _SBOX2[t2] - t3 = _SBOX3[t3] - t4 = _SBOX4[t4] - t5 = _SBOX2[t5] - t6 = _SBOX3[t6] - t7 = _SBOX4[t7] - t8 = _SBOX1[t8] - y1 = t1 ^ t3 ^ t4 ^ t6 ^ t7 ^ t8 - y2 = t1 ^ t2 ^ t4 ^ t5 ^ t7 ^ t8 - y3 = t1 ^ t2 ^ t3 ^ t5 ^ t6 ^ t8 - y4 = t2 ^ t3 ^ t4 ^ t5 ^ t6 ^ t7 - y5 = t1 ^ t2 ^ t6 ^ t7 ^ t8 - y6 = t2 ^ t3 ^ t5 ^ t7 ^ t8 - y7 = t3 ^ t4 ^ t5 ^ t6 ^ t8 - y8 = t1 ^ t4 ^ t5 ^ t6 ^ t7 - F_OUT = ((y1 << 56) | (y2 << 48) | (y3 << 40) | (y4 << 32) | - (y5 << 24) | (y6 << 16) | (y7 << 8) | y8) - return F_OUT; - - -def _FL(FL_IN, KE): # Section 2.4.2 of RFC 3713 - x1 = (FL_IN >> 32) & _MASK32 - x2 = FL_IN & _MASK32 - k1 = KE >> 32 - k2 = KE & _MASK32 - x2 = x2 ^ _left_rot_op((x1 & k1), 32, 1) - x1 = x1 ^ (x2 | k2) - FL_OUT = (x1 << 32) | x2 - return FL_OUT - -def _FLINV(FLINV_IN, KE): # Section 2.4.3 of RFC 3713 - y1 = FLINV_IN >> 32 - y2 = FLINV_IN & _MASK32 - k1 = KE >> 32 - k2 = KE & _MASK32 - y1 = y1 ^ (y2 | k2) - y2 = y2 ^ _left_rot_op((y1 & k1), 32, 1) - FLINV_OUT = (y1 << 32) | y2 - return FLINV_OUT - -# Key Scheduling Part as described in section 2.2 of RFC 3713 -def _scheduling(K): #eats the key as a 16, 24 or 32 bytes string - l = len(K) - K = pkcs_os2ip(K) - - if l == 16: - KL = K - KR = 0 - elif l == 24: - KL = K >> 64 - KR = ((K & _MASK64) << 64) | ((~(K & _MASK64)) % (_MASK64+1)) - elif l == 32: - KL = K >> 128 - KR = K & _MASK128 - else: - return None - - D1 = (KL ^ KR) >> 64 - D2 = (KL ^ KR) & _MASK64 - D2 = D2 ^ _F(D1, _Sigma1) - D1 = D1 ^ _F(D2, _Sigma2) - D1 = D1 ^ (KL >> 64) - D2 = D2 ^ (KL & _MASK64) - D2 = D2 ^ _F(D1, _Sigma3) - D1 = D1 ^ _F(D2, _Sigma4) - KA = (D1 << 64) | D2 - D1 = (KA ^ KR) >> 64 - D2 = (KA ^ KR) & _MASK64 - D2 = D2 ^ _F(D1, _Sigma5) - D1 = D1 ^ _F(D2, _Sigma6) - - if l != 16: - KB = (D1 << 64) | D2 - - if l == 16: - kw1 = _left_rot_op(KL, 128, 0) >> 64 - kw2 = _left_rot_op(KL, 128, 0) & _MASK64 - k1 = _left_rot_op(KA, 128, 0) >> 64 - k2 = _left_rot_op(KA, 128, 0) & _MASK64 - k3 = _left_rot_op(KL, 128, 15) >> 64 - k4 = _left_rot_op(KL, 128, 15) & _MASK64 - k5 = _left_rot_op(KA, 128, 15) >> 64 - k6 = _left_rot_op(KA, 128, 15) & _MASK64 - ke1 = _left_rot_op(KA, 128, 30) >> 64 - ke2 = _left_rot_op(KA, 128, 30) & _MASK64 - k7 = _left_rot_op(KL, 128, 45) >> 64 - k8 = _left_rot_op(KL, 128, 45) & _MASK64 - k9 = _left_rot_op(KA, 128, 45) >> 64 - k10 = _left_rot_op(KL, 128, 60) & _MASK64 - k11 = _left_rot_op(KA, 128, 60) >> 64 - k12 = _left_rot_op(KA, 128, 60) & _MASK64 - ke3 = _left_rot_op(KL, 128, 77) >> 64 - ke4 = _left_rot_op(KL, 128, 77) & _MASK64 - k13 = _left_rot_op(KL, 128, 94) >> 64 - k14 = _left_rot_op(KL, 128, 94) & _MASK64 - k15 = _left_rot_op(KA, 128, 94) >> 64 - k16 = _left_rot_op(KA, 128, 94) & _MASK64 - k17 = _left_rot_op(KL, 128, 111) >> 64 - k18 = _left_rot_op(KL, 128, 111) & _MASK64 - kw3 = _left_rot_op(KA, 128, 111) >> 64 - kw4 = _left_rot_op(KA, 128, 111) & _MASK64 - return [ kw1, kw2, kw3, kw4, - k1 , k2, k3, k4, k5, k6, k7, k8, k9, - k10, k11, k12, k13, k14, k15, k16, k17, k18, - ke1, ke2, ke3, ke4 ] - else: - kw1 = _left_rot_op(KL, 128, 0) >> 64 - kw2 = _left_rot_op(KL, 128, 0) & _MASK64 - k1 = _left_rot_op(KB, 128, 0) >> 64 - k2 = _left_rot_op(KB, 128, 0) & _MASK64 - k3 = _left_rot_op(KR, 128, 15) >> 64 - k4 = _left_rot_op(KR, 128, 15) & _MASK64 - k5 = _left_rot_op(KA, 128, 15) >> 64 - k6 = _left_rot_op(KA, 128, 15) & _MASK64 - ke1 = _left_rot_op(KR, 128, 30) >> 64 - ke2 = _left_rot_op(KR, 128, 30) & _MASK64 - k7 = _left_rot_op(KB, 128, 30) >> 64 - k8 = _left_rot_op(KB, 128, 30) & _MASK64 - k9 = _left_rot_op(KL, 128, 45) >> 64 - k10 = _left_rot_op(KL, 128, 45) & _MASK64 - k11 = _left_rot_op(KA, 128, 45) >> 64 - k12 = _left_rot_op(KA, 128, 45) & _MASK64 - ke3 = _left_rot_op(KL, 128, 60) >> 64 - ke4 = _left_rot_op(KL, 128, 60) & _MASK64 - k13 = _left_rot_op(KR, 128, 60) >> 64 - k14 = _left_rot_op(KR, 128, 60) & _MASK64 - k15 = _left_rot_op(KB, 128, 60) >> 64 - k16 = _left_rot_op(KB, 128, 60) & _MASK64 - k17 = _left_rot_op(KL, 128, 77) >> 64 - k18 = _left_rot_op(KL, 128, 77) & _MASK64 - ke5 = _left_rot_op(KA, 128, 77) >> 64 - ke6 = _left_rot_op(KA, 128, 77) & _MASK64 - k19 = _left_rot_op(KR, 128, 94) >> 64 - k20 = _left_rot_op(KR, 128, 94) & _MASK64 - k21 = _left_rot_op(KA, 128, 94) >> 64 - k22 = _left_rot_op(KA, 128, 94) & _MASK64 - k23 = _left_rot_op(KL, 128, 111) >> 64 - k24 = _left_rot_op(KL, 128, 111) & _MASK64 - kw3 = _left_rot_op(KB, 128, 111) >> 64 - kw4 = _left_rot_op(KB, 128, 111) & _MASK64 - return [ kw1, kw2, kw3, kw4, - k1 , k2, k3, k4, k5, k6, k7, k8, - k9 , k10, k11, k12, k13, k14, k15, k16, - k17, k18, k19, k20, k21, k22, k23, k24, - ke1, ke2, ke3, ke4, ke5, ke6 ] - -def _decrypt_encrypt_128(M, K, decrypt=False): - M = pkcs_os2ip(M) - D1 = M >> 64 - D2 = M & _MASK64 - - l = _scheduling(K) - (kw1,kw2,kw3,kw4,k1,k2,k3,k4,k5,k6,k7,k8,k9,k10, - k11,k12,k13,k14,k15,k16,k17,k18,ke1,ke2,ke3,ke4) = l - if decrypt: # Reversing the order of subkeys: See 2.3.3 of RFC 3713 - tmp = kw1; kw1 = kw3; kw3 = tmp - tmp = kw2; kw2 = kw4; kw4 = tmp - tmp = k1; k1 = k18; k18 = tmp - tmp = k2; k2 = k17; k17 = tmp - tmp = k3; k3 = k16; k16 = tmp - tmp = k4; k4 = k15; k15 = tmp - tmp = k5; k5 = k14; k14 = tmp - tmp = k6; k6 = k13; k13 = tmp - tmp = k7; k7 = k12; k12 = tmp - tmp = k8; k8 = k11; k11 = tmp - tmp = k9; k9 = k10; k10 = tmp - tmp = ke1; ke1 = ke4; ke4 = tmp - tmp = ke2; ke2 = ke3; ke3 = tmp - - D1 = D1 ^ kw1 # Prewhitening - D2 = D2 ^ kw2 - D2 = D2 ^ _F(D1, k1) # Round 1 - D1 = D1 ^ _F(D2, k2) # Round 2 - D2 = D2 ^ _F(D1, k3) # Round 3 - D1 = D1 ^ _F(D2, k4) # Round 4 - D2 = D2 ^ _F(D1, k5) # Round 5 - D1 = D1 ^ _F(D2, k6) # Round 6 - D1 = _FL(D1, ke1) # FL - D2 = _FLINV(D2, ke2) # FLINV - D2 = D2 ^ _F(D1, k7) # Round 7 - D1 = D1 ^ _F(D2, k8) # Round 8 - D2 = D2 ^ _F(D1, k9) # Round 9 - D1 = D1 ^ _F(D2, k10) # Round 10 - D2 = D2 ^ _F(D1, k11) # Round 11 - D1 = D1 ^ _F(D2, k12) # Round 12 - D1 = _FL(D1, ke3) # FL - D2 = _FLINV(D2, ke4) # FLINV - D2 = D2 ^ _F(D1, k13) # Round 13 - D1 = D1 ^ _F(D2, k14) # Round 14 - D2 = D2 ^ _F(D1, k15) # Round 15 - D1 = D1 ^ _F(D2, k16) # Round 16 - D2 = D2 ^ _F(D1, k17) # Round 17 - D1 = D1 ^ _F(D2, k18) # Round 18 - D2 = D2 ^ kw3 # Postwhitening - D1 = D1 ^ kw4 - - C = (D2 << 64) | D1 - return pkcs_i2osp(C, 16) - -def _decrypt_encrypt_192_256(M, K, decrypt=False): - M = pkcs_os2ip(M) - D1 = M >> 64 - D2 = M & _MASK64 - - l = _scheduling(K) - (kw1,kw2,kw3,kw4,k1,k2,k3,k4,k5,k6,k7,k8,k9,k10,k11, - k12,k13,k14,k15,k16,k17,k18,k19,k20,k21,k22,k23, - k24,ke1,ke2,ke3,ke4,ke5,ke6) = l - if decrypt: # Reversing the order of subkeys: See 2.3.3 of RFC 3713 - tmp = kw1; kw1 = kw3; kw3 = tmp - tmp = kw2; kw2 = kw4; kw4 = tmp - tmp = k1; k1 = k24; k24 = tmp - tmp = k2; k2 = k23; k23 = tmp - tmp = k3; k3 = k22; k22 = tmp - tmp = k4; k4 = k21; k21 = tmp - tmp = k5; k5 = k20; k20 = tmp - tmp = k6; k6 = k19; k19 = tmp - tmp = k7; k7 = k18; k18 = tmp - tmp = k8; k8 = k17; k17 = tmp - tmp = k9; k9 = k16; k16 = tmp - tmp = k10; k10 = k15; k15 = tmp - tmp = k11; k11 = k14; k14 = tmp - tmp = k12; k12 = k13; k13 = tmp - tmp = ke1; ke1 = ke6; ke6 = tmp - tmp = ke2; ke2 = ke5; ke5 = tmp - tmp = ke3; ke3 = ke4; ke4 = tmp - - D1 = D1 ^ kw1 # Prewhitening - D2 = D2 ^ kw2 - D2 = D2 ^ _F(D1, k1) # Round 1 - D1 = D1 ^ _F(D2, k2) # Round 2 - D2 = D2 ^ _F(D1, k3) # Round 3 - D1 = D1 ^ _F(D2, k4) # Round 4 - D2 = D2 ^ _F(D1, k5) # Round 5 - D1 = D1 ^ _F(D2, k6) # Round 6 - D1 = _FL (D1, ke1) # _FL - D2 = _FLINV(D2, ke2) # _FLINV - D2 = D2 ^ _F(D1, k7) # Round 7 - D1 = D1 ^ _F(D2, k8) # Round 8 - D2 = D2 ^ _F(D1, k9) # Round 9 - D1 = D1 ^ _F(D2, k10) # Round 10 - D2 = D2 ^ _F(D1, k11) # Round 11 - D1 = D1 ^ _F(D2, k12) # Round 12 - D1 = _FL (D1, ke3) # _FL - D2 = _FLINV(D2, ke4) # _FLINV - D2 = D2 ^ _F(D1, k13) # Round 13 - D1 = D1 ^ _F(D2, k14) # Round 14 - D2 = D2 ^ _F(D1, k15) # Round 15 - D1 = D1 ^ _F(D2, k16) # Round 16 - D2 = D2 ^ _F(D1, k17) # Round 17 - D1 = D1 ^ _F(D2, k18) # Round 18 - D1 = _FL (D1, ke5) # _FL - D2 = _FLINV(D2, ke6) # _FLINV - D2 = D2 ^ _F(D1, k19) # Round 19 - D1 = D1 ^ _F(D2, k20) # Round 20 - D2 = D2 ^ _F(D1, k21) # Round 21 - D1 = D1 ^ _F(D2, k22) # Round 22 - D2 = D2 ^ _F(D1, k23) # Round 23 - D1 = D1 ^ _F(D2, k24) # Round 24 - D2 = D2 ^ kw3 # Postwhitening - D1 = D1 ^ kw4 - C = (D2 << 64) | D1 - - return pkcs_i2osp(C, 16) - - -class Camellia(object): - def _decrypt_encrypt(self, M, K, dec): - if len(M) != 16: - raise AttributeError("Camellia has a block size of 128 bits") - l = len(K) - if l == 16: - return _decrypt_encrypt_128(M, K, decrypt=dec) - elif l == 24 or l == 32: - return _decrypt_encrypt_192_256(M, K, decrypt=dec) - else: - raise AttributeError("Camellia supports only 128, 192 and 256 bits keys") - - def encrypt(self, M, K): - return self._decrypt_encrypt(M, K, False) - - def decrypt(self, C, K): - return self._decrypt_encrypt(C, K, True) - diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py index 7ef30ca6385c2e39d3436218a66718189bc968a4..3b9d84a095bce3b6b1a25cc5d10af8f1e0d78ee6 100644 --- a/scapy/layers/tls/crypto/cipher_aead.py +++ b/scapy/layers/tls/crypto/cipher_aead.py @@ -10,20 +10,16 @@ RFC 5288 introduces new ciphersuites for TLS 1.2 which are based on AES in Galois/Counter Mode (GCM). RFC 6655 in turn introduces AES_CCM ciphersuites. The related AEAD algorithms are defined in RFC 5116. -For now, we use AES.MODE_GCM and AES.MODE_CCM from the pycrypto library. -Note that, even though they are supported in the last version 2.7a, -they are not supported by the last commonly packaged version 2.6. - -For the installation of pycrypto 2.7a, see doc/scapy/installation.rst -If you keep pycrypto 2.6, the suites supposed to use the ciphers below -will be tagged with 'usable' False. +For now the cryptography library only supports GCM mode. +Their interface might (and should) be changed in the future. """ import struct -from Crypto.Cipher import AES +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.backends import default_backend +from cryptography.exceptions import InvalidTag -from scapy.error import warning from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip from scapy.layers.tls.crypto.ciphers import CipherError @@ -56,25 +52,53 @@ class _AEADCipher(object): __metaclass__ = _AEADCipherMetaclass type = "aead" - pc_cls = AES - block_size = 16 - key_len = 16 - - salt_len = 4 - nonce_explicit_len = 8 - tag_len = 16 - - def __init__(self, key=None, salt=None, nonce_explicit_init=None): + def __init__(self, key=None, salt=None, nonce_explicit=None): """ 'key' and 'salt' are to be provided as strings, whereas the internal 'nonce_explicit' is an integer (it is simpler for incrementation). """ - self.key = key - self.salt = salt - - if type(nonce_explicit_init) is str: - nonce_explicit_init = pkcs_os2ip(nonce_explicit_init) - self.nonce_explicit = nonce_explicit_init + self.ready = {"key":True, "salt":True, "nonce_explicit":True} + if key is None: + self.ready["key"] = False + key = "\0" * self.key_len + if salt is None: + self.ready["salt"] = False + salt = "\0" * self.salt_len + if nonce_explicit is None: + self.ready["nonce_explicit"] = False + nonce_explicit = 0 + + if type(nonce_explicit) is str: + nonce_explicit = pkcs_os2ip(nonce_explicit) + + # we use super() in order to avoid any deadlock with __setattr__ + super(_AEADCipher, self).__setattr__("key", key) + super(_AEADCipher, self).__setattr__("salt", salt) + super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit) + + iv = salt + pkcs_i2osp(nonce_explicit, self.nonce_explicit_len) + self._cipher = Cipher(self.pc_cls(key), + self.pc_cls_mode(iv), + backend=default_backend()) + + def __setattr__(self, name, val): + if name == "key": + if self._cipher is not None: + self._cipher.algorithm.key = val + self.ready["key"] = True + elif name == "salt": + iv = val + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) + if self._cipher is not None: + self._cipher.mode._initialization_vector = iv + self.ready["salt"] = True + elif name == "nonce_explicit": + if type(val) is str: + val = pkcs_os2ip(val) + iv = self.salt + pkcs_i2osp(val, self.nonce_explicit_len) + if self._cipher is not None: + self._cipher.mode._initialization_vector = iv + self.ready["nonce_explicit"] = True + super(_AEADCipher, self).__setattr__(name, val) def _update_nonce(self): """ @@ -88,25 +112,27 @@ class _AEADCipher(object): Encrypt the data, prepend the explicit part of the nonce, and append the computed authentication code. Additional data may be authenticated without encryption (as A). + + Note that the cipher's authentication tag must be None when encrypting. """ - if self.pc_cls_mode is None: - warning("No AEAD support! Please install pycrypto 2.7a or later.") - raise CipherError + if False in self.ready.itervalues(): + raise CipherError, (P, A) + self._cipher.mode._tag = None + encryptor = self._cipher.encryptor() + encryptor.authenticate_additional_data(A) + res = encryptor.update(P) + encryptor.finalize() + res += encryptor.tag nonce_explicit = pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) - K = self.key - N = self.salt + nonce_explicit - ciph = self.pc_cls.new(K, self.pc_cls_mode, N, mac_len=self.tag_len) - ciph.update(A) self._update_nonce() - return nonce_explicit + ciph.encrypt(P) + ciph.digest() + return nonce_explicit + res def auth_decrypt(self, A, C, add_length=True): """ Decrypt the data and verify the authentication code (in this order). When additional data was authenticated, it has to be passed (as A). - If the verification fails, a ValueError is raised. It is the user's + If the verification fails, an AEADTagError is raised. It is the user's responsibility to catch it if deemed useful. If we lack the key, we raise a CipherError which contains the encrypted input. @@ -121,43 +147,48 @@ class _AEADCipher(object): C[self.nonce_explicit_len:-self.tag_len], C[-self.tag_len:]) - if self.pc_cls_mode is None: - warning("No AEAD support! Please install pycrypto 2.7a or later.") + if False in self.ready.itervalues(): raise CipherError, (nonce_explicit_str, C, mac) - if self.key is None: - raise CipherError, (nonce_explicit_str, C, mac) + self.nonce_explicit = pkcs_os2ip(nonce_explicit_str) + self._cipher.mode._tag = mac - K = self.key - N = self.salt + nonce_explicit_str - ciph = self.pc_cls.new(K, self.pc_cls_mode, N, mac_len=self.tag_len) + decryptor = self._cipher.decryptor() if add_length: A += struct.pack("!H", len(C)) - ciph.update(A) - P = ciph.decrypt(C) + decryptor.authenticate_additional_data(A) + + P = decryptor.update(C) try: - ciph.verify(mac) - except ValueError: + decryptor.finalize() + except InvalidTag: raise AEADTagError, (nonce_explicit_str, P, mac) return nonce_explicit_str, P, mac class Cipher_AES_128_GCM(_AEADCipher): - pc_cls_mode = AES.MODE_GCM if hasattr(AES, "MODE_GCM") else None + pc_cls = algorithms.AES + pc_cls_mode = modes.GCM + block_size = 16 + key_len = 16 + salt_len = 4 + nonce_explicit_len = 8 + tag_len = 16 class Cipher_AES_256_GCM(Cipher_AES_128_GCM): key_len = 32 -class Cipher_AES_128_CCM(_AEADCipher): - pc_cls_mode = AES.MODE_CCM if hasattr(AES, "MODE_CCM") else None - -class Cipher_AES_256_CCM(Cipher_AES_128_CCM): - key_len = 32 - -class Cipher_AES_128_CCM_8(Cipher_AES_128_CCM): - tag_len = 8 - -class Cipher_AES_256_CCM_8(Cipher_AES_128_CCM_8): - key_len = 32 +# no support for now in the cryptography library +#class Cipher_AES_128_CCM(_AEADCipher): +# pc_cls_mode = modes.CCM +# +#class Cipher_AES_256_CCM(Cipher_AES_128_CCM): +# key_len = 32 +# +#class Cipher_AES_128_CCM_8(Cipher_AES_128_CCM): +# tag_len = 8 +# +#class Cipher_AES_256_CCM_8(Cipher_AES_128_CCM_8): +# key_len = 32 diff --git a/scapy/layers/tls/crypto/cipher_block.py b/scapy/layers/tls/crypto/cipher_block.py index 6f38ae7988f03977195b1e1fc065659283d59e4f..de750438863f0adb8f3f4f8129c20a9a70351fa9 100644 --- a/scapy/layers/tls/crypto/cipher_block.py +++ b/scapy/layers/tls/crypto/cipher_block.py @@ -7,10 +7,10 @@ Block ciphers. """ -from Crypto.Cipher import AES, DES3, DES, ARC2 +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.backends import default_backend from scapy.utils import strxor -from scapy.layers.tls.crypto.camellia import Camellia from scapy.layers.tls.crypto.ciphers import CipherError @@ -36,15 +36,43 @@ class _BlockCipher(object): type = "block" def __init__(self, key=None, iv=None): - self.key = key - self.iv = iv + self.ready = {"key":True, "iv":True} + if key is None: + self.ready["key"] = False + key = "\0" * self.key_len + if iv is None or iv == "": + self.ready["iv"] = False + iv = "\0" * self.block_size + + # we use super() in order to avoid any deadlock with __setattr__ + super(_BlockCipher, self).__setattr__("key", key) + super(_BlockCipher, self).__setattr__("iv", iv) + + self._cipher = Cipher(self.pc_cls(key), + self.pc_cls_mode(iv), + backend=default_backend()) + + def __setattr__(self, name, val): + if name == "key": + if self._cipher is not None: + self._cipher.algorithm.key = val + self.ready["key"] = True + elif name == "iv": + if self._cipher is not None: + self._cipher.mode._initialization_vector = val + self.ready["iv"] = True + super(_BlockCipher, self).__setattr__(name, val) + def encrypt(self, data): """ Encrypt the data. Also, update the cipher iv. This is needed for SSLv3 and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.post_build(). """ - tmp = self.pc_cls.new(self.key, self.pc_cls_mode, self.iv).encrypt(data) + if False in self.ready.itervalues(): + raise CipherError, data + encryptor = self._cipher.encryptor() + tmp = encryptor.update(data) + encryptor.finalize() self.iv = tmp[-self.block_size:] return tmp @@ -54,18 +82,17 @@ class _BlockCipher(object): and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.pre_dissect(). If we lack the key, we raise a CipherError which contains the input. """ - if self.key is None: + if False in self.ready.itervalues(): raise CipherError, data - tmp = self.pc_cls.new(self.key, self.pc_cls_mode, self.iv).decrypt(data) + decryptor = self._cipher.decryptor() + tmp = decryptor.update(data) + decryptor.finalize() self.iv = data[-self.block_size:] return tmp -### Standard AES - class Cipher_AES_128_CBC(_BlockCipher): - pc_cls = AES - pc_cls_mode = AES.MODE_CBC + pc_cls = algorithms.AES + pc_cls_mode = modes.CBC block_size = 16 key_len = 16 @@ -73,119 +100,58 @@ class Cipher_AES_256_CBC(Cipher_AES_128_CBC): key_len = 32 -### Camellia - class Cipher_CAMELLIA_128_CBC(_BlockCipher): - """ - As Camellia is not supported in pycrypto, we rely on our camellia.py. - Don't expect speed, it's more for completeness than anything else. - """ - type = "block" + pc_cls = algorithms.Camellia + pc_cls_mode = modes.CBC block_size = 16 key_len = 16 - def __init__(self, key=None, iv=None): - self.key = key - self.iv = iv - self.c = Camellia() - - def encrypt(self, data): - l = len(data)/16 - p = 0 - res = [] - tmp = self.iv - while p != l: - tmp = strxor(tmp, data[p*16:(p+1)*16]) - tmp = self.c.encrypt(tmp, self.key) - res.append(tmp) - p += 1 - self.iv = tmp - return "".join(res) - - def decrypt(self, data): - if self.key is None: - raise Exception, data - l = len(data)/16 - p = 0 - res = [] - while p != l: - s = data[p*16:(p+1)*16] - tmp = self.c.decrypt(s, self.key) - tmp = strxor(tmp, self.iv) - self.iv = s - res.append(tmp) - p += 1 - return "".join(res) - class Cipher_CAMELLIA_256_CBC(Cipher_CAMELLIA_128_CBC): - name = "CAMELLIA_256_CBC" key_len = 32 ### Mostly deprecated ciphers -class Cipher_RC2_CBC_40(_BlockCipher): # RFC 2268 - pc_cls = ARC2 - pc_cls_mode = ARC2.MODE_CBC - block_size = 8 - key_len = 5 - expanded_key_len = 16 - -class Cipher_DES_CBC(_BlockCipher): - pc_cls = DES - pc_cls_mode = DES.MODE_CBC - block_size = 8 - key_len = 8 - -class Cipher_DES40_CBC(Cipher_DES_CBC): - """ - This is an export cipher example. The key length has been weakened to 5 - random bytes (i.e. 5 bytes will be extracted from the master_secret). - Yet, we still need to know the original length which will actually be - fed into the encryption algorithm. This is what expanded_key_len - is for, and it gets used in PRF.postprocess_key_for_export(). - We never define this attribute with non-export ciphers. - """ - key_len = 5 - expanded_key_len = 8 - class Cipher_3DES_EDE_CBC(_BlockCipher): - pc_cls = DES3 - pc_cls_mode = DES.MODE_CBC + pc_cls = algorithms.TripleDES + pc_cls_mode = modes.CBC block_size = 8 key_len = 24 +class Cipher_IDEA_CBC(_BlockCipher): + pc_cls = algorithms.IDEA + pc_cls_mode = modes.CBC + block_size = 8 + key_len = 16 + +class Cipher_SEED_CBC(_BlockCipher): + pc_cls = algorithms.SEED + pc_cls_mode = modes.CBC + block_size = 16 + key_len = 16 -### IDEA & SEED (XXX no support for now) - -# http://en.wikipedia.org/wiki/International_Data_Encryption_Algorithm -# IPR claim on IDEA : http://www.ietf.org/ietf/IPR/ASCOM-IDEA -# class Cipher_IDEA_CBC(_BlockCipher): -# key_len = 16 -# block_size = 8 -# -# def encrypt(self, data): -# print "IDEA is unavailable" -# return data -# -# def decrypt(self, data): -# print "IDEA is unavailable" -# return data - -# SEED is a symmetric encryption algorithm that was developed by Korea -# Information Security Agency (KISA) and a group of experts. -# Specif: http://www.kisa.or.kr/kisa/seed/data/Document_pdf/SEED_Specification_english.pdf -# http://tools.ietf.org/rfc/rfc4269.txt -# RFC 4162 : Addition of SEED Cipher Suites to TLS -# class Cipher_SEED_CBC(_BlockCipher): -# key_len = 16 -# block_size = 16 -# -# def encrypt(self, data): -# print "SEED is unavailable" -# return data -# -# def decrypt(self, data): -# print "SEED is unavailable" -# return data +#class Cipher_DES_CBC(_BlockCipher): +# pc_cls = algorithms.DES # no support in the cryptography library +# pc_cls_mode = modes.CBC +# block_size = 8 +# key_len = 8 + +#class Cipher_DES40_CBC(Cipher_DES_CBC): +# """ +# This is an export cipher example. The key length has been weakened to 5 +# random bytes (i.e. 5 bytes will be extracted from the master_secret). +# Yet, we still need to know the original length which will actually be +# fed into the encryption algorithm. This is what expanded_key_len +# is for, and it gets used in PRF.postprocess_key_for_export(). +# We never define this attribute with non-export ciphers. +# """ +# key_len = 5 +# expanded_key_len = 8 + +#class Cipher_RC2_CBC_40(_BlockCipher): # RFC 2268 +# pc_cls = ARC2 # no support in the cryptography library +# pc_cls_mode = modes.CBC +# block_size = 8 +# key_len = 5 +# expanded_key_len = 16 diff --git a/scapy/layers/tls/crypto/cipher_stream.py b/scapy/layers/tls/crypto/cipher_stream.py index 0c05e2dd3d5db703a858e999356da5a1a95c8aea..7a403f3cad91da287bf25d508c16249533e125cc 100644 --- a/scapy/layers/tls/crypto/cipher_stream.py +++ b/scapy/layers/tls/crypto/cipher_stream.py @@ -7,7 +7,8 @@ Stream ciphers. """ -from Crypto.Cipher import ARC4 +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms +from cryptography.hazmat.backends import default_backend from scapy.layers.tls.crypto.ciphers import CipherError @@ -33,41 +34,65 @@ class _StreamCipher(object): __metaclass__ = _StreamCipherMetaclass type = "stream" - -class Cipher_NULL(_StreamCipher): - key_len = 0 - expanded_key_len = 0 - def __init__(self, key=None): - self.key = key + """ + Note that we have to keep the encryption/decryption state in unique + encryptor and decryptor objects. This differs from _BlockCipher. + """ + self.ready = {"key":True} + if key is None: + self.ready["key"] = False + key = "\0" * self.key_len + + # we use super() in order to avoid any deadlock with __setattr__ + super(_StreamCipher, self).__setattr__("key", key) + + self._cipher = Cipher(self.pc_cls(key), + mode=None, + backend=default_backend()) + self.encryptor = self._cipher.encryptor() + self.decryptor = self._cipher.decryptor() + + def __setattr__(self, name, val): + if name == "key": + if self._cipher is not None: + self._cipher.algorithm.key = val + self.ready["key"] = True + super(_StreamCipher, self).__setattr__(name, val) def encrypt(self, data): - return data + if False in self.ready.itervalues(): + raise CipherError, data + return self.encryptor.update(data) def decrypt(self, data): - return data + if False in self.ready.itervalues(): + raise CipherError, data + return self.decryptor.update(data) + class Cipher_RC4_40(_StreamCipher): + pc_cls = algorithms.ARC4 key_len = 5 expanded_key_len = 16 - def __init__(self, key=None): - self.alg_state = None - self.key = key +class Cipher_RC4_128(Cipher_RC4_40): + key_len = 16 - def __setattr__(self, name, value): - super(Cipher_RC4_40, self).__setattr__(name, value) - if name == "key" and value is not None: - self.alg_state = ARC4.new(value) + +class Cipher_NULL(_StreamCipher): + key_len = 0 + expanded_key_len = 0 + + def __init__(self, key=None): + self.ready = {"key":True} + # we use super() in order to avoid any deadlock with __setattr__ + super(_StreamCipher, self).__setattr__("key", key) + self._cipher = None def encrypt(self, data): - return self.alg_state.encrypt(data) + return data def decrypt(self, data): - if self.key is None: - raise CipherError, data - return self.alg_state.decrypt(data) - -class Cipher_RC4_128(Cipher_RC4_40): - key_len = 16 + return data diff --git a/scapy/layers/tls/crypto/curves.py b/scapy/layers/tls/crypto/curves.py index 94f2e3839b1c16978688b67d5efb3d95e5887317..9a1d875e6930631e36693050da797891d431424b 100644 --- a/scapy/layers/tls/crypto/curves.py +++ b/scapy/layers/tls/crypto/curves.py @@ -8,410 +8,262 @@ Implicit elliptic curves. Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf and www.ecc-brainpool.org/download/Domain-parameters.pdf -Note that this module will overwrite curves from python-ecdsa. -""" - -import math - -from scapy.utils import long_converter, binrepr -from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip - - -def encode_point(point, point_format=0): - """ - Return a string representation of the Point p, according to point_format. - """ - pLen = len(binrepr(point.curve().p())) - x = pkcs_i2osp(point.x(), math.ceil(pLen/8)) - y = pkcs_i2osp(point.y(), math.ceil(pLen/8)) - if point_format == 0: - frmt = '\x04' - elif point_format == 1: - frmt = chr(2 + y%2) - y = '' - else: - raise Exception("No support for point_format %d" % point_format) - return frmt + x + y - - -ecdsa_support = False -try: - import ecdsa - ecdsa_support = True -except ImportError: - import logging - log_loading = logging.getLogger("scapy.loading") - log_loading.info("Can't import python ecdsa lib. No curves.") - -if ecdsa_support: - - from ecdsa.ellipticcurve import CurveFp, Point - from ecdsa.curves import Curve - from ecdsa.numbertheory import square_root_mod_prime - - - def extract_coordinates(g, curve): - """ - Return the coordinates x and y as integers, - regardless of the point format of string g. - Second expected parameter is a CurveFp. - """ - p = curve.p() - point_format = g[0] - point = g[1:] - if point_format == '\x04': - point_len = len(point) - if point_len % 2 != 0: - raise Exception("Point length is not even.") - x_bytes = point[:point_len>>1] - x = pkcs_os2ip(x_bytes) % p - y_bytes = point[point_len>>1:] - y = pkcs_os2ip(y_bytes) % p - elif point_format in ['\x02', '\x03']: - x_bytes = point - x = pkcs_os2ip(x_bytes) % p - # perform the y coordinate computation with self.tls_ec - y_square = (x*x*x + curve.a()*x + curve.b()) % p - y = square_root_mod_prime(y_square, p) - y_parity = ord(point_format) % 2 # \x02 means even, \x03 means odd - if y % 2 != y_parity: - y = -y % p - else: - raise Exception("Point starts with %s. This encoding " - "is not recognized." % repr(point_format)) - if not curve.contains_point(x, y): - raise Exception("The point we extracted does not belong on the curve!") - return x, y +This is a ghost file since the shift from 'ecdsa' to 'cryptography' lib. +Part of the code has been kept, but commented out, in case anyone would like +to improve ECC support in 'cryptography' (namely for the compressed point +format and additional curves). +""" - def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)): - """ - Create an ecdsa.curves.Curve from the usual parameters. - Arguments may be either octet strings or integers, - except g which we expect to be an octet string. - """ - if isinstance(p, str): - p = pkcs_os2ip(p) - if isinstance(a, str): - a = pkcs_os2ip(a) - if isinstance(b, str): - b = pkcs_os2ip(b) - if isinstance(r, str): - r = pkcs_os2ip(r) - curve = CurveFp(p, a, b) - x, y = extract_coordinates(g, curve) - generator = Point(curve, x, y, r) - return Curve(name, curve, generator, oid) +#import math +# +#from scapy.utils import long_converter, binrepr +#from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip +# +# +#def encode_point(point, point_format=0): +# """ +# Return a string representation of the Point p, according to point_format. +# """ +# pLen = len(binrepr(point.curve().p())) +# x = pkcs_i2osp(point.x(), math.ceil(pLen/8)) +# y = pkcs_i2osp(point.y(), math.ceil(pLen/8)) +# if point_format == 0: +# frmt = '\x04' +# elif point_format == 1: +# frmt = chr(2 + y%2) +# y = '' +# else: +# raise Exception("No support for point_format %d" % point_format) +# return frmt + x + y +# +# +#try: +# import ecdsa +# ecdsa_support = True +#except ImportError: +# import logging +# log_loading = logging.getLogger("scapy.loading") +# log_loading.info("Can't import python ecdsa lib. No curves.") +# +# +#if ecdsa_support: +# +# from ecdsa.ellipticcurve import CurveFp, Point +# from ecdsa.curves import Curve +# from ecdsa.numbertheory import square_root_mod_prime +# +# +# def extract_coordinates(g, curve): +# """ +# Return the coordinates x and y as integers, +# regardless of the point format of string g. +# Second expected parameter is a CurveFp. +# """ +# p = curve.p() +# point_format = g[0] +# point = g[1:] +# if point_format == '\x04': +# point_len = len(point) +# if point_len % 2 != 0: +# raise Exception("Point length is not even.") +# x_bytes = point[:point_len>>1] +# x = pkcs_os2ip(x_bytes) % p +# y_bytes = point[point_len>>1:] +# y = pkcs_os2ip(y_bytes) % p +# elif point_format in ['\x02', '\x03']: +# x_bytes = point +# x = pkcs_os2ip(x_bytes) % p +# # perform the y coordinate computation with self.tls_ec +# y_square = (x*x*x + curve.a()*x + curve.b()) % p +# y = square_root_mod_prime(y_square, p) +# y_parity = ord(point_format) % 2 # \x02 means even, \x03 means odd +# if y % 2 != y_parity: +# y = -y % p +# else: +# raise Exception("Point starts with %s. This encoding " +# "is not recognized." % repr(point_format)) +# if not curve.contains_point(x, y): +# raise Exception("The point we extracted does not belong on the curve!") +# return x, y +# +# def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)): +# """ +# Create an ecdsa.curves.Curve from the usual parameters. +# Arguments may be either octet strings or integers, +# except g which we expect to be an octet string. +# """ +# if isinstance(p, str): +# p = pkcs_os2ip(p) +# if isinstance(a, str): +# a = pkcs_os2ip(a) +# if isinstance(b, str): +# b = pkcs_os2ip(b) +# if isinstance(r, str): +# r = pkcs_os2ip(r) +# curve = CurveFp(p, a, b) +# x, y = extract_coordinates(g, curve) +# generator = Point(curve, x, y, r) +# return Curve(name, curve, generator, oid) ### Named curves # We always provide _a as a positive integer. - _p = long_converter(""" - ffffffff ffffffff ffffffff fffffffe ffffac73""") - _a = 0 - _b = 7 - _Gx = long_converter(""" - 3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""") - _Gy = long_converter(""" - 938cf935 318fdced 6bc28286 531733c3 f03c4fee""") - _r = long_converter("""01 - 00000000 00000000 0001b8fa 16dfab9a ca16b6b3""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP160k1 = Curve("SECP160k1", curve, generator, - (1, 3, 132, 0, 9), "secp160k1") - - _p = long_converter(""" - ffffffff ffffffff ffffffff ffffffff 7fffffff""") - _a = -3 % _p - _b = long_converter(""" - 1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""") - _Gx = long_converter(""" - 4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""") - _Gy = long_converter(""" - 23a62855 3168947d 59dcc912 04235137 7ac5fb32""") - _r = long_converter("""01 - 00000000 00000000 0001f4c8 f927aed3 ca752257""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP160r1 = Curve("SECP160r1", curve, generator, - (1, 3, 132, 0, 8), "secp160r1") - - _p = long_converter(""" - ffffffff ffffffff ffffffff fffffffe ffffac73""") - _a = -3 % _p - _b = long_converter(""" - b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""") - _Gx = long_converter(""" - 52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""") - _Gy = long_converter(""" - feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""") - _r = long_converter("""01 - 00000000 00000000 0000351e e786a818 f3a1a16b""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP160r2 = Curve("SECP160r2", curve, generator, - (1, 3, 132, 0, 30), "secp160r2") - - _p = long_converter(""" - ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""") - _a = 0 - _b = 3 - _Gx = long_converter(""" - db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""") - _Gy = long_converter(""" - 9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""") - _r = long_converter(""" - ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP192k1 = Curve("SECP192k1", curve, generator, - (1, 3, 132, 0, 31), "secp192k1") - - _p = long_converter(""" - ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff""") - _a = -3 % _p - _b = long_converter(""" - 64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1""") - _Gx = long_converter(""" - 188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012""") - _Gy = long_converter(""" - 07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811""") - _r = long_converter(""" - ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP192r1 = Curve("SECP192r1", curve, generator, - (1, 2, 840, 10045, 3, 1, 1), "prime192v1") - - _p = long_converter(""" - ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe - ffffe56d""") - _a = 0 - _b = 5 - _Gx = long_converter(""" - a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e - b6b7a45c""") - _Gy = long_converter(""" - 7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb - 556d61a5""") - _r = long_converter("""01 - 00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971 - 769fb1f7""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP224k1 = Curve("SECP224k1", curve, generator, - (1, 3, 132, 0, 32), "secp224k1") - - _p = long_converter(""" - ffffffff ffffffff ffffffff ffffffff 00000000 00000000 - 00000001""") - _a = -3 % _p - _b = long_converter(""" - b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 - 2355ffb4""") - _Gx = long_converter(""" - b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 - 115c1d21""") - _Gy = long_converter(""" - bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 - 85007e34""") - _r = long_converter(""" - ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 - 5c5c2a3d""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP224r1 = Curve("SECP224r1", curve, generator, - (1, 3, 132, 0, 33), "secp224r1") - - # (already defined as SECP256k1 by python-ecdsa) - _p = long_converter(""" - ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - fffffffe fffffc2f""") - _a = 0 - _b = 7 - _Gx = long_converter(""" - 79be667e f9dcbbac 55a06295 ce870b07 029bfcdb 2dce28d9 - 59f2815b 16f81798""") - _Gy = long_converter(""" - 483ada77 26a3c465 5da4fbfc 0e1108a8 fd17b448 a6855419 - 9c47d08f fb10d4b8""") - _r = long_converter(""" - ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b - bfd25e8c d0364141""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP256k1 = Curve("SECP256k1", curve, generator, - (1, 3, 132, 0, 10), "secp256k1") - - _p = long_converter(""" - ffffffff 00000001 00000000 00000000 00000000 ffffffff - ffffffff ffffffff""") - _a = -3 % _p - _b = long_converter(""" - 5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 - 3bce3c3e 27d2604b""") - _Gx = long_converter(""" - 6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 - f4a13945 d898c296""") - _Gy = long_converter(""" - 4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece - cbb64068 37bf51f5""") - _r = long_converter(""" - ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 - f3b9cac2 fc632551""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP256r1 = Curve("SECP256r1", curve, generator, - (1, 2, 840, 10045, 3, 1, 7), "prime256v1") - - _p = long_converter(""" - ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - ffffffff fffffffe ffffffff 00000000 00000000 ffffffff""") - _a = -3 % _p - _b = long_converter(""" - b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 - 0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef""") - _Gx = long_converter(""" - aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 - 59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7""") - _Gy = long_converter(""" - 3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c - e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f""") - _r = long_converter(""" - ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - c7634d81 f4372ddf 581a0db2 48b0a77a ecec196a ccc52973""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP384r1 = Curve("SECP384r1", curve, generator, - (1, 3, 132, 0, 34), "secp384r1") - - _p = long_converter(""" - 01ff ffffffff ffffffff ffffffff ffffffff ffffffff - ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - ffffffff ffffffff ffffffff ffffffff ffffffff""") - _a = -3 % _p - _b = long_converter(""" - 0051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b - 99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd - 3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00""") - _Gx = long_converter(""" - 00c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 - 053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 - a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66""") - _Gy = long_converter(""" - 0118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 - 579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 - 3fad0761 353c7086 a272c240 88be9476 9fd16650""") - _r = long_converter(""" - 01ff ffffffff ffffffff ffffffff ffffffff ffffffff - ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 - f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - SECP521r1 = Curve("SECP521r1", curve, generator, - (1, 3, 132, 0, 35), "secp521r1") - - _p = long_converter(""" - A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028 - 2013481D 1F6E5377""") - _a = long_converter(""" - 7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C - E94A4B44 F330B5D9""") - _b = long_converter(""" - 26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE - 6BCCDC18 FF8C07B6""") - _Gx = long_converter(""" - 8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2 - 3A4453BD 9ACE3262""") - _Gy = long_converter(""" - 547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54 - 5C1D54C7 2F046997""") - _r = long_converter(""" - A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7 - 901E0E82 974856A7""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - BRNP256r1 = Curve("BRNP256r1", curve, generator, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1") - - _p = long_converter(""" - 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4 - 12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""") - _a = long_converter(""" - 7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787 - 139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""") - _b = long_converter(""" - 04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6 - 2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""") - _Gx = long_converter(""" - 1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3 - DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""") - _Gy = long_converter(""" - 8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864 - E19C054F F9912928 0E464621 77918111 42820341 263C5315""") - _r = long_converter(""" - 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3 - 1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - BRNP384r1 = Curve("BRNP384r1", curve, generator, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1") - - _p = long_converter(""" - AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E - D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6 - 2881FF2F 2D82C685 28AA6056 583A48F3""") - _a = long_converter(""" - 7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610 - A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 - 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""") - _b = long_converter(""" - 3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 - 8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67 - 984050B7 5EBAE5DD 2809BD63 8016F723""") - _Gx = long_converter(""" - 81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1 - B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F - 7C6D5047 406A5E68 8B352209 BCB9F822""") - _Gy = long_converter(""" - 7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A - A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE - D1CA2B2F A8F05406 78CD1E0F 3AD80892""") - _r = long_converter(""" - AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E - D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047 - 1DB1D381 085DDADD B5879682 9CA90069""") - curve = CurveFp(_p, _a, _b) - generator = Point(curve, _Gx, _Gy, _r) - BRNP512r1 = Curve("BRNP512r1", curve, generator, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1") - - - # IANA identifiers - named_curves = { 15: SECP160k1, - 16: SECP160r1, - 17: SECP160r2, - 18: SECP192k1, - 19: SECP192r1, - 20: SECP224k1, - 21: SECP224r1, - 22: SECP256k1, - 23: SECP256r1, - 24: SECP384r1, - 25: SECP521r1, - 26: BRNP256r1, - 27: BRNP384r1, - 28: BRNP512r1 - } - - for cid, c in named_curves.iteritems(): - c.curve_id = cid - - # We replace/fill the previous named curves. - import ecdsa.curves - ecdsa.curves.curves = named_curves.values() - +# _p = long_converter(""" +# ffffffff ffffffff ffffffff fffffffe ffffac73""") +# _a = 0 +# _b = 7 +# _Gx = long_converter(""" +# 3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""") +# _Gy = long_converter(""" +# 938cf935 318fdced 6bc28286 531733c3 f03c4fee""") +# _r = long_converter("""01 +# 00000000 00000000 0001b8fa 16dfab9a ca16b6b3""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# SECP160k1 = Curve("SECP160k1", curve, generator, +# (1, 3, 132, 0, 9), "secp160k1") + +# _p = long_converter(""" +# ffffffff ffffffff ffffffff ffffffff 7fffffff""") +# _a = -3 % _p +# _b = long_converter(""" +# 1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""") +# _Gx = long_converter(""" +# 4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""") +# _Gy = long_converter(""" +# 23a62855 3168947d 59dcc912 04235137 7ac5fb32""") +# _r = long_converter("""01 +# 00000000 00000000 0001f4c8 f927aed3 ca752257""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# SECP160r1 = Curve("SECP160r1", curve, generator, +# (1, 3, 132, 0, 8), "secp160r1") + +# _p = long_converter(""" +# ffffffff ffffffff ffffffff fffffffe ffffac73""") +# _a = -3 % _p +# _b = long_converter(""" +# b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""") +# _Gx = long_converter(""" +# 52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""") +# _Gy = long_converter(""" +# feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""") +# _r = long_converter("""01 +# 00000000 00000000 0000351e e786a818 f3a1a16b""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# SECP160r2 = Curve("SECP160r2", curve, generator, +# (1, 3, 132, 0, 30), "secp160r2") + +# _p = long_converter(""" +# ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""") +# _a = 0 +# _b = 3 +# _Gx = long_converter(""" +# db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""") +# _Gy = long_converter(""" +# 9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""") +# _r = long_converter(""" +# ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# SECP192k1 = Curve("SECP192k1", curve, generator, +# (1, 3, 132, 0, 31), "secp192k1") + +# _p = long_converter(""" +# ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe +# ffffe56d""") +# _a = 0 +# _b = 5 +# _Gx = long_converter(""" +# a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e +# b6b7a45c""") +# _Gy = long_converter(""" +# 7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb +# 556d61a5""") +# _r = long_converter("""01 +# 00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971 +# 769fb1f7""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# SECP224k1 = Curve("SECP224k1", curve, generator, +# (1, 3, 132, 0, 32), "secp224k1") + +# _p = long_converter(""" +# A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028 +# 2013481D 1F6E5377""") +# _a = long_converter(""" +# 7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C +# E94A4B44 F330B5D9""") +# _b = long_converter(""" +# 26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE +# 6BCCDC18 FF8C07B6""") +# _Gx = long_converter(""" +# 8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2 +# 3A4453BD 9ACE3262""") +# _Gy = long_converter(""" +# 547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54 +# 5C1D54C7 2F046997""") +# _r = long_converter(""" +# A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7 +# 901E0E82 974856A7""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# BRNP256r1 = Curve("BRNP256r1", curve, generator, +# (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1") + +# _p = long_converter(""" +# 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4 +# 12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""") +# _a = long_converter(""" +# 7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787 +# 139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""") +# _b = long_converter(""" +# 04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6 +# 2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""") +# _Gx = long_converter(""" +# 1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3 +# DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""") +# _Gy = long_converter(""" +# 8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864 +# E19C054F F9912928 0E464621 77918111 42820341 263C5315""") +# _r = long_converter(""" +# 8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3 +# 1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# BRNP384r1 = Curve("BRNP384r1", curve, generator, +# (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1") + +# _p = long_converter(""" +# AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E +# D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6 +# 2881FF2F 2D82C685 28AA6056 583A48F3""") +# _a = long_converter(""" +# 7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610 +# A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5 +# 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""") +# _b = long_converter(""" +# 3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 +# 8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67 +# 984050B7 5EBAE5DD 2809BD63 8016F723""") +# _Gx = long_converter(""" +# 81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1 +# B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F +# 7C6D5047 406A5E68 8B352209 BCB9F822""") +# _Gy = long_converter(""" +# 7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A +# A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE +# D1CA2B2F A8F05406 78CD1E0F 3AD80892""") +# _r = long_converter(""" +# AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E +# D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047 +# 1DB1D381 085DDADD B5879682 9CA90069""") +# curve = CurveFp(_p, _a, _b) +# generator = Point(curve, _Gx, _Gy, _r) +# BRNP512r1 = Curve("BRNP512r1", curve, generator, +# (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1") diff --git a/scapy/layers/tls/crypto/ecdh.py b/scapy/layers/tls/crypto/ecdh.py deleted file mode 100644 index 56765ca79e405e6e92bb22a5feb0aa888cd54b5b..0000000000000000000000000000000000000000 --- a/scapy/layers/tls/crypto/ecdh.py +++ /dev/null @@ -1,181 +0,0 @@ -## This file is part of Scapy -## Copyright (C) 2016 Pascal Delaunay, Maxence Tury -## This program is published under a GPLv2 license - -""" -Primitive Elliptic Curve Diffie-Hellman module. -""" - -ecdsa_support = False -try: - import ecdsa - ecdsa_support = True -except ImportError: - import logging - log_loading = logging.getLogger("scapy.loading") - log_loading.info("Can't import python ecdsa lib. No ECDH.") - - -if ecdsa_support: - - import random - - from scapy.layers.tls.crypto.curves import (Point, - named_curves, - import_curve, - encode_point, - extract_coordinates) - from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp - - - class ECParams(object): - def __init__(self, curve_type): - """ - RFC 3279: ec_type = 1 for prime-fields - = 2 for char-two-fields (no real support for now) - As to self.curve, it stores an ecdsa.curves.Curve. - """ - self.ec_type = None - self.curve = None - - def set_named_curve(self, curve_tls_id): - """ - Identify and store the named curve we're working with. - We always set self.ec_type to 1 because, for now at least, - there are no char2 curves among the named_curves. - """ - if curve_tls_id not in named_curves: - raise Exception("Unsupported named curve id %d" % curve_tls_id) - self.curve = named_curves[curve_tls_id] - self.ec_type = 1 - - def set_explicit_char2_curve(self, basetype, base, a, b, g, r): - self.ec_type = 2 - raise Exception("No char2 support for now") - - def set_explicit_prime_curve(self, p, a, b, g, r): - """ - Create and store the explicit curve we're working with. - """ - self.ec_type = 1 - self.curve = import_curve(p, a, b, g, r) - - - class ECDHParams(object): - """ - Elliptic Curve Diffie-Hellman parameters. - Holds an instance of ECParams and some attributes of the DH algorithm. - These are used in ServerECDH*Params for the TLS key exchange. - - self.priv is an integer. Its value may remain unknown. - - self.pub and self.other_pub values (the public value we generated and the - one we received) are encoded as octet strings according to point_format. - - self.secret is the shared secret, also encoded as an octet string. - - Default ec_parameters relate to the SECP256r1 curve. - """ - def __init__(self, ec_parameters=None, point_format=0): - """ - RFC 4492: point_format = 0 for uncompressed - = 1 for compressed_prime - = 2 for compressed_char2 - """ - if ec_parameters is None: - ec_parameters = ECParams(1) - ec_parameters.set_named_curve(23) - self.ec_parameters = ec_parameters - - if point_format not in [0, 1]: - if point_format == 2: - raise Exception("No support for ansiX962_compressed_char2") - else: - raise Exception("Unknown point format") - self.point_format = point_format - - self.priv = None - self.pub = None - self.other_pub = None - self.secret = None - - def gen_public_params(self): - """ - Generate ECDH public parameter, by choosing a random private value - in ] 0, self.ec_parameters.generator.order() [ and then multiplying - the generator of the group self.ec_parameters.base with the - private value. The public point is returned as a bitstring. - The private parameter is internally available for further - secret generation (using .gen_secret()). - - Note that 'secret', 'secret_point', 'other_pub' and 'other_pub_point' - attributes of the instance are reset by the call. - """ - self.other_pub = None - self.secret = None - - params = self.ec_parameters - if params.ec_type == 1: - # Variables, for readability - order = params.curve.order - base = params.curve.generator - - # Ephemeral private key generation : self.priv in [1..order-1] - self.priv = random.randint(1, order-1) - - # Scalar multiplication of priv and base point in pub_point - pub_point = self.priv * base - - # Encode the public key to be sent according to our point_format - self.pub = encode_point(pub_point, point_format=self.point_format) - else: - raise Exception("No support for ec_type %d" % params.ec_type) - - return self.pub - - def gen_secret(self, other_pub): - """ - Given the peer's public point 'other_pub' as an octet string, - the shared secret is computed by multiplying the value with - self.priv which was generated with .gen_public_params() - """ - if type(other_pub) is not str: - blen = self.ec_parameters.curve.baselen - if self.point_format == 1: - other_pub = pkcs_i2osp(other_pub, blen) - else: - other_pub = pkcs_i2osp(other_pub, 2*blen+1) - - self.other_pub = other_pub - - z = "" - params = self.ec_parameters - if params.ec_type == 1: - # Get underlying variables for readability - ec = params.curve - curveFp = ec.curve - order = ec.order - - x, y = extract_coordinates(self.other_pub, curveFp) - - # Construct the other_pub_point with integers (mod p) x and y - other_pub_point = Point(curveFp, x, y, order) - - # Scalar multiplication with ephemeral private key - secret_point = self.priv * other_pub_point - - # Shared secret is x-coordinate of secret_point as an octet string - secret_long = secret_point.x() - - # Note that this string never depends on point_format - z = pkcs_i2osp(secret_long, ec.baselen) - self.secret = z - else: - raise Exception("No support for ec_type %d" % params.ec_type) - - return z - - def check_params(self): - #XXX Do me, maybe - pass - diff --git a/scapy/layers/tls/crypto/ffdh.py b/scapy/layers/tls/crypto/ffdh.py index fd250d45391e5c2f97118d21cdc55084b0ebb122..7451c96e52788284fc101ecf1b149038a66d9657 100644 --- a/scapy/layers/tls/crypto/ffdh.py +++ b/scapy/layers/tls/crypto/ffdh.py @@ -4,57 +4,15 @@ ## This program is published under a GPLv2 license """ -Primitive Finite Field Diffie-Hellman module. - - -We prefer using 'FFDH' rather than 'DH' so as to differentiate it from 'ECDH'. - -This module provides: - -- a set of modp* classes providing verified DH groups parameters - (generator, modulus, modulus length). They are extracted from various - IKE-related RFC (RFC 4306, RFC 3526). Those group parameter classes - are keyed by modulus length in dh_params dictionary. For instance, - to get access to 1024 bits modulus length DH parameters, you can simply - do dh_params[1024] - -- a function dh_import_params() that converts PKCS#3-encoded DH parameters - into a 3-tuple (g, m, mLen). The PKCS#3-encoded parameters can be - provided in PEM or DER format, directly or by providing a filename. - The function directly reuses openssl command line utility - -- a FFDHParams() class, which should be instantiated with some group - parameters and provide access to methods for generating DH public values - and DH shared secret - -------- Example of use: - -In this example, we will use internally available group params - -a = ffdh_params[2048] # get 2048 bit modulus FFDH group params -p = FFDHParams(a.g, a.m, a.mLen) # instantiate our params -myPub = p.gen_public_params() # generate public params (private part - # is stored internally) - - --> send myPub to the peer - <-- get other_pub from the peer - -z = p.gen_secret(other_pub) # Compute shared secret from peer's - # public value. - -This implementation is based on information provided by RSA Data Security -PKCS#3 document available from their FTP site at the following address: - - ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-3.asc +This is a register for DH groups from RFC 3526 and RFC 4306. +XXX These groups (and the ones from RFC 7919) should be registered to +the cryptography library. And this file should eventually be removed. """ - -import popen2 -import random -import struct +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.asymmetric import dh from scapy.utils import long_converter -from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp class modp768: # From RFC 4306 @@ -234,99 +192,108 @@ class modp8192: # From RFC 3526 60C980DD 98EDD3DF FFFFFFFF FFFFFFFF""") mLen = 8192 -ffdh_params = { 768: modp768 , - 1024: modp1024, - 1536: modp1536, - 2048: modp2048, - 3072: modp3072, - 4096: modp4096, - 6144: modp6144, - 8192: modp8192 } - - -class FFDHParams(object): - """ - Finite-Field Diffie-Hellman parameters. - self.priv is an integer. Its value may remain unknown. - self.pub, self.other_pub, and finally self.secret, are also integers. - Default group parameters relate to the 2048-bit group from RFC 3526. - """ - def __init__(self, g=ffdh_params[2048].g, - m=ffdh_params[2048].m, - mLen=ffdh_params[2048].mLen): - """ - g: group (2, 5, ...). Can be provided as a string or long. - m: prime modulus. Can be provided as a string or long. - mLen: prime modulus length in bits. - """ - if type(g) is str: - g = pkcs_os2ip(g) - if type(m) is str: - m = pkcs_os2ip(m) - - self.g = long(g) - self.m = long(m) - self.mLen = mLen - - self.priv = None - self.pub = None - self.other_pub = None - self.secret = None - - def gen_public_params(self): - """ - Generate FFDH public parameter, by choosing a random private - value in ] 0, p-1 [ and then exponentiating the generator of - the group with the private value. The public parameter is - returned as an octet string. The private parameter is internally - available for further secret generation (using .gen_secret()). - - Note that 'secret' and 'other_pub' attribute of the instance - are reset by the call. - """ - self.other_pub = None - self.secret = None - - # Private key generation : 0 < x < p-1 - x = random.randint(1, self.m-2) - self.priv = x - - # Exponentiation - y = pow(self.g, self.priv, self.m) - self.pub = y - - # Integer-to-octet-string conversion - y = pkcs_i2osp(y, self.mLen/8) - - return y - - def gen_secret(self, other_pub): - """ - Given the peer's public value 'other_pub' provided as an octet string, - the shared secret is computed by exponentiating the value using - internally stored private value (self.priv, generated during - public_parameter generation using .gen_public_params()). - - Computed secret is returned as a bitstring and stored internally. - - No specific check is done on 'other_pub' before exponentiation. - """ - if type(other_pub) is str: - other_pub = pkcs_os2ip(other_pub) - - # Octet-string-to-integer conversion - self.other_pub = other_pub - - # Exponentiation - z = pow(other_pub, self.priv, self.m) - - # Integer-to-octet-string conversion - z = pkcs_i2osp(z, self.mLen/8) - self.secret = z - - return z - - def check_params(self): - #XXX Do me, maybe - pass +_ffdh_raw_params = { 'modp768' : modp768, + 'modp1024': modp1024, + 'modp1536': modp1536, + 'modp2048': modp2048, + 'modp3072': modp3072, + 'modp4096': modp4096, + 'modp6144': modp6144, + 'modp8192': modp8192 } + +FFDH_GROUPS = {} +for name, group in _ffdh_raw_params.iteritems(): + pn = dh.DHParameterNumbers(group.m, group.g) + params = pn.parameters(default_backend()) + FFDH_GROUPS[name] = [params, group.mLen] + + +#from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp +# +# +#class FFDHParams(object): +# """ +# Finite-Field Diffie-Hellman parameters. +# self.priv is an integer. Its value may remain unknown. +# self.pub, self.other_pub, and finally self.secret, are also integers. +# Default group parameters relate to the 2048-bit group from RFC 3526. +# """ +# def __init__(self, g=ffdh_params[2048].g, +# m=ffdh_params[2048].m, +# mLen=ffdh_params[2048].mLen): +# """ +# g: group (2, 5, ...). Can be provided as a string or long. +# m: prime modulus. Can be provided as a string or long. +# mLen: prime modulus length in bits. +# """ +# if type(g) is str: +# g = pkcs_os2ip(g) +# if type(m) is str: +# m = pkcs_os2ip(m) +# +# self.g = long(g) +# self.m = long(m) +# self.mLen = mLen +# +# self.priv = None +# self.pub = None +# self.other_pub = None +# self.secret = None +# +# def gen_public_params(self): +# """ +# Generate FFDH public parameter, by choosing a random private +# value in ] 0, p-1 [ and then exponentiating the generator of +# the group with the private value. The public parameter is +# returned as an octet string. The private parameter is internally +# available for further secret generation (using .gen_secret()). +# +# Note that 'secret' and 'other_pub' attribute of the instance +# are reset by the call. +# """ +# self.other_pub = None +# self.secret = None +# +# # Private key generation : 0 < x < p-1 +# x = random.randint(1, self.m-2) +# self.priv = x +# +# # Exponentiation +# y = pow(self.g, self.priv, self.m) +# self.pub = y +# +# # Integer-to-octet-string conversion +# y = pkcs_i2osp(y, self.mLen/8) +# +# return y +# +# def gen_secret(self, other_pub): +# """ +# Given the peer's public value 'other_pub' provided as an octet string, +# the shared secret is computed by exponentiating the value using +# internally stored private value (self.priv, generated during +# public_parameter generation using .gen_public_params()). +# +# Computed secret is returned as a bitstring and stored internally. +# +# No specific check is done on 'other_pub' before exponentiation. +# """ +# if type(other_pub) is str: +# other_pub = pkcs_os2ip(other_pub) +# +# # Octet-string-to-integer conversion +# self.other_pub = other_pub +# +# # Exponentiation +# z = pow(other_pub, self.priv, self.m) +# +# # Integer-to-octet-string conversion +# z = pkcs_i2osp(z, self.mLen/8) +# self.secret = z +# +# return z +# +# def check_params(self): +# #XXX Do me, maybe +# pass diff --git a/scapy/layers/tls/crypto/h_mac.py b/scapy/layers/tls/crypto/h_mac.py index b399f56232528f2ca4ed3c0769cd7d2cf03edf48..6be3803b9c41b80050d7a2f5561d0415deddf6dd 100644 --- a/scapy/layers/tls/crypto/h_mac.py +++ b/scapy/layers/tls/crypto/h_mac.py @@ -12,6 +12,11 @@ import hmac from scapy.layers.tls.crypto.hash import tls_hash_algs +SSLv3_PAD1_MD5 = "\x36"*48 +SSLv3_PAD1_SHA1 = "\x36"*40 +SSLv3_PAD2_MD5 = "\x5c"*48 +SSLv3_PAD2_SHA1 = "\x5c"*40 + tls_hmac_algs = {} class _GenericHMACMetaclass(type): @@ -54,6 +59,23 @@ class _GenericHMAC(object): raise HMACError return hmac.new(self.key, tbd, self.hash_alg.hash_cls).digest() + def digest_sslv3(self, tbd): + if self.key is None: + raise HMACError + + h = self.hash_alg() + if h.name == "SHA": + pad1 = SSLv3_PAD1_SHA1 + pad2 = SSLv3_PAD2_SHA1 + elif h.name == "MD5": + pad1 = SSLv3_PAD1_MD5 + pad2 = SSLv3_PAD2_MD5 + else: + raise HMACError("Provided hash does not work with SSLv3.") + + return h.digest(self.key + pad2 + + h.digest(self.key + pad1 + tbd)) + class Hmac_NULL(_GenericHMAC): hmac_len = 0 @@ -62,6 +84,9 @@ class Hmac_NULL(_GenericHMAC): def digest(self, tbd): return "" + def digest_sslv3(self, tbd): + return "" + class Hmac_MD5(_GenericHMAC): pass diff --git a/scapy/layers/tls/crypto/kx_algs.py b/scapy/layers/tls/crypto/kx_algs.py index 4cf908d256185466151d916864434c5c935397a6..501e22ff1aad865d5a085b1eb8cd8ad507059946 100644 --- a/scapy/layers/tls/crypto/kx_algs.py +++ b/scapy/layers/tls/crypto/kx_algs.py @@ -33,6 +33,7 @@ class _GenericKXMetaclass(type): the_class.export = kx_name.endswith("_EXPORT") the_class.anonymous = "_anon_" in kx_name the_class.no_ske = not ("DHE" in kx_name or "_anon_" in kx_name) + the_class.no_ske &= not the_class.export tls_kx_algs[kx_name[3:]] = the_class return the_class diff --git a/scapy/layers/tls/crypto/pkcs1.py b/scapy/layers/tls/crypto/pkcs1.py index 7e3a64bbff3e209c484645ee1eeb6780b80b81ec..7da7182d30ca4a20c282f25f20b103791470eef7 100644 --- a/scapy/layers/tls/crypto/pkcs1.py +++ b/scapy/layers/tls/crypto/pkcs1.py @@ -1,10 +1,14 @@ ## This file is part of Scapy -## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard -## 2015, 2016 Maxence Tury +## Copyright (C) 2008 Arnaud Ebalard <arno@natisbad.org> +## 2015, 2016 Maxence Tury <maxence.tury@ssi.gouv.fr> ## This program is published under a GPLv2 license """ PKCS #1 methods as defined in RFC 3447. + +XXX We cannot rely solely on the cryptography library, because it does not +support our "tls" hash used with TLS 1.0. Once it is added to (or from) the +library, most of the present module should be removed. """ import os, popen2, tempfile @@ -72,18 +76,20 @@ def pkcs_ilen(n): i += 1 return i -# for every hash function a tuple is provided, giving access to + +##################################################################### +# Hash functions +##################################################################### + +# For every hash function a tuple is provided, giving access to # - hash output length in byte # - associated hash function that take data to be hashed as parameter # XXX I do not provide update() at the moment. # - DER encoding of the leading bits of digestInfo (the hash value # will be concatenated to create the complete digestInfo). # -# Notes: -# - MD4 asn.1 value should be verified. Also, as stated in -# PKCS#1 v2.1, MD4 should not be used. -# - 'tls' one is the concatenation of both md5 and sha1 hashes used -# by SSL/TLS when signing/verifying things +# Note that 'tls' is the concatenation of both md5 and sha1 hashes used by +# SSL/TLS 1.0 when signing/verifying things. _hashFuncParams = {} if conf.crypto_valid: @@ -124,12 +130,18 @@ if conf.crypto_valid: } def mapHashFunc(hashStr): + if hashStr == "tls": + raise Exception("mapHashFunc is not supposed to be called on 'tls'") try: - return _hashFuncParams[hashStr][1] + return _hashFuncParams[hashStr][1]() except: raise Exception("Unknown hash function %s" % hashStr) +##################################################################### +# Some more PKCS helpers +##################################################################### + def pkcs_mgf1(mgfSeed, maskLen, h): """ Implements generic MGF1 Mask Generation function as described in @@ -308,165 +320,166 @@ def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447 return EM # 6) -# XXX should add other pgf1 instance in a better fashion. +##################################################################### +# Asymmetric Cryptography wrappers +##################################################################### -def create_ca_file(anchor_list, filename): - """ - Concatenate all the certificates (PEM format for the export) in - 'anchor_list' and write the result to file 'filename'. On success - 'filename' is returned, None otherwise. +class _EncryptAndVerifyRSA(object): - If you are used to OpenSSL tools, this function builds a CAfile - that can be used for certificate and CRL check. + def _rsaep(self, m): + """ + Internal method providing raw RSA encryption, i.e. simple modular + exponentiation of the given message representative 'm', a long + between 0 and n-1. - Also see create_temporary_ca_file(). - """ - try: - f = open(filename, "w") - for a in anchor_list: - s = a.output(fmt="PEM") - f.write(s) - f.close() - except: - return None - return filename + This is the encryption primitive RSAEP described in PKCS#1 v2.1, + i.e. RFC 3447 Sect. 5.1.1. -def create_temporary_ca_file(anchor_list): - """ - Concatenate all the certificates (PEM format for the export) in - 'anchor_list' and write the result to file to a temporary file - using mkstemp() from tempfile module. On success 'filename' is - returned, None otherwise. + Input: + m: message representative, a long between 0 and n-1, where + n is the key modulus. - If you are used to OpenSSL tools, this function builds a CAfile - that can be used for certificate and CRL check. - """ - try: - f, fname = tempfile.mkstemp() - for a in anchor_list: - s = a.output(fmt="PEM") - l = os.write(f, s) - os.close(f) - except: - return None - return fname + Output: + ciphertext representative, a long between 0 and n-1 -def create_temporary_ca_path(anchor_list, folder): - """ - Create a CA path folder as defined in OpenSSL terminology, by - storing all certificates in 'anchor_list' list in PEM format - under provided 'folder' and then creating the associated links - using the hash as usually done by c_rehash. + Not intended to be used directly. Please, see encrypt() method. + """ - Note that you can also include CRL in 'anchor_list'. In that - case, they will also be stored under 'folder' and associated - links will be created. + n = self.modulus + if isinstance(m, int): + m = long(m) + if (not isinstance(m, long)) or m > n-1: + warning("Key._rsaep() expects a long between 0 and n-1") + return None - In folder, the files are created with names of the form - 0...ZZ.pem. If you provide an empty list, folder will be created - if it does not already exist, but that's all. + return pow(m, self.pubExp, n) - The number of certificates written to folder is returned on - success, None on error. - """ - # We should probably avoid writing duplicate anchors and also - # check if they are all certs. - try: - if not os.path.isdir(folder): - os.makedirs(folder) - except: - return None - l = len(anchor_list) - if l == 0: - return None - fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10)) - i = 0 - try: - for a in anchor_list: - fname = os.path.join(folder, fmtstr % i) - f = open(fname, "w") - s = a.output(fmt="PEM") - f.write(s) - f.close() - i += 1 - except: - return None + @crypto_validator + def encrypt(self, m, t="pkcs", h=None, mgf=None, L=None): + if h == "tls" or t is None: + #return self.encrypt_legacy(m, t=t, h=h, mgf=mgf, L=L) + warning("Cannot call encrypt_legacy anymore.") + return None - r,w=popen2.popen2("c_rehash %s" % folder) - r.close(); w.close() + if h is not None: + h = mapHashFunc(h) - return l + if t == "pkcs": + pad = padding.PKCS1v15() + elif t == "oaep": + pad = padding.OAEP(mgf=mgf(h), algorithm=h, label=L) + else: + warning("Key.encrypt(): Unknown encryption type (%s) provided" % t) + return None + return self.pubkey.encrypt(m, pad) -##################################################################### -# Public Key Cryptography related stuff -##################################################################### + ### Below are verification related methods -class _EncryptAndVerifyRSA(object): - @crypto_validator - def encrypt(self, m, t=None, h=None, mgf=None, L=None): + def _rsavp1(self, s): """ - Encrypt message 'm' using 't' encryption scheme where 't' can be: + Internal method providing raw RSA verification, i.e. simple modular + exponentiation of the given signature representative 'c', an integer + between 0 and n-1. - - None: the message 'm' is directly applied the RSAEP encryption - primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 - Sect 5.1.1. Simply put, the message undergo a modular - exponentiation using the public key. Additionnal method - parameters are just ignored. + This is the signature verification primitive RSAVP1 described in + PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2. - -'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption - scheme as described in section 7.2.1 of RFC 3447. In that - context, other parameters ('h', 'mgf', 'l') are not used. + Input: + s: signature representative, an integer between 0 and n-1, + where n is the key modulus. - -'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption - scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect - 7.1.1. In that context, + Output: + message representative, an integer between 0 and n-1 - o 'h' parameter provides the name of the hash method to use. - Possible values are "md2", "md4", "md5", "sha1", "tls", - "sha224", "sha256", "sha384" and "sha512". If none is - provided, sha1 is used. + Not intended to be used directly. Please, see verify() method. + """ + return self._rsaep(s) - o 'mgf' is the mask generation function. By default, mgf - is derived from the provided hash function using the - generic MGF1 (see pkcs_mgf1() for details). - - o 'L' is the optional label to be associated with the message. - If not provided, the default value is used, i.e the empty - string. No check is done on the input limitation of the hash - function regarding the size of 'L' (for instance, 2^61 - 1 - for SHA-1). You have been warned. + def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None): """ - if h is not None: - h = mapHashFunc(h) - if t is None: # Raw encryption - return self.key.encrypt( - m, - padding.AsymmetricPadding(), - ) - elif t == "pkcs": - return self.key.encrypt( - m, - padding.PKCS1v15(), - ) + Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2 + of RFC 3447 - elif t == "oaep": - return self.key.encrypt( - m, - padding.OAEP( - mgf=mgf(h()), - algorithm=h(), - label=L, - ), - ) + Input: + M: message whose signature is to be verified + S: signature to be verified, an octet string of length k, where k + is the length in octets of the RSA modulus n. - else: - warning("Key.encrypt(): Unknown encryption type (%s) provided" % t) - return None + Output: + True is the signature is valid. False otherwise. + """ - @crypto_validator - def verify(self, M, S, t=None, h=None, mgf=None, sLen=None): + # Set default parameters if not provided + if h is None: # By default, sha1 + h = "sha1" + if not _hashFuncParams.has_key(h): + warning("Key._rsassa_pss_verify(): unknown hash function " + "provided (%s)" % h) + return False + if mgf is None: # use mgf1 with underlying hash function + mgf = lambda x,y: pkcs_mgf1(x, y, h) + if sLen is None: # use Hash output length (A.2.3 of RFC 3447) + hLen = _hashFuncParams[h][0] + sLen = hLen + + # 1) Length checking + modBits = self.modulusLen + k = modBits / 8 + if len(S) != k: + return False + + # 2) RSA verification + s = pkcs_os2ip(S) # 2.a) + m = self._rsavp1(s) # 2.b) + emLen = math.ceil((modBits - 1) / 8.) # 2.c) + EM = pkcs_i2osp(m, emLen) + + # 3) EMSA-PSS verification + result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen) + + return result # 4) + + + def _rsassa_pkcs1_v1_5_verify(self, M, S, h): + """ + Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in + Sect. 8.2.2 of RFC 3447. + + Input: + M: message whose signature is to be verified, an octet string + S: signature to be verified, an octet string of length k, where + k is the length in octets of the RSA modulus n + h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', + 'sha256', 'sha384'). + + Output: + True if the signature is valid. False otherwise. + """ + + # 1) Length checking + k = self.modulusLen / 8 + if len(S) != k: + warning("invalid signature (len(S) != k)") + return False + + # 2) RSA verification + s = pkcs_os2ip(S) # 2.a) + m = self._rsavp1(s) # 2.b) + EM = pkcs_i2osp(m, k) # 2.c) + + # 3) EMSA-PKCS1-v1_5 encoding + EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h) + if EMPrime is None: + warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.") + return False + + # 4) Comparison + return EM == EMPrime + + + def verify_legacy(self, M, S, t=None, h=None, mgf=None, sLen=None): """ Verify alleged signature 'S' is indeed the signature of message 'M' using 't' signature scheme where 't' can be: @@ -503,33 +516,51 @@ class _EncryptAndVerifyRSA(object): default value (the byte length of the hash value for provided algorithm) by providing another one with that parameter. """ - if h is not None: - h = mapHashFunc(h) - if t is None: # RSAVP1 - pad_inst = padding.AsymmetricPadding() + S = pkcs_os2ip(S) + n = self.modulus + if S > n-1: + warning("Signature to be verified is too long for key modulus") + return False + m = self._rsavp1(S) + if m is None: + return False + l = int(math.ceil(math.log(m, 2) / 8.)) # Hack + m = pkcs_i2osp(m, l) + return M == m elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY if h is None: - h = hashes.SHA1 - pad_inst = padding.PKCS1v15() + h = "sha1" + return self._rsassa_pkcs1_v1_5_verify(M, S, h) elif t == "pss": # RSASSA-PSS-VERIFY - pad_inst = padding.PSS(mgf=mgf, salt_length=sLen) + return self._rsassa_pss_verify(M, S, h, mgf, sLen) + else: + warning("Key.verify(): Unknown signature type (%s) provided" % t) + return None + + @crypto_validator + def verify(self, M, S, t="pkcs", h=None, mgf=None, sLen=None): + if h == "tls" or t is None: + return self.verify_legacy(M, S, t=t, h=h, mgf=mgf, sLen=sLen) + if h is not None: + h = mapHashFunc(h) + + if t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY + pad = padding.PKCS1v15() + elif t == "pss": # RSASSA-PSS-VERIFY + pad = padding.PSS(mgf=mgf(h), salt_length=sLen) else: warning("Key.verify(): Unknown signature type (%s) provided" % t) return None try: - self.key.verify( - signature=S, - data=M, - padding=pad_inst, - algorithm=h(), - ) + self.pubkey.verify(signature=S, data=M, padding=pad, algorithm=h) return True except InvalidSignature: return False + class _DecryptAndSignRSA(object): ### Below are decryption related methods. Encryption ones are inherited ### from PubKey @@ -557,210 +588,30 @@ class _DecryptAndSignRSA(object): if isinstance(c, int): c = long(c) if (not isinstance(c, long)) or c > n-1: - warning("Key._rsadp() expects a long between 0 and n-1") - return None - - return self.key.decrypt(c) - - - def _rsaes_pkcs1_v1_5_decrypt(self, C): - """ - Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section - 7.2.2 of RFC 3447. - - Input: - C: ciphertext to be decrypted, an octet string of length k, where - k is the length in octets of the RSA modulus n. - - Output: - an octet string of length k at most k - 11 - - on error, None is returned. - """ - - # 1) Length checking - cLen = len(C) - k = self.modulusLen / 8 - if cLen != k or k < 11: - warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error " - "(cLen != k or k < 11)") + warning("Key._rsaep() expects a long between 0 and n-1") return None - # 2) RSA decryption - c = pkcs_os2ip(C) # 2.a) - m = self._rsadp(c) # 2.b) - EM = pkcs_i2osp(m, k) # 2.c) - - # 3) EME-PKCS1-v1_5 decoding - - # I am aware of the note at the end of 7.2.2 regarding error - # conditions reporting but the one provided below are for _local_ - # debugging purposes. --arno - - if EM[0] != '\x00': - warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " - "(first byte is not 0x00)") - return None - - if EM[1] != '\x02': - warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " - "(second byte is not 0x02)") - return None - - tmp = EM[2:].split('\x00', 1) - if len(tmp) != 2: - warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " - "(no 0x00 to separate PS from M)") - return None - - PS, M = tmp - if len(PS) < 8: - warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " - "(PS is less than 8 byte long)") - return None - - return M # 4) - - - def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None): - """ - Internal method providing RSAES-OAEP-DECRYPT as defined in Sect. - 7.1.2 of RFC 3447. Not intended to be used directly. Please, see - encrypt() method for type "OAEP". - - - Input: - C : ciphertext to be decrypted, an octet string of length k, where - k = 2*hLen + 2 (k denotes the byte length of the RSA modulus - and hLen the byte length of the hash function output) - h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', - 'sha256', 'sha384'). 'sha1' is used if none is provided. - mgf: the mask generation function f : seed, maskLen -> mask - L : optional label whose association with the message is to be - verified; the default value for L, if not provided is the empty - string. - - Output: - message, an octet string of length k mLen, where mLen <= k-2*hLen-2 - - On error, None is returned. - """ - # The steps below are the one described in Sect. 7.1.2 of RFC 3447. - - # 1) Length Checking - # 1.a) is not done - if h is None: - h = "sha1" - if not _hashFuncParams.has_key(h): - warning("Key._rsaes_oaep_decrypt(): unknown hash function %s." % h) - return None - hLen = _hashFuncParams[h][0] - hFun = _hashFuncParams[h][2] - k = self.modulusLen / 8 - cLen = len(C) - if cLen != k: # 1.b) - warning("Key._rsaes_oaep_decrypt(): decryption error. " - "(cLen != k)") - return None - if k < 2*hLen + 2: - warning("Key._rsaes_oaep_decrypt(): decryption error. " - "(k < 2*hLen + 2)") - return None + return pow(c, self.privExp, n) - # 2) RSA decryption - c = pkcs_os2ip(C) # 2.a) - m = self._rsadp(c) # 2.b) - EM = pkcs_i2osp(m, k) # 2.c) - # 3) EME-OAEP decoding - if L is None: # 3.a) - L = "" - lHash = hFun(L) - Y = EM[:1] # 3.b) - if Y != '\x00': - warning("Key._rsaes_oaep_decrypt(): decryption error. " - "(Y is not zero)") - return None - maskedSeed = EM[1:1+hLen] - maskedDB = EM[1+hLen:] - if mgf is None: - mgf = lambda x,y: pkcs_mgf1(x, y, h) - seedMask = mgf(maskedDB, hLen) # 3.c) - seed = strxor(maskedSeed, seedMask) # 3.d) - dbMask = mgf(seed, k - hLen - 1) # 3.e) - DB = strxor(maskedDB, dbMask) # 3.f) - - # I am aware of the note at the end of 7.1.2 regarding error - # conditions reporting but the one provided below are for _local_ - # debugging purposes. --arno - - lHashPrime = DB[:hLen] # 3.g) - tmp = DB[hLen:].split('\x01', 1) - if len(tmp) != 2: - warning("Key._rsaes_oaep_decrypt(): decryption error. " - "(0x01 separator not found)") - return None - PS, M = tmp - if PS != '\x00'*len(PS): - warning("Key._rsaes_oaep_decrypt(): decryption error. " - "(invalid padding string)") - return None - if lHash != lHashPrime: - warning("Key._rsaes_oaep_decrypt(): decryption error. " - "(invalid hash)") + def decrypt(self, C, t="pkcs", h=None, mgf=None, L=None): + if h == "tls" or t is None: + #return self.decrypt_legacy(C, t=t, h=h, mgf=mgf, L=L) + warning("Cannot call decrypt_legacy anymore.") return None - return M # 4) - - - def decrypt(self, C, t=None, h=None, mgf=None, L=None): - """ - Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be: - - - None: the ciphertext 'C' is directly applied the RSADP decryption - primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 - Sect 5.1.2. Simply, put the message undergo a modular - exponentiation using the private key. Additionnal method - parameters are just ignored. - - - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT - decryption scheme as described in section 7.2.2 of RFC 3447. - In that context, other parameters ('h', 'mgf', 'l') are not - used. - - - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT - decryption scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 - Sect 7.1.2. In that context, - - o 'h' parameter provides the name of the hash method to use. - Possible values are "md2", "md4", "md5", "sha1", "tls", - "sha224", "sha256", "sha384" and "sha512". If None is - provided, sha1 is used by default. - - o 'mgf' is the mask generation function. By default, mgf - is derived from the provided hash function using the - generic MGF1 (see pkcs_mgf1() for details). - - o 'L' is the optional label to be associated with the - message. If not provided, the default value is used, i.e - the empty string. No check is done on the input limitation - of the hash function regarding the size of 'L' (for - instance, 2^61 - 1 for SHA-1). You have been warned. - """ - if t is None: - C = pkcs_os2ip(C) - c = self._rsadp(C) - l = int(math.ceil(math.log(c, 2) / 8.)) # Hack - return pkcs_i2osp(c, l) - elif t == "pkcs": - return self._rsaes_pkcs1_v1_5_decrypt(C) + if h is not None: + h = mapHashFunc(h) + if t == "pkcs": + pad = padding.PKCS1v15() elif t == "oaep": - return self._rsaes_oaep_decrypt(C, h, mgf, L) - + pad = padding.OAEP(mgf=mgf(h), algorithm=h, label=L) else: warning("Key.decrypt(): Unknown decryption type (%s) provided" % t) return None + return self.key.decrypt(C, pad) + ### Below are signature related methods. ### Verification methods are inherited from PubKey. @@ -859,7 +710,7 @@ class _DecryptAndSignRSA(object): return S # 3) - def sign(self, M, t=None, h=None, mgf=None, sLen=None): + def sign_legacy(self, M, t=None, h=None, mgf=None, sLen=None): """ Sign message 'M' using 't' signature scheme where 't' can be: @@ -892,7 +743,6 @@ class _DecryptAndSignRSA(object): default value (the byte length of the hash value for provided algorithm) by providing another one with that parameter. """ - if t is None: # RSASP1 M = pkcs_os2ip(M) n = self.modulus @@ -903,17 +753,123 @@ class _DecryptAndSignRSA(object): if s is None: return None return pkcs_i2osp(s, self.modulusLen/8) - elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN if h is None: h = "sha1" return self._rsassa_pkcs1_v1_5_sign(M, h) - elif t == "pss": # RSASSA-PSS-SIGN return self._rsassa_pss_sign(M, h, mgf, sLen) + else: + warning("Key.sign(): Unknown signature type (%s) provided" % t) + return None + + def sign(self, M, t="pkcs", h=None, mgf=None, sLen=None): + if h == "tls" or t is None: + return self.sign_legacy(M, t=t, h=h, mgf=mgf, sLen=sLen) + if h is not None: + h = mapHashFunc(h) + + if t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN + pad = padding.PKCS1v15() + elif t == "pss": # RSASSA-PSS-SIGN + pad = padding.PSS(mgf=mgf(h), salt_length=sLen) else: warning("Key.sign(): Unknown signature type (%s) provided" % t) return None + return self.key.sign(M, pad, h) + + + +##################################################################### +# CA files helpers +##################################################################### + +def create_ca_file(anchor_list, filename): + """ + Concatenate all the certificates (PEM format for the export) in + 'anchor_list' and write the result to file 'filename'. On success + 'filename' is returned, None otherwise. + + If you are used to OpenSSL tools, this function builds a CAfile + that can be used for certificate and CRL check. + + Also see create_temporary_ca_file(). + """ + try: + f = open(filename, "w") + for a in anchor_list: + s = a.output(fmt="PEM") + f.write(s) + f.close() + except: + return None + return filename + +def create_temporary_ca_file(anchor_list): + """ + Concatenate all the certificates (PEM format for the export) in + 'anchor_list' and write the result to file to a temporary file + using mkstemp() from tempfile module. On success 'filename' is + returned, None otherwise. + + If you are used to OpenSSL tools, this function builds a CAfile + that can be used for certificate and CRL check. + """ + try: + f, fname = tempfile.mkstemp() + for a in anchor_list: + s = a.output(fmt="PEM") + l = os.write(f, s) + os.close(f) + except: + return None + return fname + +def create_temporary_ca_path(anchor_list, folder): + """ + Create a CA path folder as defined in OpenSSL terminology, by + storing all certificates in 'anchor_list' list in PEM format + under provided 'folder' and then creating the associated links + using the hash as usually done by c_rehash. + + Note that you can also include CRL in 'anchor_list'. In that + case, they will also be stored under 'folder' and associated + links will be created. + + In folder, the files are created with names of the form + 0...ZZ.pem. If you provide an empty list, folder will be created + if it does not already exist, but that's all. + The number of certificates written to folder is returned on + success, None on error. + """ + # We should probably avoid writing duplicate anchors and also + # check if they are all certs. + try: + if not os.path.isdir(folder): + os.makedirs(folder) + except: + return None + + l = len(anchor_list) + if l == 0: + return None + fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10)) + i = 0 + try: + for a in anchor_list: + fname = os.path.join(folder, fmtstr % i) + f = open(fname, "w") + s = a.output(fmt="PEM") + f.write(s) + f.close() + i += 1 + except: + return None + + r,w=popen2.popen2("c_rehash %s" % folder) + r.close(); w.close() + + return l diff --git a/scapy/layers/tls/crypto/prf.py b/scapy/layers/tls/crypto/prf.py index 94a2aa97330175e7fc10438f59ca7e6eec7d5a2f..1f93f92569074352bfb156160ec3518e16b9e70c 100644 --- a/scapy/layers/tls/crypto/prf.py +++ b/scapy/layers/tls/crypto/prf.py @@ -215,25 +215,50 @@ class PRF(object): prior to this document when TLS 1.2 is negotiated." Cipher suites using SHA-384 were defined later on. """ + if self.tls_version <= 0x0300: - if read_or_write == "write": - d = {"client": "client", "server": "server"} - else: - d = {"client": "server", "server": "client"} - finished_label = d[con_end] + " finished" + if read_or_write == "write": + d = {"client": "CLNT", "server": "SRVR"} + else: + d = {"client": "SRVR", "server": "CLNT"} + label = d[con_end] + + sslv3_md5_pad1 = "\x36"*48 + sslv3_md5_pad2 = "\x5c"*48 + sslv3_sha1_pad1 = "\x36"*40 + sslv3_sha1_pad2 = "\x5c"*40 + + md5 = tls_hash_algs["MD5"]() + sha1 = tls_hash_algs["SHA"]() - if self.tls_version <= 0x0302: - s1 = tls_hash_algs["MD5"]().digest(handshake_msg) - s2 = tls_hash_algs["SHA"]().digest(handshake_msg) + md5_hash = md5.digest(master_secret + sslv3_md5_pad2 + + md5.digest(handshake_msg + label + + master_secret + sslv3_md5_pad1)) + sha1_hash = sha1.digest(master_secret + sslv3_sha1_pad2 + + sha1.digest(handshake_msg + label + + master_secret + sslv3_sha1_pad1)) + verify_data = md5_hash + sha1_hash - verify_data = self.prf(master_secret, finished_label, s1 + s2, 12) else: - if self.hash_name in ["MD5", "SHA"]: - h = tls_hash_algs["SHA256"]() + + if read_or_write == "write": + d = {"client": "client", "server": "server"} + else: + d = {"client": "server", "server": "client"} + label = d[con_end] + " finished" + + if self.tls_version <= 0x0302: + s1 = tls_hash_algs["MD5"]().digest(handshake_msg) + s2 = tls_hash_algs["SHA"]().digest(handshake_msg) + verify_data = self.prf(master_secret, label, s1 + s2, 12) else: - h = tls_hash_algs[self.hash_name]() - s = h.digest(handshake_msg) - verify_data = self.prf(master_secret, finished_label, s, 12) + if self.hash_name in ["MD5", "SHA"]: + h = tls_hash_algs["SHA256"]() + else: + h = tls_hash_algs[self.hash_name]() + s = h.digest(handshake_msg) + verify_data = self.prf(master_secret, label, s, 12) + return verify_data def postprocess_key_for_export(self, key, client_random, server_random, diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py index 316c4e1f74447e7aed06ead0825bd99c687d42ca..5a6ae985afb229accd7cbafd489becffb542634a 100644 --- a/scapy/layers/tls/handshake.py +++ b/scapy/layers/tls/handshake.py @@ -21,6 +21,7 @@ from scapy.layers.tls.basefields import _tls_version, _TLSVersionField from scapy.layers.tls.keyexchange import (_tls_named_curves, _TLSServerParamsField, _TLSSignature, _TLSSignatureField, _tls_hash_sig, SigAndHashAlgsField, + ServerRSAParams, SigAndHashAlgsLenField) from scapy.layers.tls.session import (_GenericTLSSessionInheritance, writeConnState, @@ -1031,8 +1032,11 @@ class TLSServerKeyExchange(_TLSHandshake): if fval is None: s = self.tls_session if s.pwcs: - cls = s.pwcs.key_exchange.server_kx_msg_cls("\x03") - cls = cls(tls_session=s) + if s.pwcs.key_exchange.export: + cls = ServerRSAParams(tls_session=s) + else: + cls = s.pwcs.key_exchange.server_kx_msg_cls("\x03") + cls = cls(tls_session=s) try: cls.fill_missing() except: @@ -1255,11 +1259,19 @@ class TLSClientKeyExchange(_TLSHandshake): ### Finished ### ############################################################################### +class _VerifyDataField(StrLenField): + def getfield(self, pkt, s): + if pkt.tls_session.tls_version == 0x300: + sep = 36 + else: + sep = 12 + return s[sep:], s[:sep] + class TLSFinished(_TLSHandshake): name = "TLS Handshake - Finished" fields_desc = [ ByteEnumField("msgtype", 20, _tls_handshake_type), ThreeBytesField("msglen", None), - StrFixedLenField("vdata", None, 12) ] + _VerifyDataField("vdata", None) ] def build(self, *args, **kargs): fval = self.getfieldval("vdata") diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py index 1a8abd1ee529fd67550f430487edc39f1bf75587..faf69bfa80ff54a2fe06b451ffbf114743173306 100644 --- a/scapy/layers/tls/keyexchange.py +++ b/scapy/layers/tls/keyexchange.py @@ -9,33 +9,19 @@ TLS key exchange logic. import math +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import dh, ec, rsa + from scapy.config import conf from scapy.error import warning from scapy.fields import * from scapy.packet import Packet, Raw, Padding -from scapy.layers.tls.cert import PubKey +from scapy.layers.tls.cert import PubKey, PrivKey from scapy.layers.tls.session import _GenericTLSSessionInheritance from scapy.layers.tls.basefields import _tls_version, _TLSClientVersionField from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip -from scapy.layers.tls.crypto.ffdh import FFDHParams -from scapy.layers.tls.crypto.curves import encode_point - -ecdsa_support = False -try: - from ecdsa.util import sigencode_der, sigdecode_der - from scapy.layers.tls.crypto.ecdh import ECParams, ECDHParams - ecdsa_support = True -except ImportError: - import logging - log_loading = logging.getLogger("scapy.loading") - log_loading.info("Can't import python ecdsa lib. No EC-based kx.") - sigencode_der = sigdecode_der = None - -def ecdsa_warning(func): - def func_in(*args, **kwargs): - if ecdsa_support: - return func(*args, **kwargs) - return func_in +from scapy.layers.tls.crypto.ffdh import FFDH_GROUPS ############################################################################### @@ -149,7 +135,7 @@ class _TLSSignature(_GenericTLSSessionInheritance): self.sig_val = key.sign(m, t='pkcs', h='tls') else: h = _tls_hash_sig[self.sig_alg].split('+')[0] - self.sig_val = key.sign(m, t='pkcs', h=h, sigencode=sigencode_der) + self.sig_val = key.sign(m, t='pkcs', h=h) def _verify_sig(self, m, cert): """ @@ -159,8 +145,7 @@ class _TLSSignature(_GenericTLSSessionInheritance): if self.sig_val: if self.sig_alg: h = _tls_hash_sig[self.sig_alg].split('+')[0] - return cert.verify(m, self.sig_val, t='pkcs', h=h, - sigdecode=sigdecode_der) + return cert.verify(m, self.sig_val, t='pkcs', h=h) else: return cert.verify(m, self.sig_val, t='pkcs', h='tls') return False @@ -214,8 +199,7 @@ class _TLSServerParamsField(PacketField): p = ServerDHParams(m, tls_session=s) if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig: raise Exception - else: - return p + return p except: cls = _tls_server_ecdh_cls_guess(m) p = cls(m, tls_session=s) @@ -262,47 +246,53 @@ class ServerDHParams(_GenericTLSSessionInheritance): Note that we do not expect dh_params_def.g to be more than 0xff. """ s = self.tls_session - dh_params_def = s.default_ffdh_params - dh_Ys = dh_params_def.gen_public_params() - if self.dh_plen is None: - self.dh_plen = dh_params_def.mLen/8 + default_params = FFDH_GROUPS['modp2048'][0].parameter_numbers() + default_mLen = FFDH_GROUPS['modp2048'][1] + if self.dh_p is "": - self.dh_p = pkcs_i2osp(dh_params_def.m, dh_params_def.mLen/8) + self.dh_p = pkcs_i2osp(default_params.p, default_mLen/8) + if self.dh_plen is None: + self.dh_plen = len(self.dh_p) + + if self.dh_g is "": + self.dh_g = pkcs_i2osp(default_params.g, 1) if self.dh_glen is None: self.dh_glen = 1 - if self.dh_g is "": - self.dh_g = pkcs_i2osp(dh_params_def.g, 1) - if self.dh_Yslen is None: - self.dh_Yslen = len(dh_Ys) - if self.dh_Ys is "": - self.dh_Ys = dh_Ys - s.server_kx_params = FFDHParams(self.dh_g, - self.dh_p, - self.dh_plen*8) - s.server_kx_params.priv = dh_params_def.priv - s.server_kx_params.pub = pkcs_os2ip(self.dh_Ys) + p = pkcs_os2ip(self.dh_p) + g = pkcs_os2ip(self.dh_g) + real_params = dh.DHParameterNumbers(p, g).parameters(default_backend()) + + if self.dh_Ys is "": + s.server_kx_privkey = real_params.generate_private_key() + pubkey = s.server_kx_privkey.public_key() + y = pubkey.public_numbers().y + self.dh_Ys = pkcs_i2osp(y, pubkey.key_size/8) + # else, we assume that the user wrote the server_kx_privkey by himself + if self.dh_Yslen is None: + self.dh_Yslen = len(self.dh_Ys) - s.client_kx_params = FFDHParams(self.dh_g, - self.dh_p, - self.dh_plen*8) - s.client_kx_params.other_pub = pkcs_os2ip(self.dh_Ys) + if not s.client_kx_ffdh_params: + s.client_kx_ffdh_params = real_params def post_dissection(self, r): """ - XXX Do a check_params() once it has been implemented in crypto/ffdh.py. + XXX Check that the pubkey received is in the group. """ - if self.dh_g and self.dh_p and self.dh_plen: - s = self.tls_session + #if self.dh_g and self.dh_p and self.dh_Ys: #XXX remove this, probably + p = pkcs_os2ip(self.dh_p) + g = pkcs_os2ip(self.dh_g) + pn = dh.DHParameterNumbers(p, g) - dh_params_s = FFDHParams(self.dh_g, self.dh_p, self.dh_plen*8) - s.server_kx_params = dh_params_s - s.server_kx_params.pub = pkcs_os2ip(self.dh_Ys) + y = pkcs_os2ip(self.dh_Ys) + public_numbers = dh.DHPublicNumbers(y, pn) - dh_params_c = FFDHParams(self.dh_g, self.dh_p, self.dh_plen*8) - s.client_kx_params = dh_params_c - s.client_kx_params.other_pub = pkcs_os2ip(self.dh_Ys) + s = self.tls_session + s.server_kx_pubkey = public_numbers.public_key(default_backend()) + + if not s.client_kx_ffdh_params: + s.client_kx_ffdh_params = pn.parameters(default_backend()) def guess_payload_class(self, p): """ @@ -409,6 +399,10 @@ class _ECBasisField(PacketField): ## The most frequent encounter of the 3 is (by far) ServerECDHNamedCurveParams. class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance): + """ + XXX We provide parsing abilities for ExplicitPrimeParams, but there is no + 'cryptography' support, hence no context operations. + """ name = "Server ECDH parameters - Explicit Prime" fields_desc = [ ByteEnumField("curve_type", 1, _tls_ec_curve_types), FieldLenField("plen", None, length_of="p", fmt="B"), @@ -430,7 +424,6 @@ class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance): StrLenField("point", "", length_from=lambda pkt: pkt.pointlen) ] - @ecdsa_warning def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute @@ -439,89 +432,18 @@ class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance): XXX Note that if it is not set by the user, the cofactor will always be 1. It is true for most, but not all, TLS elliptic curves. - """ - s = self.tls_session - dh_params_def = s.default_ecdh_params[0] - c = dh_params_def.ec_parameters.curve - point = dh_params_def.gen_public_params() + XXX Try and create a curve with the 'cryptography' lib somehow, + extract the missing fields for filling, then set s.server_kx_privkey. + """ if self.curve_type is None: - self.curve_type = 1 - if self.plen is None: - p = c.curve.p() - self.plen = len(pkcs_i2osp(p, c.baselen)) - if self.p is "": - p = c.curve.p() - self.p = pkcs_i2osp(p, c.baselen) - if self.curve is None: - a = c.curve.a() - b = c.curve.b() - a_str = pkcs_i2osp(a, c.baselen) - b_str = pkcs_i2osp(b, c.baselen) - self.curve = ECCurvePkt(a=a_str, b=b_str) - if self.baselen is None: - self.baselen = c.baselen - if self.base is "": - self.base = encode_point(c.generator, dh_params_def.point_format) - if self.orderlen is None: - order = c.generator.order() - self.orderlen = len(pkcs_i2osp(order, c.baselen)) - if self.order is "": - order = c.generator.order() - self.order = pkcs_i2osp(order, c.baselen) - if self.cofactorlen is None: - self.cofactorlen = 1 - if self.cofactor is "": - self.cofactor = "\x01" - if self.pointlen is None: - self.pointlen = len(point) - if self.point is "": - self.point = point - - ec_params_s = ECParams(self.curve_type) - ec_params_s.set_explicit_prime_curve(self.p, - self.curve.a, self.curve.b, - self.base, self.order) - dh_params_s = ECDHParams(ec_params_s, dh_params_def.point_format) - s.server_kx_params = dh_params_s - s.server_kx_params.priv = dh_params_def.priv - s.server_kx_params.pub = self.point - - ec_params_c = ECParams(self.curve_type) - ec_params_c.set_explicit_prime_curve(self.p, - self.curve.a, self.curve.b, - self.base, self.order) - dh_params_c = ECDHParams(ec_params_c, dh_params_def.point_format) - s.client_kx_params = dh_params_c - s.client_kx_params.other_pub = self.point - - @ecdsa_warning + self.curve_type = _tls_ec_curve_types["explicit_prime"] + def post_dissection(self, pkt): """ - XXX Do a check_params() once it has been implemented in crypto/ecdh.py. + XXX Store the server_kx_pubkey. + XXX Check that the pubkey received is on the curve. """ - s = self.tls_session - - point_format = 0 - if self.point[0] in ["\x02", "\x03"]: - point_format = 1 - - ec_params_s = ECParams(self.curve_type) - ec_params_s.set_explicit_prime_curve(self.p, - self.curve.a, self.curve.b, - self.base, self.order) - dh_params_s = ECDHParams(ec_params_s, point_format) - s.server_kx_params = dh_params_s - s.server_kx_params.priv = dh_params_def.priv - s.server_kx_params.pub = self.point - - ec_params_c = ECParams(self.curve_type) - ec_params_c.set_explicit_prime_curve(self.p, - self.curve.a, self.curve.b, - self.base, self.order) - dh_params_c = ECDHParams(ec_params_c, point_format) - s.client_kx_params = dh_params_c - s.client_kx_params.other_pub = self.point def guess_payload_class(self, p): return Padding @@ -529,8 +451,8 @@ class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance): class ServerECDHExplicitChar2Params(_GenericTLSSessionInheritance): """ - XXX We provide parsing abilities for Char2Params, but the context - operations have not been implemented yet. + XXX We provide parsing abilities for Char2Params, but there is no + 'cryptography' support, hence no context operations. """ name = "Server ECDH parameters - Explicit Char2" fields_desc = [ ByteEnumField("curve_type", 2, _tls_ec_curve_types), @@ -551,19 +473,19 @@ class ServerECDHExplicitChar2Params(_GenericTLSSessionInheritance): StrLenField("point", "", length_from = lambda pkt: pkt.pointlen) ] - @ecdsa_warning def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things everytime it is called. This method can be called specifically to have things filled in a smart fashion. """ - pass + if self.curve_type is None: + self.curve_type = _tls_ec_curve_types["explicit_char2"] - @ecdsa_warning def post_dissection(self, pkt): """ - XXX Do a check_params() once it has been implemented in crypto/ecdh.py. + XXX Store the server_kx_pubkey. + XXX Check that the pubkey received is in the group. """ pass @@ -580,62 +502,72 @@ class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance): StrLenField("point", None, length_from = lambda pkt: pkt.pointlen) ] - @ecdsa_warning def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things everytime it is called. This method can be called specifically to have things filled in a smart fashion. - XXX We should check our point_format before 'point' filling. + XXX We should account for the point_format (before 'point' filling). """ s = self.tls_session - dh_params_def = s.default_ecdh_params[0] - point = dh_params_def.gen_public_params() if self.curve_type is None: - self.curve_type = 3 + self.curve_type = _tls_ec_curve_types["named_curve"] + if self.named_curve is None: - self.named_curve = dh_params_def.ec_parameters.curve.curve_id - if self.pointlen is None: - self.pointlen = len(point) + curve = ec.SECP256R1() + s.server_kx_privkey = ec.generate_private_key(curve, + default_backend()) + curve_id = 0 + for cid, name in _tls_named_curves.iteritems(): + if name == curve.name: + curve_id = cid + break + self.named_curve = curve_id + else: + curve_name = _tls_named_curves.get(self.named_curve) + if curve_name is None: + # this fallback is arguable + curve = ec.SECP256R1() + else: + curve_cls = ec._CURVE_TYPES.get(curve_name) + if curve_cls is None: + # this fallback is arguable + curve = ec.SECP256R1() + else: + curve = curve_cls() + s.server_kx_privkey = ec.generate_private_key(curve, + default_backend()) + if self.point is None: - self.point = point - - ec_params_s = ECParams(self.curve_type) - ec_params_s.set_named_curve(self.named_curve) - dh_params_s = ECDHParams(ec_params_s, dh_params_def.point_format) - s.server_kx_params = dh_params_s - s.server_kx_params.priv = dh_params_def.priv - s.server_kx_params.pub = self.point - - ec_params_c = ECParams(self.curve_type) - ec_params_c.set_named_curve(self.named_curve) - dh_params_c = ECDHParams(ec_params_c, dh_params_def.point_format) - s.client_kx_params = dh_params_c - s.client_kx_params.other_pub = self.point - - @ecdsa_warning + pubkey = s.server_kx_privkey.public_key() + self.point = pubkey.public_numbers().encode_point() + # else, we assume that the user wrote the server_kx_privkey by himself + if self.pointlen is None: + self.pointlen = len(self.point) + + if not s.client_kx_ecdh_params: + s.client_kx_ecdh_params = curve + def post_dissection(self, r): """ - XXX Do a check_params() once it has been implemented in crypto/ecdh.py. + XXX Support compressed point format. + XXX Check that the pubkey received is on the curve. """ + #point_format = 0 + #if self.point[0] in ['\x02', '\x03']: + # point_format = 1 + + #if self.named_curve and self.point: #XXX remove this, probably + curve_name = _tls_named_curves[self.named_curve] + curve = ec._CURVE_TYPES[curve_name]() + import_point = ec.EllipticCurvePublicNumbers.from_encoded_point + pubnum = import_point(curve, self.point) s = self.tls_session + s.server_kx_pubkey = pubnum.public_key(default_backend()) - point_format = 0 - if self.point[0] in ['\x02', '\x03']: - point_format = 1 - - ec_params_s = ECParams(self.curve_type) - ec_params_s.set_named_curve(self.named_curve) - dh_params_s = ECDHParams(ec_params_s, point_format) - s.server_kx_params = dh_params_s - s.server_kx_params.pub = self.point - - ec_params_c = ECParams(self.curve_type) - ec_params_c.set_named_curve(self.named_curve) - dh_params_c = ECDHParams(ec_params_c, point_format) - s.client_kx_params = dh_params_c - s.client_kx_params.other_pub = self.point + if not s.client_kx_ecdh_params: + s.client_kx_ecdh_params = curve def guess_payload_class(self, p): return Padding @@ -657,7 +589,7 @@ def _tls_server_ecdh_cls_guess(m): class ServerRSAParams(_GenericTLSSessionInheritance): """ Defined for RSA_EXPORT kx : it enables servers to share RSA keys shorter - than their principal {>512}-bit key, when is not allowed for kx. + than their principal {>512}-bit key, when it is not allowed for kx. This should not appear in standard RSA kx negotiation, as the key has already been advertised in the Certificate message. @@ -671,16 +603,24 @@ class ServerRSAParams(_GenericTLSSessionInheritance): length_from = lambda pkt: pkt.rsaexplen) ] def fill_missing(self): - k = self.tls_session.server_rsa_key + ext_k = rsa.generate_private_key(public_exponent=0x10001, + key_size=512, + backend=default_backend()) + pem_k = ext_k.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption()) + k = PrivKey(pem_k) + self.tls_session.server_tmp_rsa_key = k modlen = k.modulusLen / 8 - if self.rsamod is None: + if self.rsamod is "": self.rsamod = pkcs_i2osp(k.modulus, modlen) if self.rsamodlen is None: self.rsamodlen = len(self.rsamod) rsaexplen = math.ceil(math.log(k.pubExp)/math.log(2)/8.) - if self.rsaexp is None: + if self.rsaexp is "": self.rsaexp = pkcs_i2osp(k.pubExp, rsaexplen) if self.rsaexplen is None: self.rsaexplen = len(self.rsaexp) @@ -719,9 +659,6 @@ class ServerPSKParams(Packet): pass def post_dissection(self, pkt): - """ - XXX Do a check_params() once it has been implemented in crypto/ecdh.py. - """ pass def guess_payload_class(self, p): @@ -752,24 +689,21 @@ class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance): s = self.tls_session if self.dh_Yc == "": - dh_Yc = s.client_kx_params.gen_public_params() - dh_Yclen = len(dh_Yc) - self.dh_Yc = dh_Yc + params = s.client_kx_ffdh_params + s.client_kx_privkey = params.generate_private_key() + pubkey = s.client_kx_privkey.public_key() + y = pubkey.public_numbers().y + self.dh_Yc = pkcs_i2osp(y, pubkey.key_size/8) + # else, we assume that the user wrote the client_kx_privkey by himself if self.dh_Yclen is None: - self.dh_Yclen = dh_Yclen - # small hack so that ClientECDiffieHellmanPublic also works - l = struct.pack(self.fields_desc[0].fmt, self.dh_Yclen) - pkt = l + self.dh_Yc - - if s.server_kx_params: - s.server_kx_params.other_pub = pkcs_os2ip(self.dh_Yc) + self.dh_Yclen = len(self.dh_Yc) - if s.client_kx_params.priv is not None: - pms = s.client_kx_params.gen_secret(s.server_kx_params.pub) + if s.client_kx_privkey and s.server_kx_pubkey: + pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() - return pkt + pay + return pkcs_i2osp(self.dh_Yclen, 2) + self.dh_Yc + pay def post_dissection(self, m): """ @@ -779,37 +713,66 @@ class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance): """ s = self.tls_session - if s.client_kx_params: - s.client_kx_params.pub = pkcs_os2ip(self.dh_Yc) + if s.client_kx_ffdh_params: + y = pkcs_os2ip(self.dh_Yc) + param_numbers = s.client_kx_ffdh_params.parameter_numbers() + public_numbers = dh.DHPublicNumbers(y, param_numbers) + s.client_kx_pubkey = public_numbers.public_key(default_backend()) - if s.server_kx_params: - s.server_kx_params.other_pub = pkcs_os2ip(self.dh_Yc) - if s.server_kx_params.priv: - ZZ = s.server_kx_params.gen_secret(self.dh_Yc) - s.pre_master_secret = ZZ - s.compute_ms_and_derive_keys() + if s.server_kx_privkey and s.client_kx_pubkey: + ZZ = s.server_kx_privkey.exchange(s.client_kx_pubkey) + s.pre_master_secret = ZZ + s.compute_ms_and_derive_keys() def guess_payload_class(self, p): return Padding -class ClientECDiffieHellmanPublic(ClientDiffieHellmanPublic): +class ClientECDiffieHellmanPublic(_GenericTLSSessionInheritance): """ - We need an EC-dedicated subclass because the 'len' field is 1 byte longer. + Note that the 'len' field is 1 byte longer than with the previous class. """ name = "Client ECDH Public Value" - fields_desc = [ FieldLenField("dh_Yclen", None, length_of="dh_Yc", fmt="B"), - StrLenField("dh_Yc", "", - length_from=lambda pkt: pkt.dh_Yclen)] + fields_desc = [ FieldLenField("ecdh_Yclen", None, + length_of="ecdh_Yc", fmt="B"), + StrLenField("ecdh_Yc", "", + length_from=lambda pkt: pkt.ecdh_Yclen)] def post_build(self, pkt, pay): - if ecdsa_support: - return super(ClientECDiffieHellmanPublic, self).post_build(pkt, pay) - else: - return pkt + pay + s = self.tls_session + + if self.ecdh_Yc == "": + params = s.client_kx_ecdh_params + s.client_kx_privkey = ec.generate_private_key(params, + default_backend()) + pubkey = s.client_kx_privkey.public_key() + x = pubkey.public_numbers().x + y = pubkey.public_numbers().y + self.ecdh_Yc = ("\x04" + + pkcs_i2osp(x, params.key_size/8) + + pkcs_i2osp(y, params.key_size/8)) + # else, we assume that the user wrote the client_kx_privkey by himself + if self.ecdh_Yclen is None: + self.ecdh_Yclen = len(self.ecdh_Yc) + + if s.client_kx_privkey and s.server_kx_pubkey: + pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) + s.pre_master_secret = pms + s.compute_ms_and_derive_keys() + + return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay - @ecdsa_warning def post_dissection(self, m): - return super(ClientECDiffieHellmanPublic, self).post_dissection(m) + s = self.tls_session + + if s.client_kx_ecdh_params: + import_point = ec.EllipticCurvePublicNumbers.from_encoded_point + pub_num = import_point(s.client_kx_ecdh_params, self.ecdh_Yc) + s.client_kx_pubkey = pub_num.public_key(default_backend()) + + if s.server_kx_privkey and s.client_kx_pubkey: + ZZ = s.server_kx_privkey.exchange(ec.ECDH(), s.client_kx_pubkey) + s.pre_master_secret = ZZ + s.compute_ms_and_derive_keys() ### RSA Encryption (standard & export) @@ -835,7 +798,11 @@ class EncryptedPreMasterSecret(_GenericTLSSessionInheritance): warning(err) else: tbd = m[2:] - if s.server_rsa_key is not None: + if s.server_tmp_rsa_key is not None: + # priority is given to the tmp_key, if there is one + decrypted = s.server_tmp_rsa_key.decrypt(tbd) + pms = decrypted[-48:] + elif s.server_rsa_key is not None: decrypted = s.server_rsa_key.decrypt(tbd) pms = decrypted[-48:] else: @@ -850,7 +817,7 @@ class EncryptedPreMasterSecret(_GenericTLSSessionInheritance): def post_build(self, pkt, pay): """ - We encrypt premaster secret (the 48 bytes) with either the server + We encrypt the premaster secret (the 48 bytes) with either the server certificate or the temporary RSA key provided in a server key exchange message. After that step, we add the 2 bytes to provide the length, as described in implementation notes at the end of section 7.4.7.1. @@ -861,10 +828,10 @@ class EncryptedPreMasterSecret(_GenericTLSSessionInheritance): s.pre_master_secret = enc s.compute_ms_and_derive_keys() - if s.server_certs: - enc = s.server_certs[0].encrypt(pkt, "pkcs") - elif self.tls_session.server_tmp_rsa_key is not None: + if s.server_tmp_rsa_key is not None: enc = s.server_tmp_rsa_key.encrypt(pkt, "pkcs") + elif s.server_certs is not None and len(s.server_certs) > 0: + enc = s.server_certs[0].encrypt(pkt, "pkcs") else: warning("No material to encrypt Pre Master Secret") diff --git a/scapy/layers/tls/record.py b/scapy/layers/tls/record.py index 160d322b6167e725e9031b7f0a31a58a1e055106..cebe3c21d38d551bb22d23268cc385a098946a3e 100644 --- a/scapy/layers/tls/record.py +++ b/scapy/layers/tls/record.py @@ -19,8 +19,7 @@ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import TCP from scapy.layers.tls.session import _GenericTLSSessionInheritance -from scapy.layers.tls.handshake import (_tls_handshake_cls, _TLSHandshake, - TLSClientHello) +from scapy.layers.tls.handshake import _tls_handshake_cls, _TLSHandshake from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version, _TLSIVField, _TLSMACField, _TLSPadField, _TLSPadLenField, @@ -103,6 +102,8 @@ class _TLSMsgListField(PacketListField): remain, ret = s[:l], s[l:] if pkt.decipherable: + if remain == "": + return ret, [TLSApplicationData(data="")] while remain: raw_msg = remain p = self.m2i(pkt, remain) @@ -286,8 +287,15 @@ class TLS(_GenericTLSSessionInheritance): read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num) self.tls_session.rcs.seq_num += 1 alg = self.tls_session.rcs.hmac + + version = struct.unpack("!H", hdr[1:3])[0] try: - h = alg.digest(read_seq_num + hdr + msg) + if version > 0x300: + h = alg.digest(read_seq_num + hdr + msg) + elif version == 0x300: + h = alg.digest_sslv3(read_seq_num + hdr[0] + hdr[3:5] + msg) + else: + raise Exception("Unrecognized version.") except HMACError: h = mac return h == mac @@ -308,6 +316,9 @@ class TLS(_GenericTLSSessionInheritance): TLSPlaintext.fragment. Else, it should be the length of the _TLSEncryptedContent. """ + if len(s) < 5: + raise Exception("Invalid record: header is too short.") + msglen = struct.unpack('!H', s[3:5])[0] hdr, efrag, r = s[:5], s[5:5+msglen], s[msglen+5:] @@ -316,8 +327,7 @@ class TLS(_GenericTLSSessionInheritance): cipher_type = self.tls_session.rcs.cipher.type if cipher_type == 'block': - if len(s) >= 3: - version = struct.unpack("!H", s[1:3])[0] + version = struct.unpack("!H", s[1:3])[0] # Decrypt if version >= 0x0302: @@ -462,7 +472,14 @@ class TLS(_GenericTLSSessionInheritance): write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num) self.tls_session.wcs.seq_num += 1 alg = self.tls_session.wcs.hmac - h = alg.digest(write_seq_num + hdr + msg) + + version = struct.unpack("!H", hdr[1:3])[0] + if version > 0x300: + h = alg.digest(write_seq_num + hdr + msg) + elif version == 0x300: + h = alg.digest_sslv3(write_seq_num + hdr[0] + hdr[3:5] + msg) + else: + raise Exception("Unrecognized version.") return msg + h def _tls_pad(self, s): diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py index 7af0c305d7feb6ad61faf1b74f0fd4c9903a2e63..0c781552ccf1ef65e86f5465250331a4ca1c4019 100644 --- a/scapy/layers/tls/session.py +++ b/scapy/layers/tls/session.py @@ -16,18 +16,8 @@ from scapy.error import warning from scapy.packet import Packet from scapy.utils import repr_hex from scapy.layers.tls.crypto.compression import Comp_NULL -from scapy.layers.tls.crypto.ffdh import FFDHParams from scapy.layers.tls.crypto.prf import PRF -ecdsa_support = False -try: - from scapy.layers.tls.crypto.ecdh import ECDHParams - ecdsa_support = True -except ImportError: - import logging - log_loading = logging.getLogger("scapy.loading") - log_loading.info("Can't import python ecdsa lib. No default ECDH params.") - # Note the following import may happen inside connState.__init__() # in order to avoid to avoid cyclical dependancies. # from scapy.layers.tls.crypto.suites import TLS_NULL_WITH_NULL_NULL @@ -326,25 +316,24 @@ class tlsSession(object): ### Ephemeral key exchange parameters - # Default FFDH/ECDH parameters when acting as server. - # They are used for building Server(EC)DHParams, via .fill_missing(). - # Note that there are two types of curves, Fp and char2. - # We use the same defaults for explicit and named Fp curves, - # but for now there is no support for char2 curves. - self.default_ffdh_params = FFDHParams() - if ecdsa_support: - self.default_ecdh_params = [ECDHParams(), None] - else: - self.default_ecdh_params = [None, None] - - # Either an instance of FFDHParams or ECDHParams. - # Depending on which side of the connection we operate, - # one of these params will not hold 'priv' and 'secret' attributes. - # We did not use these intermediaries for RSAkx, as the 'priv' would - # equate the PrivKey, and the 'secret' the pre_master_secret. - # (It could have been useful for RSAkx export, though...) - self.server_kx_params = None - self.client_kx_params = None + ## XXX Explain why we need pubkey (which should be contained in privkey) + # also, params is used to hold params between the SKE and the CKE + self.server_kx_privkey = None + self.server_kx_pubkey = None + self.client_kx_privkey = None + self.client_kx_pubkey = None + + self.client_kx_ffdh_params = None + self.client_kx_ecdh_params = None + + ## Either an instance of FFDHParams or ECDHParams. + ## Depending on which side of the connection we operate, + ## one of these params will not hold 'priv' and 'secret' attributes. + ## We did not use these intermediaries for RSAkx, as the 'priv' would + ## equate the PrivKey, and the 'secret' the pre_master_secret. + ## (It could have been useful for RSAkx export, though...) + #self.server_kx_params = None + #self.client_kx_params = None ### Negotiated session parameters diff --git a/test/cert.uts b/test/cert.uts index 876286d26b414b9c59bb317fb0c2ac67a3b6e21e..69b981b5deff396dad3f7b69de8c1d31cb2db952 100644 --- a/test/cert.uts +++ b/test/cert.uts @@ -60,10 +60,10 @@ Jd5qtmDF2Zu+xrwrBRT0HBnPweDU+RsFxcyU/QxD9WYORzYarqxbcA== type(z) is PubKeyECDSA = PubKey class : checking curve -z.key.curve.name == "SECP256k1" +z.pubkey.curve.name == "secp256k1" = PubKey class : checking point value -z.key.pubkey.point.x() == 104748656174769496952370005421566518252704263000192720134585149244759951661467L +z.pubkey.public_numbers().x == 104748656174769496952370005421566518252704263000192720134585149244759951661467L ########### PrivKey class ############################################### @@ -124,11 +124,24 @@ weDU+RsFxcyU/QxD9WYORzYarqxbcA== type(y) is PrivKeyECDSA = PrivKey class : checking public attributes -assert(y.key.curve.name == "SECP256k1") -y.key.privkey.public_key.point.y() == 86290575637772818452062569410092503179882738810918951913926481113065456425840L +assert(y.key.curve.name == "secp256k1") +y.key.public_key().public_numbers().y == 86290575637772818452062569410092503179882738810918951913926481113065456425840L = PrivKey class : checking private attributes -y.key.privkey.secret_multiplier == 90719786431263082134670936670180839782031078050773732489701961692235185651857L +y.key.private_numbers().private_value == 90719786431263082134670936670180839782031078050773732489701961692235185651857L + + +########### PKCS crypto ############################################# +# these are legacy tests which should be removed eventually (see our pkcs1.py) + ++ PKCS legacy tests + += PKCS legacy : RSA signature & verification +m = "Testing our PKCS legacy methods" +s = x.sign_legacy(m, t="pkcs", h="tls") +assert(s == "Zp\xd7`\x80\x97\x18\xc5t\xf1\xc2ZZ\x1e'\xee\xf1\x8d\xc2\xfat#Z\xde\xa5gnZ\xb5\xad\xc0?\xe0\xb5\xef\xe4\xc3Z\xe1>\x04@'<\xda\x8dtd\xd1Y\xdfm\x93\x88Sg\x9d\xc42\x87\x01\xd1\xfd\x03$C\x07A\x89\x86\xf4\x87*\x96\xd7\xf0\x0c\x19\x12\xf5\xa5\xa6\x07\x8b\xe6\x04c\x1a8\x18B\xfb\\t\xdf\x9f:\xdc\xa8\x0eg],I\xdc\xcc\x99\xa8\x93rl\xb3\xad\x91Kd\x92H\xf4\xfc\xc2\xf9\xf0\xe0`7\xb0\xf7E\x97\\k\xec\xfb\xff\xcff\xb3\xbf\xf6\xdf(\xae]\x01\x85\x06\xdd\x91\x91(\xf1,\x15c\x8b\x0b,\x1f \xc2\x1aN/\xeeJ$Qz\x9a\xc0\x8c\x99\x80\xe2\xba\xfdpu\x15p\x18;3\xff\x90\xbb$\xf1-VT\xe5'F\xed*0\xadXd9T!\x8au]\xc1\xe8\xf0\xb3\x04\x0f6b\xf2\x7f\xa8\x06}\x97*\xec\x97X\x94g\xd1\xc3a\x11\x1a4\xd0\x01\xb3yp\xdc\xdf5{\xb7\x9d\x97y`\xa6\xde\x03\xe9\xa6\x14s\x0fa") +x_pub = PubKey((x.pubExp, x.modulus, x.modulusLen)) +x_pub.verify_legacy(m, s, t="pkcs", h="tls") ########### Cert class ############################################## @@ -223,8 +236,9 @@ JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv = Cert class : Checking ECDSA public key assert(type(y.pubKey) is PubKeyECDSA) -assert(y.pubKey.key.curve.name == 'SECP384r1') -y.pubKey.key.pubkey.point.x() == 3987178688175281746349180015490646948656137448666005327832107126183726641822596270780616285891030558662603987311874L +pubkey = y.pubKey.pubkey +assert(pubkey.curve.name == 'secp384r1') +pubkey.public_numbers().x == 3987178688175281746349180015490646948656137448666005327832107126183726641822596270780616285891030558662603987311874L = Cert class : Checking ECDSA signature y.signatureValue == '0d\x020%\xa4\x81E\x02k\x12KutO\xc8#\xe3p\xf2ur\xde|\x89\xf0\xcf\x91ra\x9e^\x10\x92YV\xb9\x83\xc7\x10\xe78\xe9X&6}\xd5\xe44\x869\x020|6S\xf00\xe5bc:\x99\xe2\xb6\xa3;\x9b4\xfa\x1e\xda\x10\x92q^\x91\x13\xa7\xdd\xa4n\x92\xcc2\xd6\xf5!f\xc7/\xea\x96cjeE\x92\x95\x01\xb4' diff --git a/test/tls.uts b/test/tls.uts index 82513014f31cc95b003fd3a96fa3a653da154fcd..32af086dee88685a520f3b42b2171041659175b3 100644 --- a/test/tls.uts +++ b/test/tls.uts @@ -3,6 +3,7 @@ # Try me with : # bash test/run_tests -t test/tls.uts -F +~ crypto ############################################################################### ################################### Crypto #################################### @@ -536,7 +537,7 @@ def _all_aes_gcm_tests(): ciphers = [] for t in [_aes128gcm_test_1, _aes128gcm_test_2]: c = Cipher_AES_128_GCM(key=t.k, salt=t.n[:4], - nonce_explicit_init=pkcs_os2ip(t.n[4:])) + nonce_explicit=pkcs_os2ip(t.n[4:])) ne = t.n[-c.nonce_explicit_len:] tup = ne, t.p, t.ct[-c.tag_len:] tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup @@ -544,7 +545,7 @@ def _all_aes_gcm_tests(): res = res and tmp1 and tmp2 for t in [_aes256gcm_test_1, _aes256gcm_test_2]: c = Cipher_AES_256_GCM(key=t.k, salt=t.n[:4], - nonce_explicit_init=pkcs_os2ip(t.n[4:])) + nonce_explicit=pkcs_os2ip(t.n[4:])) ne = t.n[-c.nonce_explicit_len:] tup = ne, t.p, t.ct[-c.tag_len:] tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup @@ -555,63 +556,64 @@ def _all_aes_gcm_tests(): _all_aes_gcm_tests() -+ Test AES-CCM -= Crypto - AES cipher in CCM mode, checks from IEEE P1619.1 -~ combined_modes - -class _aes256ccm_test_1: - k= "\0"*32 - n= "\0"*12 - p= "\0"*16 - a= "" - ct=("\xc1\x94\x40\x44\xc8\xe7\xaa\x95\xd2\xde\x95\x13\xc7\xf3\xdd\x8c" + - "\x4b\x0a\x3e\x5e\x51\xf1\x51\xeb\x0f\xfa\xe7\xc4\x3d\x01\x0f\xdb") - -class _aes256ccm_test_2: - k=("\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" + - "\xb2\xfb\x64\xce\x60\x97\x87\x8d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7") - n= "\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f" - p= "\xa9" - a= "\x36" - ct="\x9d\x32\x61\xb1\xcf\x93\x14\x31\xe9\x9a\x32\x80\x67\x38\xec\xbd\x2a" - -class _aes256ccm_test_3: - k=("\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" + - "\xb2\xfb\x64\xce\x60\x97\x8f\x4d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7") - n= "\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f" - p= "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e" - a= "" - ct=("\xcc\x88\x12\x61\xc6\xa7\xfa\x72\xb9\x6a\x17\x39\x17\x6b\x27\x7f" + - "\x34\x72\xe1\x14\x5f\x2c\x0c\xbe\x14\x63\x49\x06\x2c\xf0\xe4\x23") - -class _aes256ccm_test_4: - k=("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + - "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f") - n= "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b" - p=("\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + - "\x30\x31\x32\x33\x34\x35\x36\x37") - a=("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + - "\x10\x11\x12\x13") - ct=("\x04\xf8\x83\xae\xb3\xbd\x07\x30\xea\xf5\x0b\xb6\xde\x4f\xa2\x21" + - "\x20\x34\xe4\xe4\x1b\x0e\x75\xe5\x9b\xba\x3f\x3a\x10\x7f\x32\x39" + - "\xbd\x63\x90\x29\x23\xf8\x03\x71") - -def _all_aes_ccm_tests(): - from scapy.layers.tls.crypto.cipher_aead import Cipher_AES_256_CCM - res = True - ciphers = [] - for t in [_aes256ccm_test_1, _aes256ccm_test_2, - _aes256ccm_test_3, _aes256ccm_test_4]: - c = Cipher_AES_256_CCM(key=t.k, salt=t.n[:4], - nonce_explicit_init=pkcs_os2ip(t.n[4:])) - ne = t.n[-c.nonce_explicit_len:] - tup = ne, t.p, t.ct[-c.tag_len:] - tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup - tmp2 = c.auth_encrypt(t.p, t.a) == (ne + t.ct) - res = res and tmp1 and tmp2 - return res - -_all_aes_ccm_tests() +#XXX CCM remains to be added to the cryptography library +#+ Test AES-CCM +#= Crypto - AES cipher in CCM mode, checks from IEEE P1619.1 +#~ combined_modes +# +#class _aes256ccm_test_1: +# k= "\0"*32 +# n= "\0"*12 +# p= "\0"*16 +# a= "" +# ct=("\xc1\x94\x40\x44\xc8\xe7\xaa\x95\xd2\xde\x95\x13\xc7\xf3\xdd\x8c" + +# "\x4b\x0a\x3e\x5e\x51\xf1\x51\xeb\x0f\xfa\xe7\xc4\x3d\x01\x0f\xdb") +# +#class _aes256ccm_test_2: +# k=("\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" + +# "\xb2\xfb\x64\xce\x60\x97\x87\x8d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7") +# n= "\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f" +# p= "\xa9" +# a= "\x36" +# ct="\x9d\x32\x61\xb1\xcf\x93\x14\x31\xe9\x9a\x32\x80\x67\x38\xec\xbd\x2a" +# +#class _aes256ccm_test_3: +# k=("\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" + +# "\xb2\xfb\x64\xce\x60\x97\x8f\x4d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7") +# n= "\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f" +# p= "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e" +# a= "" +# ct=("\xcc\x88\x12\x61\xc6\xa7\xfa\x72\xb9\x6a\x17\x39\x17\x6b\x27\x7f" + +# "\x34\x72\xe1\x14\x5f\x2c\x0c\xbe\x14\x63\x49\x06\x2c\xf0\xe4\x23") +# +#class _aes256ccm_test_4: +# k=("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" + +# "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f") +# n= "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b" +# p=("\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" + +# "\x30\x31\x32\x33\x34\x35\x36\x37") +# a=("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" + +# "\x10\x11\x12\x13") +# ct=("\x04\xf8\x83\xae\xb3\xbd\x07\x30\xea\xf5\x0b\xb6\xde\x4f\xa2\x21" + +# "\x20\x34\xe4\xe4\x1b\x0e\x75\xe5\x9b\xba\x3f\x3a\x10\x7f\x32\x39" + +# "\xbd\x63\x90\x29\x23\xf8\x03\x71") +# +#def _all_aes_ccm_tests(): +# from scapy.layers.tls.crypto.cipher_aead import Cipher_AES_256_CCM +# res = True +# ciphers = [] +# for t in [_aes256ccm_test_1, _aes256ccm_test_2, +# _aes256ccm_test_3, _aes256ccm_test_4]: +# c = Cipher_AES_256_CCM(key=t.k, salt=t.n[:4], +# nonce_explicit_init=pkcs_os2ip(t.n[4:])) +# ne = t.n[-c.nonce_explicit_len:] +# tup = ne, t.p, t.ct[-c.tag_len:] +# tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup +# tmp2 = c.auth_encrypt(t.p, t.a) == (ne + t.ct) +# res = res and tmp1 and tmp2 +# return res +# +#_all_aes_ccm_tests() + Test camellia @@ -621,27 +623,27 @@ class _Camellia128_test: k= "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" p= "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" c= "\x67\x67\x31\x38\x54\x96\x69\x73\x08\x57\x06\x56\x48\xea\xbe\x43" - -class _Camellia192_test: - k=("\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" + - "\x00\x11\x22\x33\x44\x55\x66\x77") - p= "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" - c= "\xb4\x99\x34\x01\xb3\xe9\x96\xf8\x4e\xe5\xce\xe7\xd7\x9b\x09\xb9" + iv="\0"*16 class _Camellia256_test: k=("\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff") p= "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" c= "\x9a\xcc\x23\x7d\xff\x16\xd7\x6c\x20\xef\x7c\x91\x9e\x3a\x75\x09" + iv="\0"*16 def _all_camellia_tests(): - from scapy.layers.tls.crypto.camellia import Camellia - cam = Camellia() + from scapy.layers.tls.crypto.cipher_block import (Cipher_CAMELLIA_128_CBC, + Cipher_CAMELLIA_256_CBC) res = True - for t in [_Camellia128_test, _Camellia192_test, _Camellia256_test]: - tmp = ((cam.encrypt(t.p, t.k) == t.c) and - (cam.decrypt(t.c, t.k) == t.p)) - res = res and tmp + t = _Camellia128_test + tmp = (Cipher_CAMELLIA_128_CBC(t.k, t.iv).encrypt(t.p) == t.c and + Cipher_CAMELLIA_128_CBC(t.k, t.iv).decrypt(t.c) == t.p) + res = res and tmp + t = _Camellia256_test + tmp = (Cipher_CAMELLIA_256_CBC(t.k, t.iv).encrypt(t.p) == t.c and + Cipher_CAMELLIA_256_CBC(t.k, t.iv).decrypt(t.c) == t.p) + res = res and tmp return res _all_camellia_tests() @@ -785,7 +787,7 @@ assert(isinstance(ske, TLSServerKeyExchange)) p = ske.params assert(isinstance(p, ServerECDHNamedCurveParams)) assert(p.named_curve == 0x0017) -assert(p.point[:4] == '\x04\xc3\x9d\x1c' and p.point[-4:] == 'X\x19\x03u') +assert(p.point[0] == '\x04' and p.point[1:5] == '\xc3\x9d\x1cD' and p.point[-4:] == 'X\x19\x03u') assert(ske.sig.sig_alg == 0x0601) ske.sig.sig_val[:4] == 'y\x8aQ\x11' and ske.sig.sig_val[-4:] == '`15\xef' @@ -801,12 +803,12 @@ t = shd.tls_session assert(len(t.handshake_messages) == 6) assert(t.handshake_messages_parsed[-1] is shd) assert(t.tls_version == 0x0303) -assert(isinstance(t.server_kx_params, ECDHParams)) -p = t.server_kx_params.pub -assert(p[:4] == '\x04\xc3\x9d\x1c' and p[-4:] == 'X\x19\x03u') -assert(isinstance(t.client_kx_params, ECDHParams)) -p = t.client_kx_params.other_pub -assert(p[:4] == '\x04\xc3\x9d\x1c' and p[-4:] == 'X\x19\x03u') +assert(t.client_kx_ffdh_params is None) +assert(t.client_kx_ecdh_params is not None) +pn = t.server_kx_pubkey.public_numbers() +x = pkcs_i2osp(pn.x, pn.curve.key_size/8) +y = pkcs_i2osp(pn.y, pn.curve.key_size/8) +assert(x[:4] == '\xc3\x9d\x1cD' and y[-4:] == 'X\x19\x03u') assert(t.rcs.row == "read") assert(t.wcs.row == "write") t.rcs.ciphersuite.val == 0 @@ -829,9 +831,8 @@ isinstance(t5.payload.payload.payload, NoPayload) assert(isinstance(cke, TLSClientKeyExchange)) k = cke.exchkeys assert(isinstance(k, ClientECDiffieHellmanPublic)) -assert(k.dh_Yclen == 65) -assert(k.dh_Yc[:4] == '\x04\xd2\x07\xce' and k.dh_Yc[-4:] == '\xdc\x86[\xe7') -t.server_kx_params.other_pub == pkcs_os2ip(k.dh_Yc) +assert(k.ecdh_Yclen == 65) +assert(k.ecdh_Yc[:4] == '\x04\xd2\x07\xce' and k.ecdh_Yc[-4:] == '\xdc\x86[\xe7') + Test TLS ChangeCipherSpec diff --git a/scapy/layers/tls/examples/__init__.py b/test/tls/__init__.py similarity index 100% rename from scapy/layers/tls/examples/__init__.py rename to test/tls/__init__.py diff --git a/scapy/layers/tls/examples/client_simple.py b/test/tls/example_client.py similarity index 83% rename from scapy/layers/tls/examples/client_simple.py rename to test/tls/example_client.py index c1f4f9d00ac715ecfac9cd9f56aac8f545ebc734..3ed4256203a246ef25c6001efca50859ad329f56 100755 --- a/scapy/layers/tls/examples/client_simple.py +++ b/test/tls/example_client.py @@ -10,10 +10,16 @@ For instance, "sudo ./client_simple.py c014" will try to connect to any TLS server at 127.0.0.1:4433, with suite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA. """ +import os import sys + from scapy.layers.tls.automaton import TLSClientAutomaton from scapy.layers.tls.handshake import TLSClientHello +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../")) +sys.path=[basedir]+sys.path + + if len(sys.argv) == 2: ch = TLSClientHello(ciphers=int(sys.argv[1], 16)) else: diff --git a/scapy/layers/tls/examples/server_simple.py b/test/tls/example_server.py similarity index 70% rename from scapy/layers/tls/examples/server_simple.py rename to test/tls/example_server.py index f89aa4f99edb77e260f7598282c0d791ca909cad..30ffd2748bfe3451a8dbd199d978f5c3395e3512 100755 --- a/scapy/layers/tls/examples/server_simple.py +++ b/test/tls/example_server.py @@ -11,17 +11,22 @@ any TLS client connection. If provided, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA will be preferred to any other suite the client might propose. """ +import os import sys from scapy.layers.tls.automaton import TLSServerAutomaton +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../")) +sys.path=[basedir]+sys.path + + if len(sys.argv) == 2: pcs = int(sys.argv[1], 16) else: pcs = None -t = TLSServerAutomaton(mycert='pki_test/srv_cert.pem', - mykey='pki_test/srv_key.pem', +t = TLSServerAutomaton(mycert=basedir+'/test/tls/pki/srv_cert.pem', + mykey=basedir+'/test/tls/pki/srv_key.pem', preferred_ciphersuite=pcs) t.run() diff --git a/scapy/layers/tls/examples/pki_test/ca_cert.pem b/test/tls/pki/ca_cert.pem similarity index 100% rename from scapy/layers/tls/examples/pki_test/ca_cert.pem rename to test/tls/pki/ca_cert.pem diff --git a/scapy/layers/tls/examples/pki_test/ca_key.pem b/test/tls/pki/ca_key.pem similarity index 100% rename from scapy/layers/tls/examples/pki_test/ca_key.pem rename to test/tls/pki/ca_key.pem diff --git a/scapy/layers/tls/examples/pki_test/cli_cert.pem b/test/tls/pki/cli_cert.pem similarity index 100% rename from scapy/layers/tls/examples/pki_test/cli_cert.pem rename to test/tls/pki/cli_cert.pem diff --git a/scapy/layers/tls/examples/pki_test/cli_key.pem b/test/tls/pki/cli_key.pem similarity index 100% rename from scapy/layers/tls/examples/pki_test/cli_key.pem rename to test/tls/pki/cli_key.pem diff --git a/scapy/layers/tls/examples/pki_test/srv_cert.pem b/test/tls/pki/srv_cert.pem similarity index 100% rename from scapy/layers/tls/examples/pki_test/srv_cert.pem rename to test/tls/pki/srv_cert.pem diff --git a/scapy/layers/tls/examples/pki_test/srv_key.pem b/test/tls/pki/srv_key.pem similarity index 100% rename from scapy/layers/tls/examples/pki_test/srv_key.pem rename to test/tls/pki/srv_key.pem diff --git a/test/run_tests_tls_netaccess b/test/tls/run_tests_tls_netaccess similarity index 69% rename from test/run_tests_tls_netaccess rename to test/tls/run_tests_tls_netaccess index 7c11798b3ec050e3256ef8ea51f1bc3503f5bf0e..0d6a87d24b7c744c578ea901cf0d4901f8f3702a 100755 --- a/test/run_tests_tls_netaccess +++ b/test/tls/run_tests_tls_netaccess @@ -4,32 +4,32 @@ function test_tls_server { msg=$1 suite=$2 - exclude_versions=$3 + version=$3 EXIT_CODE=test_tls_server_tmp_code OSSL_STDO=test_tls_server_tmp_openssl_stdout OSSL_STDE=test_tls_server_tmp_openssl_stderr SCAPY_STDE=test_tls_server_tmp_scapy_stderr echo "1" > $EXIT_CODE - ($TRAVIS_SUDO ./tls_test_server.py "$msg" 2>$SCAPY_STDE; echo $? > $EXIT_CODE) & - sleep 1 # wait for the server to start + ($TRAVIS_SUDO python travis_test_server.py "$msg" 2>$SCAPY_STDE; echo $? > $EXIT_CODE) & + sleep 2 # wait for the server to start - echo -n "$msg" | openssl s_client -cipher $suite $exclude_versions 1>$OSSL_STDO 2>$OSSL_STDE + echo -n "$msg" | sudo openssl s_client -cipher $suite $version 1>$OSSL_STDO 2>$OSSL_STDE sleep 0.5 # wait for the connection to be established, then torn down - if [[ -z $exclude_versions ]]; + if [[ -z $version ]]; then - versions="" + version="" else - versions="and option $exclude_versions" + version="and option $version" fi if [ $(<$EXIT_CODE) == "0" ]; then - echo "PASSED - TLS server test with cipher suite $suite $versions" + echo "PASSED - TLS server test with cipher suite $suite $version" rm -f $EXIT_CODE $OSSL_STDO $OSSL_STDE $SCAPY_STDE else - echo "FAILED - TLS server test with cipher suite $suite $versions" + echo "FAILED - TLS server test with cipher suite $suite $version" echo -e "\n###\nHere are scapy writings to stderr:\n" cat $SCAPY_STDE echo -e "\nHere are openssl writings to stdout & stderr:\n" @@ -55,10 +55,10 @@ function test_tls_client { CLI_STDO=test_tls_client_tmp_client_stdout CLI_STDE=test_tls_client_tmp_client_stderr - ($TRAVIS_SUDO ./tls_test_server.py "$msg" 1>$SRV_STDO 2>$SRV_STDE; echo $? > $EXIT_CODE) & + ($TRAVIS_SUDO python travis_test_server.py "$msg" 1>$SRV_STDO 2>$SRV_STDE; echo $? > $EXIT_CODE) & sleep 1 # wait for the server to start - $TRAVIS_SUDO ./tls_test_client.py "$msg" $suite $version 1>$CLI_STDO 2>$CLI_STDE + $TRAVIS_SUDO python travis_test_client.py "$msg" $suite $version 1>$CLI_STDO 2>$CLI_STDE sleep 0.5 if [[ -z $version ]]; @@ -88,19 +88,16 @@ function test_tls_client { echo "TLS server automaton tests" -test_tls_server "Testing TLS server with TLS 1.0 and TLS_RSA_WITH_RC4_128_SHA" "RC4-SHA" "-no_tls1_2 -no_tls1_1" -test_tls_server "Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_CBC_SHA256" "DHE-RSA-AES128-SHA256" -if [ "$TEST_COMBINED_MODES" == "yes" ]; then - test_tls_server "Testing TLS server with TLS 1.2 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" "ECDHE-RSA-AES256-GCM-SHA384" -fi +test_tls_server "Testing TLS server with TLS 1.0 and TLS_RSA_WITH_RC4_128_SHA" "RC4-SHA" "-tls1" +test_tls_server "Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_CBC_SHA256" "DHE-RSA-AES128-SHA256" "-tls1_2" +test_tls_server "Testing TLS server with TLS 1.2 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" "ECDHE-RSA-AES256-GCM-SHA384" "-tls1_2" echo sleep 1 echo "TLS client automaton tests" -test_tls_client "Testing TLS client with TLS 1.0 and TLS_RSA_WITH_RC4_128_SHA" "0005" "0301" +test_tls_client "Testing TLS client with TLS 1.0 and TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA" "0088" "0301" test_tls_client "Testing TLS client with TLS 1.1 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" "c013" "0302" -if [ "$TEST_COMBINED_MODES" == "yes" ]; then - test_tls_client "Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_256_CCM" "c09f" "0303" -fi +test_tls_client "Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_GCM_SHA256" "009e" "0303" +test_tls_client "Testing TLS server with SSLv3 and TLS_RSA_EXPORT_WITH_RC4_40_MD5" "0003" "0300" diff --git a/test/tls_test_client.py b/test/tls/travis_test_client.py similarity index 98% rename from test/tls_test_client.py rename to test/tls/travis_test_client.py index f4a16c37320c5963152c7bce65d25eae411d8d42..edfa5a5bf962c2eadfba09c2d670ab70f35e16a6 100755 --- a/test/tls_test_client.py +++ b/test/tls/travis_test_client.py @@ -16,12 +16,13 @@ Reception of the exact send_data on the server is to be checked externally. import os import sys -basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) -sys.path=[basedir]+sys.path - from scapy.layers.tls.automaton import TLSClientAutomaton from scapy.layers.tls.handshake import TLSClientHello +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../")) +sys.path=[basedir]+sys.path + + send_data = cipher_suite_code = version = None if len(sys.argv) >= 2: diff --git a/test/tls_test_server.py b/test/tls/travis_test_server.py similarity index 89% rename from test/tls_test_server.py rename to test/tls/travis_test_server.py index bb9f4e669007d6f31ea14048a020c6dba58fd89e..4db52f20df8300de51d1c100355ae49d87199fe9 100755 --- a/test/tls_test_server.py +++ b/test/tls/travis_test_server.py @@ -18,11 +18,11 @@ import sys from contextlib import contextmanager from StringIO import StringIO -basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../")) -sys.path=[basedir]+sys.path - from scapy.layers.tls.automaton import TLSServerAutomaton +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../")) +sys.path=[basedir]+sys.path + @contextmanager def captured_output(): @@ -56,10 +56,9 @@ else: expected_data = None with captured_output() as (out, err): - t = TLSServerAutomaton(mycert=basedir+'/scapy/layers/tls/examples/pki_test/srv_cert.pem', - mykey=basedir+'/scapy/layers/tls/examples/pki_test/srv_key.pem') + t = TLSServerAutomaton(mycert=basedir+'/test/tls/pki/srv_cert.pem', + mykey=basedir+'/test/tls/pki/srv_key.pem') t.run() check_output_for_data(out, err, expected_data) -