Ask for python-cryptography v1.7+ in warnings and documentation

This commit is contained in:
mtu 2017-01-26 16:26:59 +01:00
parent e8b5a18944
commit e758a218ea
9 changed files with 161 additions and 120 deletions

View File

@ -29,7 +29,7 @@ test_script:
# Secondary unit tests
- 'del test\bpf.uts' # Don't bother with BPF regression tests
- "for %%t in (test\\*.uts) do (%PYTHON%\\python -m coverage run -a bin\\UTscapy -f text -t %%t -F -K combined_modes || exit /b 42)"
- "for %%t in (test\\*.uts) do (%PYTHON%\\python -m coverage run -a bin\\UTscapy -f text -t %%t -F -K combined_modes_ccm || exit /b 42)"
# Contrib unit tests
- "for %%t in (scapy\\contrib\\*.uts) do (%PYTHON%\\python -m coverage run -a bin\\UTscapy -f text -t %%t -F -P \"load_contrib(\'%%~nt\')\" || exit /b 42)"

View File

@ -166,11 +166,13 @@ Here are the topics involved and some examples that you can use to try if your i
>>> enc=rdpcap("weplab-64bit-AA-managed.pcap")
>>> enc.show()
>>> enc[0]
>>> conf.wepkey="AA\x00\x00\x00"
>>> dec=Dot11PacketList(enc).toEthernet()
>>> dec.show()
>>> dec[0]
>>> conf.wepkey="AA\x00\x00\x00"
>>> dec=Dot11PacketList(enc).toEthernet()
>>> dec.show()
>>> dec[0]
* PKI operations and TLS decryption. `cryptography <https://cryptography.io>` is also needed.
* Fingerprinting. ``nmap_fp()`` needs `Nmap <http://nmap.org>`_. You need an `old version <http://nmap.org/dist-old/>`_ (before v4.23) that still supports first generation fingerprinting.
.. code-block:: python
@ -187,8 +189,6 @@ Here are the topics involved and some examples that you can use to try if your i
* VOIP. ``voip_play()`` needs `SoX <http://sox.sourceforge.net/>`_.
* IPsec Crypto Support. ``SecurityAssociation()`` needs `Pycrypto 2.7a1 <https://github.com/dlitz/pycrypto>`_. Combined AEAD modes such as GCM and CCM are not available yet because of cryptography restrictions.
Platform-specific instructions
==============================
@ -207,7 +207,13 @@ Debian/Ubuntu
Just use the standard packages::
$ sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-crypto python-pyx
$ sudo apt-get install tcpdump graphviz imagemagick python-gnuplot python-cryptography python-pyx
Scapy optionally uses python-cryptography v1.7 or later. It has not been packaged for ``apt`` in less recent OS versions (e.g. Debian Jessie). If you need the cryptography-related methods, you may install the library with:
.. code-block:: text
# pip install cryptography
Fedora
------
@ -226,7 +232,7 @@ Some optional packages:
.. code-block:: text
# yum install graphviz python-crypto sox PyX gnuplot numpy
# yum install graphviz python-cryptography sox PyX gnuplot numpy
# cd /tmp
# wget http://heanet.dl.sourceforge.net/sourceforge/gnuplot-py/gnuplot-py-1.8.tar.gz
# tar xvfz gnuplot-py-1.8.tar.gz
@ -288,11 +294,11 @@ Here's how to install Scapy on OpenBSD 5.9+
Optional packages (OpenBSD only)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
py-crypto
py-cryptography
.. code-block:: text
# pkg_add py-crypto
# pkg_add py-cryptography
gnuplot and its Python binding:

View File

@ -4,7 +4,7 @@
## This program is published under a GPLv2 license
"""
Implementation for of the configuration object.
Implementation of the configuration object.
"""
import os,time,socket,sys
@ -279,6 +279,14 @@ class LogLevel(object):
obj._logLevel = val
def isCryptographyValid():
try:
import cryptography
except ImportError:
return False
from distutils.version import LooseVersion
return LooseVersion(cryptography.__version__) >= LooseVersion("1.7")
def _prompt_changer(attr,val):
prompt = conf.prompt
@ -396,6 +404,7 @@ contribs: a dict which can be used by contrib layers to store local configuratio
"tftp", "x509", "bluetooth", "dhcp6", "llmnr",
"sctp", "vrrp", "ipsec", "lltd", "vxlan"]
contribs = dict()
crypto_valid = isCryptographyValid()
if not Conf.ipv6_enabled:
@ -404,7 +413,23 @@ if not Conf.ipv6_enabled:
if m in Conf.load_layers:
Conf.load_layers.remove(m)
if not Conf.crypto_valid:
log_scapy.warning("Crypto-related methods disabled for IPsec, Dot11 "
"and TLS layers (needs python-cryptography v1.7+).")
conf=Conf()
conf.logLevel=30 # 30=Warning
def crypto_validator(func):
"""
This a decorator to be used for any method relying on the cryptography library.
Its behaviour depends on the 'crypto_valid' attribute of the global 'conf'.
"""
def func_in(*args, **kwargs):
if not conf.crypto_valid:
raise ImportError("Cannot execute crypto-related method! "
"Please install python-cryptography v1.7 or later.")
return func(*args, **kwargs)
return func_in

View File

@ -10,7 +10,7 @@ Wireless LAN according to IEEE 802.11.
import re,struct
from zlib import crc32
from scapy.config import conf
from scapy.config import conf, crypto_validator
from scapy.data import *
from scapy.packet import *
from scapy.fields import *
@ -20,14 +20,12 @@ from scapy.layers.l2 import *
from scapy.layers.inet import IP, TCP
try:
if conf.crypto_valid:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import (
Cipher,
algorithms,
)
except ImportError:
log_loading.info("Can't import python cryptography lib. Won't be able to decrypt WEP.")
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
else:
default_backend = Ciphers = algorithms = None
log_loading.info("Can't import python-cryptography v1.7+. Disabled WEP decryption/encryption.")
### Fields
@ -324,6 +322,18 @@ class Dot11WEP(Packet):
StrField("wepdata",None,remain=4),
IntField("icv",None) ]
@crypto_validator
def decrypt(self, key=None):
if key is None:
key = conf.wepkey
if key:
d = Cipher(
algorithms.ARC4(self.iv + key),
None,
default_backend(),
).decryptor()
self.add_payload(LLC(d.update(self.wepdata) + d.finalize()))
def post_dissect(self, s):
self.decrypt()
@ -332,36 +342,30 @@ class Dot11WEP(Packet):
return Packet.build_payload(self)
return ""
def post_build(self, p, pay):
if self.wepdata is None:
key = conf.wepkey
if key:
if self.icv is None:
pay += struct.pack("<I",crc32(pay))
icv = ""
else:
icv = p[4:8]
e = Cipher(
algorithms.ARC4(self.iv+key),
None,
default_backend(),
).encryptor()
p = p[:4]+e.update(pay)+e.finalize()+icv
else:
warning("No WEP key set (conf.wepkey).. strange results expected..")
return p
def decrypt(self,key=None):
@crypto_validator
def encrypt(self, p, pay, key=None):
if key is None:
key = conf.wepkey
if key:
d = Cipher(
algorithms.ARC4(self.iv+key),
if self.icv is None:
pay += struct.pack("<I", crc32(pay))
icv = ""
else:
icv = p[4:8]
e = Cipher(
algorithms.ARC4(self.iv + key),
None,
default_backend(),
).decryptor()
self.add_payload(LLC(d.update(self.wepdata)+d.finalize()))
).encryptor()
return p[:4] + e.update(pay) + e.finalize() + icv
else:
warning("No WEP key set (conf.wepkey).. strange results expected..")
return None
def post_build(self, p, pay):
if self.wepdata is None:
p = self.encrypt(p, pay)
return p
bind_layers( PrismHeader, Dot11, )

View File

@ -44,19 +44,15 @@ import os
import socket
import struct
from scapy.error import warning
from scapy.config import conf, crypto_validator
from scapy.data import IP_PROTOS
from scapy.error import log_loading
from scapy.fields import ByteEnumField, ByteField, StrField, XIntField, IntField, \
ShortField, PacketField
from scapy.fields import (ByteEnumField, ByteField, StrField, XIntField,
IntField, ShortField, PacketField)
from scapy.packet import Packet, bind_layers, Raw
from scapy.layers.inet import IP, UDP
from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \
IPv6ExtHdrRouting
from scapy.layers.inet6 import (IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt,
IPv6ExtHdrRouting)
#------------------------------------------------------------------------------
@ -142,26 +138,20 @@ class _ESPPlain(Packet):
return str(self.data) + self.padding + chr(self.padlen) + chr(self.nh)
#------------------------------------------------------------------------------
try:
if conf.crypto_valid:
from cryptography.exceptions import InvalidTag
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import interfaces
from cryptography.hazmat.primitives.ciphers import (
Cipher,
algorithms,
modes,
)
except ImportError:
log_loading.info("Can't import python cryptography lib. "
else:
log_loading.info("Can't import python-cryptography v1.7+. "
"Disabled IPsec encryption/authentication.")
algorithms = None
Cipher = None
modes = None
try:
from Crypto.Cipher.AES import MODE_GCM
from Crypto.Cipher.AES import MODE_CCM
except ImportError:
warning("Combined crypto modes not available for IPsec (pycrypto 2.7a1 required).")
InvalidTag = default_backend = interfaces = None
Cipher = algorithms = modes = None
#------------------------------------------------------------------------------
def _lcm(a, b):
@ -201,8 +191,9 @@ class CryptAlgo(object):
self.mode = mode
self.icv_size = icv_size
if self.mode is not None:
self.is_aead = issubclass(self.mode, modes.ModeWithAuthenticationTag)
if modes and self.mode is not None:
self.is_aead = issubclass(self.mode,
modes.ModeWithAuthenticationTag)
else:
self.is_aead = False
@ -248,6 +239,7 @@ class CryptAlgo(object):
# XXX: random bytes for counters, so it is not wrong to do it that way
return os.urandom(self.iv_size - self.salt_size)
@crypto_validator
def new_cipher(self, key, iv, digest=None):
"""
@param key: the secret key, a byte string
@ -430,15 +422,13 @@ if algorithms:
mode=modes.CBC)
#------------------------------------------------------------------------------
try:
if conf.crypto_valid:
from cryptography.hazmat.primitives.hmac import HMAC
from cryptography.hazmat.primitives.cmac import CMAC
from cryptography.hazmat.primitives import hashes
except ImportError:
else:
# no error if cryptography is not available but authentication won't be supported
HMAC = None
CMAC = None
hashes = None
HMAC = CMAC = hashes = None
#------------------------------------------------------------------------------
class IPSecIntegrityError(Exception):
@ -478,6 +468,7 @@ class AuthAlgo(object):
raise TypeError('invalid key size %s, must be one of %s' %
(len(key), self.key_size))
@crypto_validator
def new_mac(self, key):
"""
@param key: a byte string

View File

@ -7,12 +7,12 @@
Tools for handling TLS sessions and digital certificates.
"""
try:
import cryptography
except ImportError:
from scapy.config import conf
if not conf.crypto_valid:
import logging
log_loading = logging.getLogger("scapy.loading")
log_loading.info("Can't import python cryptography lib. Disabled certificate manipulation tools")
log_loading.info("Can't import python-cryptography v1.7+. Disabled PKCS #1 signing/verifying.")
try:
import ecdsa

View File

@ -29,9 +29,13 @@ Supports both RSA and ECDSA objects.
import base64, os, time
import ecdsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from scapy.config import conf, crypto_validator
if conf.crypto_valid:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
else:
default_backend = rsa = None
from scapy.layers.tls.crypto.curves import import_curve
from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp, mapHashFunc
@ -199,7 +203,7 @@ class _PubKeyFactory(_PKIObjMaker):
obj.__class__ = PubKeyECDSA
obj.updateWith(spki)
else:
raise Exception("Unsupported publicKey type")
raise
marker = "PUBLIC KEY"
except:
try:
@ -241,6 +245,7 @@ class PubKeyRSA(_PKIObj, PubKey, _EncryptAndVerifyRSA):
Wrapper for RSA keys based on _EncryptAndVerifyRSA from crypto/pkcs1.py
Use the 'key' attribute to access original object.
"""
@crypto_validator
def updateWith(self, pubkey):
self.modulus = pubkey.modulus.val
self.modulusLen = len(binrepr(pubkey.modulus.val))
@ -391,6 +396,7 @@ class PrivKeyRSA(_PKIObj, PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA):
Wrapper for RSA keys based on _DecryptAndSignRSA from crypto/pkcs1.py
Use the 'key' attribute to access original object.
"""
@crypto_validator
def updateWith(self, privkey):
self.modulus = privkey.modulus.val
self.modulusLen = len(binrepr(privkey.modulus.val))

View File

@ -10,10 +10,14 @@ PKCS #1 methods as defined in RFC 3447.
import os, popen2, tempfile
import math, random, struct
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from scapy.config import conf, crypto_validator
if conf.crypto_valid:
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
else:
InvalidSignature = dafault_backend = hashes = padding = None
#####################################################################
@ -108,40 +112,44 @@ def pkcs_ilen(n):
# 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
def _hashWrapper(hash_algo, message, backend=default_backend()):
digest = hashes.Hash(hash_algo, backend).update(message)
return digest.finalize()
_hashFuncParams = {
"md5" : (16,
hashes.MD5,
lambda x: _hashWrapper(hashes.MD5, x),
'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
"sha1" : (20,
hashes.SHA1,
lambda x: _hashWrapper(hashes.SHA1, x),
'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
"sha224" : (28,
hashes.SHA224,
lambda x: _hashWrapper(hashes.SHA224, x),
'\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'),
"sha256" : (32,
hashes.SHA256,
lambda x: _hashWrapper(hashes.SHA256, x),
'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
"sha384" : (48,
hashes.SHA384,
lambda x: _hashWrapper(hashes.SHA384, x),
'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
"sha512" : (64,
hashes.SHA512,
lambda x: _hashWrapper(hashes.SHA512, x),
'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
"tls" : (36,
None,
lambda x: _hashWrapper(hashes.MD5, x) + _hashWrapper(hashes.SHA1, x),
'')
}
_hashFuncParams = {}
if conf.crypto_valid:
def _hashWrapper(hash_algo, message, backend=default_backend()):
digest = hashes.Hash(hash_algo, backend).update(message)
return digest.finalize()
_hashFuncParams = {
"md5" : (16,
hashes.MD5,
lambda x: _hashWrapper(hashes.MD5, x),
'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
"sha1" : (20,
hashes.SHA1,
lambda x: _hashWrapper(hashes.SHA1, x),
'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
"sha224" : (28,
hashes.SHA224,
lambda x: _hashWrapper(hashes.SHA224, x),
'\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'),
"sha256" : (32,
hashes.SHA256,
lambda x: _hashWrapper(hashes.SHA256, x),
'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
"sha384" : (48,
hashes.SHA384,
lambda x: _hashWrapper(hashes.SHA384, x),
'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
"sha512" : (64,
hashes.SHA512,
lambda x: _hashWrapper(hashes.SHA512, x),
'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
"tls" : (36,
None,
lambda x: _hashWrapper(hashes.MD5, x) + _hashWrapper(hashes.SHA1, x),
'')
}
def mapHashFunc(hashStr):
try:
@ -424,6 +432,7 @@ def create_temporary_ca_path(anchor_list, folder):
#####################################################################
class _EncryptAndVerifyRSA(object):
@crypto_validator
def encrypt(self, m, t=None, h=None, mgf=None, L=None):
"""
Encrypt message 'm' using 't' encryption scheme where 't' can be:
@ -484,6 +493,7 @@ class _EncryptAndVerifyRSA(object):
_warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
return None
@crypto_validator
def verify(self, M, S, t=None, h=None, mgf=None, sLen=None):
"""
Verify alleged signature 'S' is indeed the signature of message 'M'

View File

@ -2491,7 +2491,6 @@ assert(d[TCP] == p[TCP])
#######################################
= IPv4 / ESP - Tunnel - AES-GCM - NULL
~ combined_modes
p = IP(src='1.1.1.1', dst='2.2.2.2')
p /= TCP(sport=45012, dport=80)
@ -2526,7 +2525,7 @@ assert(d == p)
#######################################
= IPv4 / ESP - Tunnel - AES-CCM - NULL
~ combined_modes combined_modes_ccm
~ combined_modes_ccm
p = IP(src='1.1.1.1', dst='2.2.2.2')
p /= TCP(sport=45012, dport=80)