ipsec: fix padding/alignment for native IPsec encryption
Not all ESP crypto algorithms require padding/alignment to be the same as AES block/IV size. CCM, CTR and GCM all have no padding/alignment requirements, and the RFCs indicate that no padding (beyond ESPs 4 octet alignment requirement) should be used unless TFC (traffic flow confidentiality) has been requested. CTR: https://tools.ietf.org/html/rfc3686#section-3.2 GCM: https://tools.ietf.org/html/rfc4106#section-3.2 CCM: https://tools.ietf.org/html/rfc4309#section-3.2 - VPP is incorrectly using the IV/AES block size to pad CTR and GCM. These modes do not require padding (beyond ESPs 4 octet requirement), as a result packets will have unnecessary padding, which will waste bandwidth at least and possibly fail certain network configurations that have finely tuned MTU configurations at worst. Fix this as well as changing the field names from ".*block_size" to ".*block_align" to better represent their actual (and only) use. Rename "block_sz" in esp_encrypt to "esp_align" and set it correctly as well. test: ipsec: Add unit-test to test for RFC correct padding/alignment test: patch scapy to not incorrectly pad ccm, ctr, gcm modes as well - Scapy is also incorrectly using the AES block size of 16 to pad CCM, CTR, and GCM cipher modes. A bug report has been opened with the and acknowledged with the upstream scapy project as well: https://github.com/secdev/scapy/issues/2322 Ticket: VPP-1928 Type: fix Signed-off-by: Christian Hopps <chopps@labn.net> Change-Id: Iaa4d6a325a2e99fdcb2c375a3395bcfe7947770e
This commit is contained in:

committed by
Damjan Marion

parent
dce44e4e23
commit
fb7e7ed2cd
@ -1,5 +1,5 @@
|
||||
diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
|
||||
index f8c601fa..f566d288 100644
|
||||
index ae057ee1..24a1f6ea 100644
|
||||
--- a/scapy/layers/ipsec.py
|
||||
+++ b/scapy/layers/ipsec.py
|
||||
@@ -138,6 +138,7 @@ bind_layers(IP, ESP, proto=socket.IPPROTO_ESP)
|
||||
@ -9,8 +9,8 @@ index f8c601fa..f566d288 100644
|
||||
+bind_layers(UDP, ESP, dport=4545) # NAT-Traversal encapsulation - random port
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -359,11 +359,8 @@ class CryptAlgo(object):
|
||||
|
||||
@@ -359,11 +360,8 @@ class CryptAlgo(object):
|
||||
encryptor = cipher.encryptor()
|
||||
|
||||
if self.is_aead:
|
||||
@ -24,7 +24,7 @@ index f8c601fa..f566d288 100644
|
||||
data = encryptor.update(data) + encryptor.finalize()
|
||||
data += encryptor.tag[:self.icv_size]
|
||||
else:
|
||||
@@ -401,12 +398,7 @@ class CryptAlgo(object):
|
||||
@@ -400,12 +398,7 @@ class CryptAlgo(object):
|
||||
|
||||
if self.is_aead:
|
||||
# Tag value check is done during the finalize method
|
||||
@ -38,7 +38,31 @@ index f8c601fa..f566d288 100644
|
||||
try:
|
||||
data = decryptor.update(data) + decryptor.finalize()
|
||||
except InvalidTag as err:
|
||||
@@ -545,7 +537,7 @@ class AuthAlgo(object):
|
||||
@@ -445,6 +438,7 @@ if algorithms:
|
||||
CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR',
|
||||
cipher=algorithms.AES,
|
||||
mode=modes.CTR,
|
||||
+ block_size=1,
|
||||
iv_size=8,
|
||||
salt_size=4,
|
||||
format_mode_iv=_aes_ctr_format_mode_iv)
|
||||
@@ -452,6 +446,7 @@ if algorithms:
|
||||
CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
|
||||
cipher=algorithms.AES,
|
||||
mode=modes.GCM,
|
||||
+ block_size=1,
|
||||
salt_size=4,
|
||||
iv_size=8,
|
||||
icv_size=16,
|
||||
@@ -460,6 +455,7 @@ if algorithms:
|
||||
CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
|
||||
cipher=algorithms.AES,
|
||||
mode=modes.CCM,
|
||||
+ block_size=1,
|
||||
iv_size=8,
|
||||
salt_size=3,
|
||||
icv_size=16,
|
||||
@@ -544,7 +540,7 @@ class AuthAlgo(object):
|
||||
else:
|
||||
return self.mac(key, self.digestmod(), default_backend())
|
||||
|
||||
@ -47,7 +71,7 @@ index f8c601fa..f566d288 100644
|
||||
"""
|
||||
Sign an IPsec (ESP or AH) packet with this algo.
|
||||
|
||||
@@ -561,16 +553,20 @@ class AuthAlgo(object):
|
||||
@@ -560,16 +556,20 @@ class AuthAlgo(object):
|
||||
|
||||
if pkt.haslayer(ESP):
|
||||
mac.update(raw(pkt[ESP]))
|
||||
@ -69,7 +93,7 @@ index f8c601fa..f566d288 100644
|
||||
"""
|
||||
Check that the integrity check value (icv) of a packet is valid.
|
||||
|
||||
@@ -602,6 +598,8 @@ class AuthAlgo(object):
|
||||
@@ -600,6 +600,8 @@ class AuthAlgo(object):
|
||||
clone = zero_mutable_fields(pkt.copy(), sending=False)
|
||||
|
||||
mac.update(raw(clone))
|
||||
@ -78,7 +102,7 @@ index f8c601fa..f566d288 100644
|
||||
computed_icv = mac.finalize()[:self.icv_size]
|
||||
|
||||
# XXX: Cannot use mac.verify because the ICV can be truncated
|
||||
@@ -864,6 +862,23 @@ class SecurityAssociation(object):
|
||||
@@ -862,6 +864,23 @@ class SecurityAssociation(object):
|
||||
raise TypeError('nat_t_header must be %s' % UDP.name)
|
||||
self.nat_t_header = nat_t_header
|
||||
|
||||
@ -102,7 +126,7 @@ index f8c601fa..f566d288 100644
|
||||
def check_spi(self, pkt):
|
||||
if pkt.spi != self.spi:
|
||||
raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' %
|
||||
@@ -877,7 +892,8 @@ class SecurityAssociation(object):
|
||||
@@ -875,7 +894,8 @@ class SecurityAssociation(object):
|
||||
if len(iv) != self.crypt_algo.iv_size:
|
||||
raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) # noqa: E501
|
||||
|
||||
@ -112,7 +136,7 @@ index f8c601fa..f566d288 100644
|
||||
|
||||
if self.tunnel_header:
|
||||
tunnel = self.tunnel_header.copy()
|
||||
@@ -901,7 +917,7 @@ class SecurityAssociation(object):
|
||||
@@ -899,7 +919,7 @@ class SecurityAssociation(object):
|
||||
esn_en=esn_en or self.esn_en,
|
||||
esn=esn or self.esn)
|
||||
|
||||
@ -121,7 +145,7 @@ index f8c601fa..f566d288 100644
|
||||
|
||||
if self.nat_t_header:
|
||||
nat_t_header = self.nat_t_header.copy()
|
||||
@@ -928,7 +944,8 @@ class SecurityAssociation(object):
|
||||
@@ -926,7 +946,8 @@ class SecurityAssociation(object):
|
||||
|
||||
def _encrypt_ah(self, pkt, seq_num=None):
|
||||
|
||||
@ -131,7 +155,7 @@ index f8c601fa..f566d288 100644
|
||||
icv=b"\x00" * self.auth_algo.icv_size)
|
||||
|
||||
if self.tunnel_header:
|
||||
@@ -968,7 +985,8 @@ class SecurityAssociation(object):
|
||||
@@ -966,7 +987,8 @@ class SecurityAssociation(object):
|
||||
else:
|
||||
ip_header.plen = len(ip_header.payload) + len(ah) + len(payload)
|
||||
|
||||
@ -141,7 +165,7 @@ index f8c601fa..f566d288 100644
|
||||
|
||||
# sequence number must always change, unless specified by the user
|
||||
if seq_num is None:
|
||||
@@ -1005,11 +1023,12 @@ class SecurityAssociation(object):
|
||||
@@ -1003,11 +1025,12 @@ class SecurityAssociation(object):
|
||||
|
||||
def _decrypt_esp(self, pkt, verify=True, esn_en=None, esn=None):
|
||||
|
||||
@ -155,7 +179,7 @@ index f8c601fa..f566d288 100644
|
||||
|
||||
esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key,
|
||||
self.crypt_algo.icv_size or
|
||||
@@ -1050,11 +1069,12 @@ class SecurityAssociation(object):
|
||||
@@ -1048,9 +1071,10 @@ class SecurityAssociation(object):
|
||||
|
||||
def _decrypt_ah(self, pkt, verify=True):
|
||||
|
||||
@ -167,5 +191,3 @@ index f8c601fa..f566d288 100644
|
||||
|
||||
ah = pkt[AH]
|
||||
payload = ah.payload
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
|
||||
index 69e7ae3b..3a1724b2 100644
|
||||
index 69e7ae3b..d9c1705b 100644
|
||||
--- a/scapy/layers/ipsec.py
|
||||
+++ b/scapy/layers/ipsec.py
|
||||
@@ -344,8 +344,7 @@ class CryptAlgo(object):
|
||||
@ -24,7 +24,31 @@ index 69e7ae3b..3a1724b2 100644
|
||||
try:
|
||||
data = decryptor.update(data) + decryptor.finalize()
|
||||
except InvalidTag as err:
|
||||
@@ -518,12 +514,16 @@ class AuthAlgo(object):
|
||||
@@ -422,6 +418,7 @@ if algorithms:
|
||||
CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR',
|
||||
cipher=algorithms.AES,
|
||||
mode=modes.CTR,
|
||||
+ block_size=1,
|
||||
iv_size=8,
|
||||
salt_size=4,
|
||||
format_mode_iv=_aes_ctr_format_mode_iv)
|
||||
@@ -429,6 +426,7 @@ if algorithms:
|
||||
CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
|
||||
cipher=algorithms.AES,
|
||||
mode=modes.GCM,
|
||||
+ block_size=1,
|
||||
salt_size=4,
|
||||
iv_size=8,
|
||||
icv_size=16,
|
||||
@@ -437,6 +435,7 @@ if algorithms:
|
||||
CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
|
||||
cipher=algorithms.AES,
|
||||
mode=modes.CCM,
|
||||
+ block_size=1,
|
||||
iv_size=8,
|
||||
salt_size=3,
|
||||
icv_size=16,
|
||||
@@ -518,12 +517,16 @@ class AuthAlgo(object):
|
||||
else:
|
||||
return self.mac(key, self.digestmod(), default_backend())
|
||||
|
||||
@ -42,7 +66,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
|
||||
@return: the signed packet
|
||||
"""
|
||||
@@ -534,16 +534,20 @@ class AuthAlgo(object):
|
||||
@@ -534,16 +537,20 @@ class AuthAlgo(object):
|
||||
|
||||
if pkt.haslayer(ESP):
|
||||
mac.update(raw(pkt[ESP]))
|
||||
@ -64,7 +88,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
"""
|
||||
Check that the integrity check value (icv) of a packet is valid.
|
||||
|
||||
@@ -574,6 +578,8 @@ class AuthAlgo(object):
|
||||
@@ -574,6 +581,8 @@ class AuthAlgo(object):
|
||||
clone = zero_mutable_fields(pkt.copy(), sending=False)
|
||||
|
||||
mac.update(raw(clone))
|
||||
@ -73,7 +97,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
computed_icv = mac.finalize()[:self.icv_size]
|
||||
|
||||
# XXX: Cannot use mac.verify because the ICV can be truncated
|
||||
@@ -757,7 +763,8 @@ class SecurityAssociation(object):
|
||||
@@ -757,7 +766,8 @@ class SecurityAssociation(object):
|
||||
SUPPORTED_PROTOS = (IP, IPv6)
|
||||
|
||||
def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None,
|
||||
@ -83,7 +107,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
"""
|
||||
@param proto: the IPsec proto to use (ESP or AH)
|
||||
@param spi: the Security Parameters Index of this SA
|
||||
@@ -771,6 +778,7 @@ class SecurityAssociation(object):
|
||||
@@ -771,6 +781,7 @@ class SecurityAssociation(object):
|
||||
to encapsulate the encrypted packets.
|
||||
@param nat_t_header: an instance of a UDP header that will be used
|
||||
for NAT-Traversal.
|
||||
@ -91,7 +115,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
"""
|
||||
|
||||
if proto not in (ESP, AH, ESP.name, AH.name):
|
||||
@@ -782,6 +790,7 @@ class SecurityAssociation(object):
|
||||
@@ -782,6 +793,7 @@ class SecurityAssociation(object):
|
||||
|
||||
self.spi = spi
|
||||
self.seq_num = seq_num
|
||||
@ -99,7 +123,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
|
||||
if crypt_algo:
|
||||
if crypt_algo not in CRYPT_ALGOS:
|
||||
@@ -827,6 +836,23 @@ class SecurityAssociation(object):
|
||||
@@ -827,6 +839,23 @@ class SecurityAssociation(object):
|
||||
raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' %
|
||||
(pkt.spi, self.spi))
|
||||
|
||||
@ -123,7 +147,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
def _encrypt_esp(self, pkt, seq_num=None, iv=None):
|
||||
|
||||
if iv is None:
|
||||
@@ -835,7 +861,8 @@ class SecurityAssociation(object):
|
||||
@@ -835,7 +864,8 @@ class SecurityAssociation(object):
|
||||
if len(iv) != self.crypt_algo.iv_size:
|
||||
raise TypeError('iv length must be %s' % self.crypt_algo.iv_size)
|
||||
|
||||
@ -133,7 +157,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
|
||||
if self.tunnel_header:
|
||||
tunnel = self.tunnel_header.copy()
|
||||
@@ -857,7 +884,7 @@ class SecurityAssociation(object):
|
||||
@@ -857,7 +887,7 @@ class SecurityAssociation(object):
|
||||
esp = self.crypt_algo.pad(esp)
|
||||
esp = self.crypt_algo.encrypt(self, esp, self.crypt_key)
|
||||
|
||||
@ -142,7 +166,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
|
||||
if self.nat_t_header:
|
||||
nat_t_header = self.nat_t_header.copy()
|
||||
@@ -884,7 +911,8 @@ class SecurityAssociation(object):
|
||||
@@ -884,7 +914,8 @@ class SecurityAssociation(object):
|
||||
|
||||
def _encrypt_ah(self, pkt, seq_num=None):
|
||||
|
||||
@ -152,7 +176,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
icv = b"\x00" * self.auth_algo.icv_size)
|
||||
|
||||
if self.tunnel_header:
|
||||
@@ -924,7 +952,8 @@ class SecurityAssociation(object):
|
||||
@@ -924,7 +955,8 @@ class SecurityAssociation(object):
|
||||
else:
|
||||
ip_header.plen = len(ip_header.payload) + len(ah) + len(payload)
|
||||
|
||||
@ -162,7 +186,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
|
||||
# sequence number must always change, unless specified by the user
|
||||
if seq_num is None:
|
||||
@@ -955,11 +984,12 @@ class SecurityAssociation(object):
|
||||
@@ -955,11 +987,12 @@ class SecurityAssociation(object):
|
||||
|
||||
def _decrypt_esp(self, pkt, verify=True):
|
||||
|
||||
@ -176,7 +200,7 @@ index 69e7ae3b..3a1724b2 100644
|
||||
|
||||
esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key,
|
||||
self.crypt_algo.icv_size or
|
||||
@@ -998,9 +1028,11 @@ class SecurityAssociation(object):
|
||||
@@ -998,9 +1031,11 @@ class SecurityAssociation(object):
|
||||
|
||||
def _decrypt_ah(self, pkt, verify=True):
|
||||
|
||||
|
Reference in New Issue
Block a user