mirror of https://github.com/secdev/scapy.git
Corrected ICV length for AES-GCM and AES-CCM in IPSec layer. Added un… (#269)
* Corrected ICV length for AES-GCM and AES-CCM in IPSec layer. Added unit tests to the ipsec campaign. * Added travis support for pycrypto 2.7a1 combined modes. * Updated documentation for pycrypto installation.
This commit is contained in:
parent
3390149230
commit
620f195c65
|
@ -28,6 +28,13 @@ matrix:
|
|||
- TRAVIS_SUDO=sudo
|
||||
- SCAPY_USE_PCAPDNET=yes
|
||||
|
||||
- os: linux
|
||||
sudo: required
|
||||
python: 2.7
|
||||
env:
|
||||
- TRAVIS_SUDO=sudo
|
||||
- TEST_COMBINED_MODES=yes
|
||||
|
||||
- os: osx
|
||||
language: generic
|
||||
env:
|
||||
|
|
|
@ -3,7 +3,18 @@ if [ -z $TRAVIS_SUDO ] && [ "$TRAVIS_OS_NAME" = "osx" ]
|
|||
then
|
||||
PIP_INSTALL_FLAGS="--user"
|
||||
fi
|
||||
$TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS pycrypto ecdsa mock
|
||||
$TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS ecdsa mock
|
||||
|
||||
# Pycrypto 2.7a1 isn't available on PyPi
|
||||
if [ "$TEST_COMBINED_MODES" = "yes" ]
|
||||
then
|
||||
curl -sL https://github.com/dlitz/pycrypto/archive/v2.7a1.tar.gz | tar xz
|
||||
cd pycrypto-2.7a1
|
||||
python setup.py build
|
||||
$TRAVIS_SUDO python setup.py install
|
||||
else
|
||||
$TRAVIS_SUDO pip install $PIP_INSTALL_FLAGS pycrypto
|
||||
fi
|
||||
|
||||
# Install pcap & dnet
|
||||
if [ ! -z $SCAPY_USE_PCAPDNET ]
|
||||
|
|
|
@ -4,7 +4,13 @@ python -c "from scapy.all import *; print conf"
|
|||
# Don't run tests that requires root privileges
|
||||
if [ -z $TRAVIS_SUDO ]
|
||||
then
|
||||
UT_FLAGS="-K netaccess"
|
||||
UT_FLAGS="-K netaccess "
|
||||
fi
|
||||
|
||||
# Test AEAD modes in IPSec if available
|
||||
if [ "$TEST_COMBINED_MODES" != "yes" ]
|
||||
then
|
||||
UT_FLAGS+="-K combined_modes "
|
||||
fi
|
||||
|
||||
# Run unit tests
|
||||
|
|
|
@ -190,6 +190,18 @@ 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 <https://github.com/dlitz/pycrypto>`_. Combined AEAD modes such as GCM and CCM require pycrypto2.7a1, which is only available from source (no pip or package).
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# pycrypto 2.6 install
|
||||
sudo pip install pycrypto
|
||||
|
||||
# pycrypto 2.7a1 install
|
||||
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
|
||||
|
||||
Platform-specific instructions
|
||||
==============================
|
||||
|
|
|
@ -41,6 +41,8 @@ True
|
|||
|
||||
import socket
|
||||
import struct
|
||||
from scapy.error import warning
|
||||
|
||||
try:
|
||||
from Crypto.Util.number import GCD as gcd
|
||||
except ImportError:
|
||||
|
@ -152,6 +154,7 @@ try:
|
|||
from Crypto import Random
|
||||
except ImportError:
|
||||
# no error if pycrypto is not available but encryption won't be supported
|
||||
warning("IPSec encryption not supported (pycrypto required).")
|
||||
AES = None
|
||||
DES = None
|
||||
DES3 = None
|
||||
|
@ -159,6 +162,12 @@ except ImportError:
|
|||
Blowfish = None
|
||||
Random = 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).")
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
def _lcm(a, b):
|
||||
"""
|
||||
|
@ -186,16 +195,20 @@ class CryptAlgo(object):
|
|||
@param key_size: an integer or list/tuple of integers. If specified,
|
||||
force the secret keys length to one of the values.
|
||||
Defaults to the `key_size` of the cipher.
|
||||
@param icv_size: the length of the integrity check value of this algo.
|
||||
Only used in this class for AEAD algorithms.
|
||||
"""
|
||||
self.name = name
|
||||
self.cipher = cipher
|
||||
self.mode = mode
|
||||
self.icv_size = icv_size
|
||||
self.is_aead = (hasattr(self.cipher, 'MODE_GCM') and
|
||||
self.mode == self.cipher.MODE_GCM) or \
|
||||
(hasattr(self.cipher, 'MODE_CCM') and
|
||||
self.mode == self.cipher.MODE_CCM)
|
||||
|
||||
if icv_size is not None:
|
||||
self.icv_size = icv_size
|
||||
|
||||
if block_size is not None:
|
||||
self.block_size = block_size
|
||||
elif cipher is not None:
|
||||
|
@ -389,19 +402,23 @@ if AES:
|
|||
block_size=1,
|
||||
iv_size=8,
|
||||
key_size=(16 + 4, 24 + 4, 32 + 4))
|
||||
|
||||
# AEAD algorithms are only supported in pycrypto 2.7a1+
|
||||
# they also have an additional field icv_size, which is usually
|
||||
# populated by an auth algo when signing and verifying signatures.
|
||||
if hasattr(AES, "MODE_GCM"):
|
||||
CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
|
||||
cipher=AES,
|
||||
mode=AES.MODE_GCM,
|
||||
iv_size=8,
|
||||
icv_size=8,
|
||||
icv_size=16,
|
||||
key_size=(16 + 4, 24 + 4, 32 + 4))
|
||||
if hasattr(AES, "MODE_CCM"):
|
||||
CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
|
||||
cipher=AES,
|
||||
mode=AES.MODE_CCM,
|
||||
iv_size=8,
|
||||
icv_size=8,
|
||||
icv_size=16,
|
||||
key_size=(16 + 4, 24 + 4, 32 + 4))
|
||||
if DES:
|
||||
CRYPT_ALGOS['DES'] = CryptAlgo('DES',
|
||||
|
|
|
@ -139,6 +139,76 @@ d
|
|||
* after decryption original packet should be preserved
|
||||
assert(d == p)
|
||||
|
||||
#######################################
|
||||
= 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)
|
||||
p /= Raw('testdata')
|
||||
p = IP(str(p))
|
||||
p
|
||||
|
||||
sa = SecurityAssociation(ESP, spi=0x222,
|
||||
crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce',
|
||||
auth_algo='NULL', auth_key=None,
|
||||
tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22'))
|
||||
|
||||
e = sa.encrypt(p)
|
||||
e
|
||||
|
||||
assert(isinstance(e, IP))
|
||||
* after encryption packet should be encapsulated with the given ip tunnel header
|
||||
assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22')
|
||||
assert(e.chksum != p.chksum)
|
||||
assert(e.proto == socket.IPPROTO_ESP)
|
||||
assert(e.haslayer(ESP))
|
||||
assert(not e.haslayer(TCP))
|
||||
assert(e[ESP].spi == sa.spi)
|
||||
* after encryption the original packet payload should NOT be readable
|
||||
assert('testdata' not in e[ESP].data)
|
||||
|
||||
d = sa.decrypt(e)
|
||||
d
|
||||
|
||||
* after decryption original packet should be preserved
|
||||
assert(d == p)
|
||||
|
||||
#######################################
|
||||
= IPv4 / ESP - Tunnel - AES-CCM - NULL
|
||||
~ combined_modes
|
||||
|
||||
p = IP(src='1.1.1.1', dst='2.2.2.2')
|
||||
p /= TCP(sport=45012, dport=80)
|
||||
p /= Raw('testdata')
|
||||
p = IP(str(p))
|
||||
p
|
||||
|
||||
sa = SecurityAssociation(ESP, spi=0x222,
|
||||
crypt_algo='AES-CCM', crypt_key='16bytekey+4bytenonce',
|
||||
auth_algo='NULL', auth_key=None,
|
||||
tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22'))
|
||||
|
||||
e = sa.encrypt(p)
|
||||
e
|
||||
|
||||
assert(isinstance(e, IP))
|
||||
* after encryption packet should be encapsulated with the given ip tunnel header
|
||||
assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22')
|
||||
assert(e.chksum != p.chksum)
|
||||
assert(e.proto == socket.IPPROTO_ESP)
|
||||
assert(e.haslayer(ESP))
|
||||
assert(not e.haslayer(TCP))
|
||||
assert(e[ESP].spi == sa.spi)
|
||||
* after encryption the original packet payload should NOT be readable
|
||||
assert('testdata' not in e[ESP].data)
|
||||
|
||||
d = sa.decrypt(e)
|
||||
d
|
||||
|
||||
* after decryption original packet should be preserved
|
||||
assert(d == p)
|
||||
|
||||
#######################################
|
||||
= IPv4 / ESP - Tunnel - NULL - SHA2-256-128
|
||||
|
||||
|
|
Loading…
Reference in New Issue