diff options
author | Christian Hopps <chopps@labn.net> | 2019-11-03 07:02:15 -0500 |
---|---|---|
committer | Damjan Marion <dmarion@me.com> | 2020-09-07 09:43:27 +0000 |
commit | fb7e7ed2cd10446d5ecd1b1e8df470e706c448ed (patch) | |
tree | 2590e834f09c2d936f83d9bc766af99e084a522f /test/patches | |
parent | dce44e4e2302042890dc8b579d4ff6f99509ae7a (diff) |
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
Diffstat (limited to 'test/patches')
-rw-r--r-- | test/patches/scapy-2.4.3/ipsec.patch | 54 | ||||
-rw-r--r-- | test/patches/scapy-2.4/ipsec.patch | 52 |
2 files changed, 76 insertions, 30 deletions
diff --git a/test/patches/scapy-2.4.3/ipsec.patch b/test/patches/scapy-2.4.3/ipsec.patch index 7ee8316bce3..d97de7fe9b7 100644 --- a/test/patches/scapy-2.4.3/ipsec.patch +++ b/test/patches/scapy-2.4.3/ipsec.patch @@ -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 - - diff --git a/test/patches/scapy-2.4/ipsec.patch b/test/patches/scapy-2.4/ipsec.patch index 083b0b94961..7eb0123a211 100644 --- a/test/patches/scapy-2.4/ipsec.patch +++ b/test/patches/scapy-2.4/ipsec.patch @@ -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): |