blob: bfa27d20abbc6dede7661940ca94f210adc16edc [file] [log] [blame]
snaramre5d4b8912019-12-13 23:39:35 +00001diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
Benoît Ganne84e66582023-03-10 17:33:03 +01002index ae057ee1..b6806f71 100644
snaramre5d4b8912019-12-13 23:39:35 +00003--- a/scapy/layers/ipsec.py
4+++ b/scapy/layers/ipsec.py
Neale Ranns4a58e492020-12-21 13:19:10 +00005@@ -56,6 +56,7 @@ from scapy.fields import ByteEnumField, ByteField, IntField, PacketField, \
6 ShortField, StrField, XIntField, XStrField, XStrLenField
7 from scapy.packet import Packet, bind_layers, Raw
8 from scapy.layers.inet import IP, UDP
9+from scapy.contrib.mpls import MPLS
10 import scapy.modules.six as six
11 from scapy.modules.six.moves import range
12 from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \
Benoît Ganne84e66582023-03-10 17:33:03 +010013@@ -359,13 +360,17 @@ class CryptAlgo(object):
snaramre5d4b8912019-12-13 23:39:35 +000014 encryptor = cipher.encryptor()
15
16 if self.is_aead:
17- if esn_en:
18- aad = struct.pack('!LLL', esp.spi, esn, esp.seq)
19- else:
20- aad = struct.pack('!LL', esp.spi, esp.seq)
Benoît Ganne84e66582023-03-10 17:33:03 +010021+ aad = sa.build_aead(esp)
22+ if self.name == 'AES-NULL-GMAC':
23+ aad = aad + esp.iv + data
24+ aes_null_gmac_data = data
25+ data = b''
26 encryptor.authenticate_additional_data(aad)
snaramre5d4b8912019-12-13 23:39:35 +000027+
28 data = encryptor.update(data) + encryptor.finalize()
29 data += encryptor.tag[:self.icv_size]
Benoît Ganne84e66582023-03-10 17:33:03 +010030+ if self.name == 'AES-NULL-GMAC':
31+ data = aes_null_gmac_data + data
snaramre5d4b8912019-12-13 23:39:35 +000032 else:
Benoît Ganne84e66582023-03-10 17:33:03 +010033 data = encryptor.update(data) + encryptor.finalize()
34
35@@ -399,17 +404,19 @@ class CryptAlgo(object):
36 decryptor = cipher.decryptor()
snaramre5d4b8912019-12-13 23:39:35 +000037
38 if self.is_aead:
Benoît Ganne84e66582023-03-10 17:33:03 +010039+ aad = sa.build_aead(esp)
40+ if self.name == 'AES-NULL-GMAC':
41+ aad = aad + iv + data
42+ aes_null_gmac_data = data
43+ data = b''
snaramre5d4b8912019-12-13 23:39:35 +000044 # Tag value check is done during the finalize method
45- if esn_en:
46- decryptor.authenticate_additional_data(
47- struct.pack('!LLL', esp.spi, esn, esp.seq))
48- else:
49- decryptor.authenticate_additional_data(
50- struct.pack('!LL', esp.spi, esp.seq))
Benoît Ganne84e66582023-03-10 17:33:03 +010051+ decryptor.authenticate_additional_data(aad)
snaramre5d4b8912019-12-13 23:39:35 +000052 try:
53 data = decryptor.update(data) + decryptor.finalize()
54 except InvalidTag as err:
Benoît Ganne84e66582023-03-10 17:33:03 +010055 raise IPSecIntegrityError(err)
56+ if self.name == 'AES-NULL-GMAC':
57+ data = aes_null_gmac_data + data
58
59 # extract padlen and nh
60 padlen = orb(data[-2])
61@@ -445,6 +452,7 @@ if algorithms:
Christian Hoppsfb7e7ed2019-11-03 07:02:15 -050062 CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR',
63 cipher=algorithms.AES,
64 mode=modes.CTR,
65+ block_size=1,
66 iv_size=8,
67 salt_size=4,
68 format_mode_iv=_aes_ctr_format_mode_iv)
Benoît Ganne84e66582023-03-10 17:33:03 +010069@@ -452,14 +460,24 @@ if algorithms:
Christian Hoppsfb7e7ed2019-11-03 07:02:15 -050070 CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
71 cipher=algorithms.AES,
72 mode=modes.GCM,
73+ block_size=1,
74 salt_size=4,
75 iv_size=8,
76 icv_size=16,
Benoît Ganne84e66582023-03-10 17:33:03 +010077 format_mode_iv=_salt_format_mode_iv)
78+ CRYPT_ALGOS['AES-NULL-GMAC'] = CryptAlgo('AES-NULL-GMAC',
79+ cipher=algorithms.AES,
80+ mode=modes.GCM,
81+ block_size=1,
82+ salt_size=4,
83+ iv_size=8,
84+ icv_size=16,
85+ format_mode_iv=_salt_format_mode_iv)
86 if hasattr(modes, 'CCM'):
Christian Hoppsfb7e7ed2019-11-03 07:02:15 -050087 CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
88 cipher=algorithms.AES,
89 mode=modes.CCM,
90+ block_size=1,
91 iv_size=8,
92 salt_size=3,
93 icv_size=16,
Benoît Ganne84e66582023-03-10 17:33:03 +010094@@ -544,7 +562,7 @@ class AuthAlgo(object):
snaramre5d4b8912019-12-13 23:39:35 +000095 else:
96 return self.mac(key, self.digestmod(), default_backend())
97
98- def sign(self, pkt, key):
99+ def sign(self, pkt, key, trailer=None):
100 """
101 Sign an IPsec (ESP or AH) packet with this algo.
102
Benoît Ganne84e66582023-03-10 17:33:03 +0100103@@ -560,16 +578,20 @@ class AuthAlgo(object):
snaramre5d4b8912019-12-13 23:39:35 +0000104
105 if pkt.haslayer(ESP):
106 mac.update(raw(pkt[ESP]))
107+ if trailer:
108+ mac.update(trailer)
109 pkt[ESP].data += mac.finalize()[:self.icv_size]
110
111 elif pkt.haslayer(AH):
112 clone = zero_mutable_fields(pkt.copy(), sending=True)
113 mac.update(raw(clone))
114+ if trailer:
115+ mac.update(trailer)
116 pkt[AH].icv = mac.finalize()[:self.icv_size]
117
118 return pkt
119
120- def verify(self, pkt, key):
121+ def verify(self, pkt, key, trailer):
122 """
123 Check that the integrity check value (icv) of a packet is valid.
124
Benoît Ganne84e66582023-03-10 17:33:03 +0100125@@ -600,6 +622,8 @@ class AuthAlgo(object):
snaramre5d4b8912019-12-13 23:39:35 +0000126 clone = zero_mutable_fields(pkt.copy(), sending=False)
127
128 mac.update(raw(clone))
129+ if trailer:
130+ mac.update(trailer) # bytearray(4)) #raw(trailer))
131 computed_icv = mac.finalize()[:self.icv_size]
132
133 # XXX: Cannot use mac.verify because the ICV can be truncated
Benoît Ganne84e66582023-03-10 17:33:03 +0100134@@ -788,7 +812,7 @@ class SecurityAssociation(object):
Neale Ranns4a58e492020-12-21 13:19:10 +0000135 This class is responsible of "encryption" and "decryption" of IPsec packets. # noqa: E501
136 """
137
138- SUPPORTED_PROTOS = (IP, IPv6)
139+ SUPPORTED_PROTOS = (IP, IPv6, MPLS)
140
141 def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None,
142 auth_algo=None, auth_key=None, tunnel_header=None, nat_t_header=None, esn_en=False, esn=0): # noqa: E501
Benoît Ganne84e66582023-03-10 17:33:03 +0100143@@ -862,6 +886,23 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000144 raise TypeError('nat_t_header must be %s' % UDP.name)
145 self.nat_t_header = nat_t_header
146
147+ def build_aead(self, esp):
148+ if self.esn_en:
149+ return (struct.pack('!LLL', esp.spi, self.seq_num >> 32, esp.seq))
150+ else:
151+ return (struct.pack('!LL', esp.spi, esp.seq))
152+
153+ def build_seq_num(self, num):
154+ # only lower order bits are transmitted
155+ # higher order bits are used in the ICV
156+ lower = num & 0xffffffff
157+ upper = num >> 32
158+
159+ if self.esn_en:
160+ return lower, struct.pack("!I", upper)
161+ else:
162+ return lower, None
163+
164 def check_spi(self, pkt):
165 if pkt.spi != self.spi:
166 raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' %
Benoît Ganne84e66582023-03-10 17:33:03 +0100167@@ -875,7 +916,8 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000168 if len(iv) != self.crypt_algo.iv_size:
169 raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) # noqa: E501
170
171- esp = _ESPPlain(spi=self.spi, seq=seq_num or self.seq_num, iv=iv)
172+ low_seq_num, high_seq_num = self.build_seq_num(seq_num or self.seq_num)
173+ esp = _ESPPlain(spi=self.spi, seq=low_seq_num, iv=iv)
174
175 if self.tunnel_header:
176 tunnel = self.tunnel_header.copy()
Benoît Ganne84e66582023-03-10 17:33:03 +0100177@@ -899,7 +941,7 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000178 esn_en=esn_en or self.esn_en,
179 esn=esn or self.esn)
180
181- self.auth_algo.sign(esp, self.auth_key)
182+ self.auth_algo.sign(esp, self.auth_key, high_seq_num)
183
184 if self.nat_t_header:
185 nat_t_header = self.nat_t_header.copy()
Benoît Ganne84e66582023-03-10 17:33:03 +0100186@@ -926,7 +968,8 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000187
188 def _encrypt_ah(self, pkt, seq_num=None):
189
190- ah = AH(spi=self.spi, seq=seq_num or self.seq_num,
191+ low_seq_num, high_seq_num = self.build_seq_num(seq_num or self.seq_num)
192+ ah = AH(spi=self.spi, seq=low_seq_num,
193 icv=b"\x00" * self.auth_algo.icv_size)
194
195 if self.tunnel_header:
Benoît Ganne84e66582023-03-10 17:33:03 +0100196@@ -966,7 +1009,8 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000197 else:
198 ip_header.plen = len(ip_header.payload) + len(ah) + len(payload)
199
200- signed_pkt = self.auth_algo.sign(ip_header / ah / payload, self.auth_key) # noqa: E501
201+ signed_pkt = self.auth_algo.sign(ip_header / ah / payload,
202+ self.auth_key, high_seq_num) # noqa: E501
203
204 # sequence number must always change, unless specified by the user
205 if seq_num is None:
Benoît Ganne84e66582023-03-10 17:33:03 +0100206@@ -1003,11 +1047,12 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000207
208 def _decrypt_esp(self, pkt, verify=True, esn_en=None, esn=None):
209
210+ low_seq_num, high_seq_num = self.build_seq_num(self.seq_num)
211 encrypted = pkt[ESP]
212
213 if verify:
214 self.check_spi(pkt)
215- self.auth_algo.verify(encrypted, self.auth_key)
216+ self.auth_algo.verify(encrypted, self.auth_key, high_seq_num)
217
218 esp = self.crypt_algo.decrypt(self, encrypted, self.crypt_key,
219 self.crypt_algo.icv_size or
Benoît Ganne84e66582023-03-10 17:33:03 +0100220@@ -1048,9 +1093,10 @@ class SecurityAssociation(object):
snaramre5d4b8912019-12-13 23:39:35 +0000221
222 def _decrypt_ah(self, pkt, verify=True):
223
224+ low_seq_num, high_seq_num = self.build_seq_num(self.seq_num)
225 if verify:
226 self.check_spi(pkt)
227- self.auth_algo.verify(pkt, self.auth_key)
228+ self.auth_algo.verify(pkt, self.auth_key, high_seq_num)
229
230 ah = pkt[AH]
231 payload = ah.payload