Skip to content

Commit 358f546

Browse files
bgannegpotter2
andauthored
Add RFC-4543 AES-NULL-GMAC support to IPSec layer (#3935)
* Add RFC-4543 AES-NULL-GMAC support to IPSec layer * Minor cleanups --------- Co-authored-by: gpotter2 <[email protected]>
1 parent c456201 commit 358f546

File tree

2 files changed

+198
-2
lines changed

2 files changed

+198
-2
lines changed

scapy/layers/ipsec.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,11 @@ def encrypt(self, sa, esp, key, icv_size=None, esn_en=False, esn=0):
413413
cipher = self.cipher(key, tag_length=icv_size)
414414
else:
415415
cipher = self.cipher(key)
416-
data = cipher.encrypt(mode_iv, data, aad)
416+
if self.name == 'AES-NULL-GMAC':
417+
# Special case for GMAC (rfc 4543 sect 3)
418+
data = data + cipher.encrypt(mode_iv, b"", aad + esp.iv + data)
419+
else:
420+
data = cipher.encrypt(mode_iv, data, aad)
417421
else:
418422
cipher = self.new_cipher(key, mode_iv)
419423
encryptor = cipher.encryptor()
@@ -465,7 +469,11 @@ def decrypt(self, sa, esp, key, icv_size=None, esn_en=False, esn=0):
465469
else:
466470
cipher = self.cipher(key)
467471
try:
468-
data = cipher.decrypt(mode_iv, data + icv, aad)
472+
if self.name == 'AES-NULL-GMAC':
473+
# Special case for GMAC (rfc 4543 sect 3)
474+
data = data + cipher.decrypt(mode_iv, icv, aad + iv + data)
475+
else:
476+
data = cipher.decrypt(mode_iv, data + icv, aad)
469477
except InvalidTag as err:
470478
raise IPSecIntegrityError(err)
471479
else:
@@ -528,6 +536,17 @@ def decrypt(self, sa, esp, key, icv_size=None, esn_en=False, esn=0):
528536
iv_size=8,
529537
icv_size=16,
530538
format_mode_iv=_salt_format_mode_iv)
539+
# GMAC: rfc 4543, "companion to the AES Galois/Counter Mode ESP"
540+
# This is defined as a crypt_algo by rfc, but has the role of an auth_algo
541+
CRYPT_ALGOS['AES-NULL-GMAC'] = CryptAlgo('AES-NULL-GMAC',
542+
cipher=aead.AESGCM,
543+
key_size=(16, 24, 32),
544+
mode=None,
545+
salt_size=4,
546+
block_size=1,
547+
iv_size=8,
548+
icv_size=16,
549+
format_mode_iv=_salt_format_mode_iv)
531550
CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
532551
cipher=aead.AESCCM,
533552
mode=None,

test/scapy/layers/ipsec.uts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,183 @@ try:
16831683
except IPSecIntegrityError as err:
16841684
err
16851685

1686+
#######################################
1687+
= IPv4 / ESP - Transport - AES-NULL-GMAC - NULL
1688+
1689+
p = IP(src='1.1.1.1', dst='2.2.2.2')
1690+
p /= TCP(sport=45012, dport=80)
1691+
p /= Raw('testdata')
1692+
p = IP(raw(p))
1693+
p
1694+
1695+
sa = SecurityAssociation(ESP, spi=0x222,
1696+
crypt_algo='AES-NULL-GMAC', crypt_key=b'16bytekey+4bytenonce',
1697+
auth_algo='NULL', auth_key=None)
1698+
1699+
e = sa.encrypt(p)
1700+
e
1701+
1702+
assert isinstance(e, IP)
1703+
assert e.src == '1.1.1.1' and e.dst == '2.2.2.2'
1704+
assert e.chksum != p.chksum
1705+
assert e.proto == socket.IPPROTO_ESP
1706+
assert e.haslayer(ESP)
1707+
assert not e.haslayer(TCP)
1708+
assert e[ESP].spi == sa.spi
1709+
* AES-NULL-GMAC is integrity only, the original packet payload should be readable
1710+
assert b'testdata' in e[ESP].data
1711+
1712+
d = sa.decrypt(e)
1713+
d
1714+
1715+
* after decryption original packet should be preserved
1716+
assert d[TCP] == p[TCP]
1717+
1718+
# Generated with Linux 5.15.0-1034-azure #41-Ubuntu
1719+
# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 0x222 reqid 1 \
1720+
# mode tunnel aead 'rfc4543(gcm(aes))' '0x3136627974656b65792b34627974656e6f6e6365' 128 flag align4
1721+
ref = IP() \
1722+
/ ESP(spi=0x222,
1723+
data=b'\x54\x70\x6c\x6a\x9f\xba\xa6\x18\x45\x00\x00\x54\xbc\x53\x00\x00'
1724+
b'\x40\x01\xa9\x59\x0a\x7d\x00\x01\x0a\x7d\x00\x02\x00\x00\xad\x53'
1725+
b'\xa8\x83\x00\x01\x02\xe6\x09\x64\x00\x00\x00\x00\xd9\x0a\x06\x00'
1726+
b'\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b'
1727+
b'\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b'
1728+
b'\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x01\x02\x02\x04'
1729+
b'\x9b\x76\x32\x30\xf6\x49\x92\xa8\x8f\x6a\x20\x87\x2c\x74\x0c\x18',
1730+
seq=22)
1731+
1732+
d_ref = sa.decrypt(ref)
1733+
d_ref
1734+
1735+
* Check for ICMP layer in decrypted reference
1736+
assert d_ref.haslayer(ICMP)
1737+
1738+
#######################################
1739+
= IPv4 / ESP - Transport - AES-NULL-GMAC - NULL -- ESN
1740+
1741+
p = IP(src='1.1.1.1', dst='2.2.2.2')
1742+
p /= TCP(sport=45012, dport=80)
1743+
p /= Raw('testdata')
1744+
p = IP(raw(p))
1745+
p
1746+
1747+
sa = SecurityAssociation(ESP, spi=0x222,
1748+
crypt_algo='AES-NULL-GMAC', crypt_key=b'16bytekey+4bytenonce',
1749+
auth_algo='NULL', auth_key=None, esn_en = True, esn = 0x1)
1750+
1751+
e = sa.encrypt(p)
1752+
e
1753+
1754+
assert isinstance(e, IP)
1755+
assert e.src == '1.1.1.1' and e.dst == '2.2.2.2'
1756+
assert e.chksum != p.chksum
1757+
assert e.proto == socket.IPPROTO_ESP
1758+
assert e.haslayer(ESP)
1759+
assert not e.haslayer(TCP)
1760+
assert e[ESP].spi == sa.spi
1761+
* AES-NULL-GMAC is integrity only, the original packet payload should be readable
1762+
assert b'testdata' in e[ESP].data
1763+
1764+
d = sa.decrypt(e)
1765+
d
1766+
1767+
* after decryption original packet should be preserved
1768+
assert d[TCP] == p[TCP]
1769+
1770+
# Generated with Linux 5.15.0-1034-azure #41-Ubuntu
1771+
# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 0x222 reqid 1 replay-oseq-hi 0x1 \
1772+
# mode tunnel aead 'rfc4543(gcm(aes))' '0x3136627974656b65792b34627974656e6f6e6365' 128 flag align4 esn
1773+
ref = IP() \
1774+
/ ESP(spi=0x222,
1775+
data=b'\x43\xe6\xa1\xce\x70\x9d\x67\xf4\x45\x00\x00\x54\x2e\x4a\x40\x00'
1776+
b'\x40\x01\xf7\x62\x0a\x7d\x00\x02\x0a\x7d\x00\x01\x08\x00\xd3\x32'
1777+
b'\x8f\x4c\x00\x02\x8d\xec\x09\x64\x00\x00\x00\x00\x3c\x5b\x03\x00'
1778+
b'\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b'
1779+
b'\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b'
1780+
b'\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x01\x02\x02\x04'
1781+
b'\x76\xd4\x93\x90\x75\xee\x3f\xa3\xf3\xcf\xcc\x27\xf5\x5b\x12\xb6',
1782+
seq=5)
1783+
1784+
d_ref = sa.decrypt(ref)
1785+
d_ref
1786+
1787+
* Check for ICMP layer in decrypted reference
1788+
assert d_ref.haslayer(ICMP)
1789+
1790+
1791+
#######################################
1792+
1793+
= IPv4 / ESP - Transport - AES-NULL-GMAC - NULL - altered packet
1794+
1795+
p = IP(src='1.1.1.1', dst='2.2.2.2')
1796+
p /= TCP(sport=45012, dport=80)
1797+
p /= Raw('testdata')
1798+
p = IP(raw(p))
1799+
p
1800+
1801+
sa = SecurityAssociation(ESP, spi=0x222,
1802+
crypt_algo='AES-NULL-GMAC', crypt_key=b'16bytekey+4bytenonce',
1803+
auth_algo='NULL', auth_key=None)
1804+
1805+
e = sa.encrypt(p)
1806+
e
1807+
1808+
assert isinstance(e, IP)
1809+
assert e.src == '1.1.1.1' and e.dst == '2.2.2.2'
1810+
assert e.chksum != p.chksum
1811+
assert e.proto == socket.IPPROTO_ESP
1812+
assert e.haslayer(ESP)
1813+
assert not e.haslayer(TCP)
1814+
assert e[ESP].spi == sa.spi
1815+
* AES-NULL-GMAC is integrity only, the original packet payload should be readable
1816+
assert b'testdata' in e[ESP].data
1817+
1818+
* simulate the alteration of the packet before decryption
1819+
e[ESP].seq += 1
1820+
1821+
* integrity verification should fail
1822+
try:
1823+
d = sa.decrypt(e)
1824+
assert False
1825+
except IPSecIntegrityError as err:
1826+
err
1827+
1828+
#######################################
1829+
1830+
= IPv4 / ESP - Transport - AES-NULL-GMAC - NULL - altered packet -- ESN
1831+
1832+
p = IP(src='1.1.1.1', dst='2.2.2.2')
1833+
p /= TCP(sport=45012, dport=80)
1834+
p /= Raw('testdata')
1835+
p = IP(raw(p))
1836+
p
1837+
1838+
sa = SecurityAssociation(ESP, spi=0x222,
1839+
crypt_algo='AES-NULL-GMAC', crypt_key=b'16bytekey+4bytenonce',
1840+
auth_algo='NULL', auth_key=None, esn_en = True, esn = 0x200)
1841+
1842+
e = sa.encrypt(p)
1843+
e
1844+
1845+
assert isinstance(e, IP)
1846+
assert e.src == '1.1.1.1' and e.dst == '2.2.2.2'
1847+
assert e.chksum != p.chksum
1848+
assert e.proto == socket.IPPROTO_ESP
1849+
assert e.haslayer(ESP)
1850+
assert not e.haslayer(TCP)
1851+
assert e[ESP].spi == sa.spi
1852+
* AES-NULL-GMAC is integrity only, the original packet payload should be readable
1853+
assert b'testdata' in e[ESP].data
1854+
1855+
* simulate the alteration of the packet before decryption
1856+
* integrity verification should fail
1857+
try:
1858+
d = sa.decrypt(e, esn = 0x201)
1859+
assert False
1860+
except IPSecIntegrityError as err:
1861+
err
1862+
16861863
#######################################
16871864
= IPv4 / ESP - Transport - AES-CCM - NULL
16881865
~ crypto_advanced

0 commit comments

Comments
 (0)