Skip to content

Commit

Permalink
Corrected ICV length for AES-GCM and AES-CCM in IPSec layer. Added un… (
Browse files Browse the repository at this point in the history
#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.
  • Loading branch information
kinap authored and guedou committed Oct 26, 2016
1 parent 3390149 commit 620f195
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 5 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
13 changes: 12 additions & 1 deletion .travis/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
Expand Down
8 changes: 7 additions & 1 deletion .travis/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions doc/scapy/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
==============================
Expand Down
23 changes: 20 additions & 3 deletions scapy/layers/ipsec.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@

import socket
import struct
from scapy.error import warning

try:
from Crypto.Util.number import GCD as gcd
except ImportError:
Expand Down Expand Up @@ -152,13 +154,20 @@ def data_for_encryption(self):
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
CAST = None
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):
"""
Expand Down Expand Up @@ -186,16 +195,20 @@ def __init__(self, name, cipher, mode, block_size=None, iv_size=None, key_size=N
@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:
Expand Down Expand Up @@ -389,19 +402,23 @@ def decrypt(self, esp, key, icv_size=0):
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',
Expand Down
70 changes: 70 additions & 0 deletions test/ipsec.uts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 620f195

Please sign in to comment.