Skip to content

Commit 770bb9f

Browse files
committed
Added IKEv2 as contrib (ticket #353)
1 parent 7ea7d36 commit 770bb9f

File tree

1 file changed

+361
-0
lines changed

1 file changed

+361
-0
lines changed

scapy/contrib/ikev2.py

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
#!/usr/bin/env python
2+
3+
# http://trac.secdev.org/scapy/ticket/353
4+
5+
# scapy.contrib.description = IKEv2
6+
# scapy.contrib.status = loads
7+
8+
from scapy.all import *
9+
import logging
10+
11+
12+
## Modified from the original ISAKMP code by Yaron Sheffer <[email protected]>, June 2010.
13+
14+
import struct
15+
from scapy.packet import *
16+
from scapy.fields import *
17+
from scapy.ansmachine import *
18+
from scapy.layers.inet import IP,UDP
19+
from scapy.sendrecv import sr
20+
21+
# see http://www.iana.org/assignments/ikev2-parameters for details
22+
IKEv2AttributeTypes= { "Encryption": (1, { "DES-IV64" : 1,
23+
"DES" : 2,
24+
"3DES" : 3,
25+
"RC5" : 4,
26+
"IDEA" : 5,
27+
"CAST" : 6,
28+
"Blowfish" : 7,
29+
"3IDEA" : 8,
30+
"DES-IV32" : 9,
31+
"AES-CBC" : 12,
32+
"AES-CTR" : 13,
33+
"AES-CCM-8" : 14,
34+
"AES-CCM-12" : 15,
35+
"AES-CCM-16" : 16,
36+
"AES-GCM-8ICV" : 18,
37+
"AES-GCM-12ICV" : 19,
38+
"AES-GCM-16ICV" : 20,
39+
"Camellia-CBC" : 23,
40+
"Camellia-CTR" : 24,
41+
"Camellia-CCM-8ICV" : 25,
42+
"Camellia-CCM-12ICV" : 26,
43+
"Camellia-CCM-16ICV" : 27,
44+
}, 0),
45+
"PRF": (2, {"PRF_HMAC_MD5":1,
46+
"PRF_HMAC_SHA1":2,
47+
"PRF_HMAC_TIGER":3,
48+
"PRF_AES128_XCBC":4,
49+
"PRF_HMAC_SHA2_256":5,
50+
"PRF_HMAC_SHA2_384":6,
51+
"PRF_HMAC_SHA2_512":7,
52+
"PRF_AES128_CMAC":8,
53+
}, 0),
54+
"Integrity": (3, { "HMAC-MD5-96": 1,
55+
"HMAC-SHA1-96": 2,
56+
"DES-MAC": 3,
57+
"KPDK-MD5": 4,
58+
"AES-XCBC-96": 5,
59+
"HMAC-MD5-128": 6,
60+
"HMAC-SHA1-160": 7,
61+
"AES-CMAC-96": 8,
62+
"AES-128-GMAC": 9,
63+
"AES-192-GMAC": 10,
64+
"AES-256-GMAC": 11,
65+
"SHA2-256-128": 12,
66+
"SHA2-384-192": 13,
67+
"SHA2-512-256": 14,
68+
}, 0),
69+
"GroupDesc": (4, { "768MODPgr" : 1,
70+
"1024MODPgr" : 2,
71+
"1536MODPgr" : 5,
72+
"2048MODPgr" : 14,
73+
"3072MODPgr" : 15,
74+
"4096MODPgr" : 16,
75+
"6144MODPgr" : 17,
76+
"8192MODPgr" : 18,
77+
"256randECPgr" : 19,
78+
"384randECPgr" : 20,
79+
"521randECPgr" : 21,
80+
"1024MODP160POSgr" : 22,
81+
"2048MODP224POSgr" : 23,
82+
"2048MODP256POSgr" : 24,
83+
"192randECPgr" : 25,
84+
"224randECPgr" : 26,
85+
}, 0),
86+
"Extended Sequence Number": (5, {"No ESN": 0,
87+
"ESN": 1, }, 0),
88+
}
89+
90+
# the name 'IKEv2TransformTypes' is actually a misnomer (since the table
91+
# holds info for all IKEv2 Attribute types, not just transforms, but we'll
92+
# keep it for backwards compatibility... for now at least
93+
IKEv2TransformTypes = IKEv2AttributeTypes
94+
95+
IKEv2TransformNum = {}
96+
for n in IKEv2TransformTypes:
97+
val = IKEv2TransformTypes[n]
98+
tmp = {}
99+
for e in val[1]:
100+
tmp[val[1][e]] = e
101+
IKEv2TransformNum[val[0]] = (n,tmp, val[2])
102+
103+
IKEv2Transforms = {}
104+
for n in IKEv2TransformTypes:
105+
IKEv2Transforms[IKEv2TransformTypes[n][0]]=n
106+
107+
del(n)
108+
del(e)
109+
del(tmp)
110+
del(val)
111+
112+
# Note: Transform and Proposal can only be used inside the SA payload
113+
IKEv2_payload_type = ["None", "", "Proposal", "Transform"]
114+
115+
IKEv2_payload_type.extend([""] * 29)
116+
IKEv2_payload_type.extend(["SA","KE","IDi","IDr", "CERT","CERTREQ","AUTH","Nonce","Notify","Delete",
117+
"VendorID","TSi","TSr","Encrypted","CP","EAP"])
118+
119+
IKEv2_exchange_type = [""] * 34
120+
IKEv2_exchange_type.extend(["IKE_SA_INIT","IKE_AUTH","CREATE_CHILD_SA",
121+
"INFORMATIONAL", "IKE_SESSION_RESUME"])
122+
123+
124+
class IKEv2_class(Packet):
125+
def guess_payload_class(self, payload):
126+
np = self.next_payload
127+
logging.debug("For IKEv2_class np=%d" % np)
128+
if np == 0:
129+
return Raw
130+
elif np < len(IKEv2_payload_type):
131+
pt = IKEv2_payload_type[np]
132+
logging.debug(globals().get("IKEv2_payload_%s" % pt, IKEv2_payload))
133+
return globals().get("IKEv2_payload_%s" % pt, IKEv2_payload)
134+
else:
135+
return IKEv2_payload
136+
137+
138+
class IKEv2(IKEv2_class): # rfc4306
139+
name = "IKEv2"
140+
fields_desc = [
141+
StrFixedLenField("init_SPI","",8),
142+
StrFixedLenField("resp_SPI","",8),
143+
ByteEnumField("next_payload",0,IKEv2_payload_type),
144+
XByteField("version",0x20), # IKEv2, right?
145+
ByteEnumField("exch_type",0,IKEv2_exchange_type),
146+
FlagsField("flags",0, 8, ["res0","res1","res2","Initiator","Version","Response","res6","res7"]),
147+
IntField("id",0),
148+
IntField("length",None)
149+
]
150+
151+
def guess_payload_class(self, payload):
152+
if self.flags & 1:
153+
return Raw
154+
return IKEv2_class.guess_payload_class(self, payload)
155+
156+
def answers(self, other):
157+
if isinstance(other, IKEv2):
158+
if other.init_SPI == self.init_SPI:
159+
return 1
160+
return 0
161+
def post_build(self, p, pay):
162+
p += pay
163+
if self.length is None:
164+
p = p[:24]+struct.pack("!I",len(p))+p[28:]
165+
return p
166+
167+
168+
class IKEv2_Key_Length_Attribute(IntField):
169+
# We only support the fixed-length Key Length attribute (the only one currently defined)
170+
name="key length"
171+
def __init__(self, name):
172+
IntField.__init__(self, name, "0x800E0000")
173+
174+
def i2h(self, pkt, x):
175+
return IntField.i2h(self, pkt, x & 0xFFFF)
176+
177+
def h2i(self, pkt, x):
178+
return IntField.h2i(self, pkt, struct.pack("!I", 0x800E0000 | int(x, 0)))
179+
180+
181+
class IKEv2_Transform_ID(ShortField):
182+
def i2h(self, pkt, x):
183+
if pkt == None:
184+
return None
185+
else:
186+
map = IKEv2TransformNum[pkt.transform_type][1]
187+
return map[x]
188+
189+
def h2i(self, pkt, x):
190+
if pkt == None:
191+
return None
192+
else:
193+
map = IKEv2TransformNum[pkt.transform_type][1]
194+
for k in keys(map):
195+
if map[k] == x:
196+
return k
197+
return None
198+
199+
class IKEv2_payload_Transform(IKEv2_class):
200+
name = "IKE Transform"
201+
fields_desc = [
202+
ByteEnumField("next_payload",None,{0:"last", 3:"Transform"}),
203+
ByteField("res",0),
204+
ShortField("length",8),
205+
ByteEnumField("transform_type",None,IKEv2Transforms),
206+
ByteField("res2",0),
207+
IKEv2_Transform_ID("transform_id", 0),
208+
ConditionalField(IKEv2_Key_Length_Attribute("key_length"), lambda pkt: pkt.length > 8),
209+
]
210+
211+
class IKEv2_payload_Proposal(IKEv2_class):
212+
name = "IKEv2 Proposal"
213+
fields_desc = [
214+
ByteEnumField("next_payload",None,{0:"last", 2:"Proposal"}),
215+
ByteField("res",0),
216+
FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8),
217+
ByteField("proposal",1),
218+
ByteEnumField("proto",1,{1:"IKEv2"}),
219+
FieldLenField("SPIsize",None,"SPI","B"),
220+
ByteField("trans_nb",None),
221+
StrLenField("SPI","",length_from=lambda x:x.SPIsize),
222+
PacketLenField("trans",Raw(),IKEv2_payload_Transform,length_from=lambda x:x.length-8),
223+
]
224+
225+
226+
class IKEv2_payload(IKEv2_class):
227+
name = "IKEv2 Payload"
228+
fields_desc = [
229+
ByteEnumField("next_payload",None,IKEv2_payload_type),
230+
FlagsField("flags",0, 8, ["critical","res1","res2","res3","res4","res5","res6","res7"]),
231+
FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
232+
StrLenField("load","",length_from=lambda x:x.length-4),
233+
]
234+
235+
236+
class IKEv2_payload_VendorID(IKEv2_class):
237+
name = "IKEv2 Vendor ID"
238+
overload_fields = { IKEv2: { "next_payload":43 }}
239+
fields_desc = [
240+
ByteEnumField("next_payload",None,IKEv2_payload_type),
241+
ByteField("res",0),
242+
FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4),
243+
StrLenField("vendorID","",length_from=lambda x:x.length-4),
244+
]
245+
246+
class IKEv2_payload_Delete(IKEv2_class):
247+
name = "IKEv2 Vendor ID"
248+
overload_fields = { IKEv2: { "next_payload":42 }}
249+
fields_desc = [
250+
ByteEnumField("next_payload",None,IKEv2_payload_type),
251+
ByteField("res",0),
252+
FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4),
253+
StrLenField("vendorID","",length_from=lambda x:x.length-4),
254+
]
255+
256+
class IKEv2_payload_SA(IKEv2_class):
257+
name = "IKEv2 SA"
258+
overload_fields = { IKEv2: { "next_payload":33 }}
259+
fields_desc = [
260+
ByteEnumField("next_payload",None,IKEv2_payload_type),
261+
ByteField("res",0),
262+
FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+4),
263+
PacketLenField("prop",Raw(),IKEv2_payload_Proposal,length_from=lambda x:x.length-4),
264+
]
265+
266+
class IKEv2_payload_Nonce(IKEv2_class):
267+
name = "IKEv2 Nonce"
268+
overload_fields = { IKEv2: { "next_payload":40 }}
269+
fields_desc = [
270+
ByteEnumField("next_payload",None,IKEv2_payload_type),
271+
ByteField("res",0),
272+
FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
273+
StrLenField("load","",length_from=lambda x:x.length-4),
274+
]
275+
276+
class IKEv2_payload_Notify(IKEv2_class):
277+
name = "IKEv2 Notify"
278+
overload_fields = { IKEv2: { "next_payload":41 }}
279+
fields_desc = [
280+
ByteEnumField("next_payload",None,IKEv2_payload_type),
281+
ByteField("res",0),
282+
FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
283+
StrLenField("load","",length_from=lambda x:x.length-4),
284+
]
285+
286+
class IKEv2_payload_KE(IKEv2_class):
287+
name = "IKEv2 Key Exchange"
288+
overload_fields = { IKEv2: { "next_payload":34 }}
289+
fields_desc = [
290+
ByteEnumField("next_payload",None,IKEv2_payload_type),
291+
ByteField("res",0),
292+
FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+6),
293+
ShortEnumField("group", 0, IKEv2TransformTypes['GroupDesc'][1]),
294+
StrLenField("load","",length_from=lambda x:x.length-6),
295+
]
296+
297+
class IKEv2_payload_IDi(IKEv2_class):
298+
name = "IKEv2 Identification - Initiator"
299+
overload_fields = { IKEv2: { "next_payload":35 }}
300+
fields_desc = [
301+
ByteEnumField("next_payload",None,IKEv2_payload_type),
302+
ByteField("res",0),
303+
FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8),
304+
ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}),
305+
ByteEnumField("ProtoID",0,{0:"Unused"}),
306+
ShortEnumField("Port",0,{0:"Unused"}),
307+
# IPField("IdentData","127.0.0.1"),
308+
StrLenField("load","",length_from=lambda x:x.length-8),
309+
]
310+
311+
class IKEv2_payload_IDr(IKEv2_class):
312+
name = "IKEv2 Identification - Responder"
313+
overload_fields = { IKEv2: { "next_payload":36 }}
314+
fields_desc = [
315+
ByteEnumField("next_payload",None,IKEv2_payload_type),
316+
ByteField("res",0),
317+
FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8),
318+
ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}),
319+
ByteEnumField("ProtoID",0,{0:"Unused"}),
320+
ShortEnumField("Port",0,{0:"Unused"}),
321+
# IPField("IdentData","127.0.0.1"),
322+
StrLenField("load","",length_from=lambda x:x.length-8),
323+
]
324+
325+
326+
327+
class IKEv2_payload_Encrypted(IKEv2_class):
328+
name = "IKEv2 Encrypted and Authenticated"
329+
overload_fields = { IKEv2: { "next_payload":46 }}
330+
fields_desc = [
331+
ByteEnumField("next_payload",None,IKEv2_payload_type),
332+
ByteField("res",0),
333+
FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4),
334+
StrLenField("load","",length_from=lambda x:x.length-4),
335+
]
336+
337+
338+
339+
IKEv2_payload_type_overload = {}
340+
for i in range(len(IKEv2_payload_type)):
341+
name = "IKEv2_payload_%s" % IKEv2_payload_type[i]
342+
if name in globals():
343+
IKEv2_payload_type_overload[globals()[name]] = {"next_payload":i}
344+
345+
del(i)
346+
del(name)
347+
IKEv2_class.overload_fields = IKEv2_payload_type_overload.copy()
348+
349+
split_layers(UDP, ISAKMP, sport=500)
350+
split_layers(UDP, ISAKMP, dport=500)
351+
352+
bind_layers( UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2
353+
bind_layers( UDP, IKEv2, dport=4500, sport=4500)
354+
355+
def ikev2scan(ip):
356+
return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8),
357+
exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal()))
358+
359+
# conf.debug_dissector = 1
360+
361+
interact(mydict=globals(), mybanner="IKEv2 alpha-level protocol implementation")

0 commit comments

Comments
 (0)