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:
Daniel Collins 2016-10-26 01:03:37 -07:00 committed by Guillaume Valadon
parent 3390149230
commit 620f195c65
6 changed files with 128 additions and 5 deletions

View File

@ -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:

View File

@ -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 ]

View File

@ -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

View File

@ -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
==============================

View File

@ -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',

View File

@ -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