1# Copyright (c) 2017, The MITRE Corporation. All rights reserved.
2# See LICENSE.txt for complete terms.
3
4import unittest
5
6from mixbox.vendor.six import u
7
8from cybox.bindings.cybox_core import parseString
9from cybox.core import Observables
10from cybox.objects.address_object import Address
11from cybox.objects.network_packet_object import (
12    ARP, EthernetInterface, ICMPv4Packet, ICMPv6Packet, IPv4Packet,
13    IPv6Packet, NDP, NDPPrefixInfo, NDPLinkAddr, NetworkPacket, TCP, TCPFlags, UDP)
14from cybox.test import EntityTestCase
15from cybox.test.objects import ObjectTestCase
16
17
18class TestNetworkPacket(ObjectTestCase, unittest.TestCase):
19    object_type = "NetworkPacketObjectType"
20    klass = NetworkPacket
21
22    _full_dict = {
23        'custom_properties': [
24            {'name': "Prop1", 'description': "Property1", 'value': "Value1"},
25            {'name': "Prop2", 'description': "Property2", 'value': "Value2"},
26        ],
27        'link_layer': {
28            'physical_interface': {
29                'ethernet': {'ethernet_header': {'checksum': "fa10"}},
30            },
31            'logical_protocols': {
32                'arp_rarp': {'hardware_addr_type': "Ethernet(1)"},
33                'ndp': {'icmpv6_header': {'checksum': "06BC"}},
34            },
35        },
36        'internet_layer': {
37            # These are tested thoroughly below
38            'ipv4': {'ipv4_header': {'ip_version': "IPv4(4)"}},
39            'icmpv4': {'icmpv4_header': {'type': "01"}},
40            'ipv6': {'ipv6_header': {'ip_version': 'IPv6(6)'}},
41            'icmpv6': {'icmpv6_header': {'type': "01"}},
42        },
43        'transport_layer': {
44            'tcp': {'data': {'data_segment': "GET /index.html http/1.1"}},
45            'udp': {'data': {'data_segment': "whois example.com"}},
46        },
47        'xsi:type': object_type,
48    }
49
50    # https://github.com/CybOXProject/python-cybox/issues/181
51    def test_round_trip_xml(self):
52        np = NetworkPacket.from_dict(self._full_dict)
53        xml = Observables(np).to_xml(encoding=None)
54
55        new_obj = Observables.from_obj(parseString(xml))
56        new_dict = new_obj.observables[0].object_.properties.to_dict()
57
58        self.maxDiff = None
59        self.assertEqual(self._full_dict, new_dict)
60
61
62class TestEthernetInterface(EntityTestCase, unittest.TestCase):
63    klass = EthernetInterface
64
65    _full_dict = {
66        'ethernet_header': {
67            'destination_mac_addr': {'address_value': u("00:11:22:33:44:55"),
68                                        'category': Address.CAT_MAC,
69                                        'xsi:type': 'AddressObjectType'},
70            'source_mac_addr': {'address_value': u("aa:bb:cc:dd:ee:ff"),
71                                'category': Address.CAT_MAC,
72                                'xsi:type': 'AddressObjectType'},
73            'type_or_length': {'length': u("1abf"),
74                                'internet_layer_type': u("IPv4(0x0800)")},
75            'checksum': u("fa10"),
76        }
77    }
78
79
80class TestARP(EntityTestCase, unittest.TestCase):
81    klass = ARP
82
83    _full_dict = {
84        'hardware_addr_type': u("Ethernet(1)"),
85        'proto_addr_type': u("IPv4(0x0800)"),
86        'hardware_addr_size': u("6"),
87        'proto_addr_size': u("4"),
88        'op_type': u("ARP request(1)"),
89        'sender_hardware_addr': {'address_value': u("01:12:23:34:45:56"),
90                                    'category': Address.CAT_MAC,
91                                    'xsi:type': 'AddressObjectType'},
92        'sender_protocol_addr': {'address_value': u("1.2.3.4"),
93                                    'category': Address.CAT_IPV4,
94                                    'xsi:type': 'AddressObjectType'},
95        'recip_hardware_addr': {'address_value': u("a0:b0:c0:d0:e0:f0"),
96                                'category': Address.CAT_MAC,
97                                'xsi:type': 'AddressObjectType'},
98        'recip_protocol_addr': {'address_value': u("9.10.11.12"),
99                                'category': Address.CAT_IPV4,
100                                'xsi:type': 'AddressObjectType'},
101    }
102
103
104class TestNDPLinkAddr(EntityTestCase, unittest.TestCase):
105    klass = NDPLinkAddr
106    _full_dict = {
107        'length': u('14'),
108        'link_layer_mac_addr': {'address_value': u("80:70:60:50:40:30"),
109                                'category': Address.CAT_MAC,
110                                'xsi:type': 'AddressObjectType'},
111    }
112
113
114class TestNDPPrefixInfo(EntityTestCase, unittest.TestCase):
115    klass = NDPPrefixInfo
116
117    _full_dict = {
118        'link_flag': False,
119        'addr_config_flag': True,
120        'length': u("14"),
121        'prefix_length': 7,
122        'valid_lifetime': 123455,
123        'preferred_lifetime': 1000,
124        'prefix': {
125            'ipv6_addr': {'address_value': u("2001:db8::ff00:42:832"),
126                            'category': Address.CAT_IPV6,
127                            'xsi:type': 'AddressObjectType'},
128            'ip_addr_prefix': {'address_value': u("2001:0db8:85a3"),
129                                    'category': Address.CAT_IPV6,
130                                    'xsi:type': 'AddressObjectType'},
131        }
132    }
133
134
135class TestNDP(EntityTestCase, unittest.TestCase):
136    klass = NDP
137
138    _full_dict = {
139        'icmpv6_header': {
140            'type': u("01"),
141            'code': u("07"),
142            'checksum': u("06BC"),
143        },
144        # This is normally a choice, but we can currently just check
145        # them all at once. For X_link_addr, we are checking them above,
146        # so we only use a length here.
147        'router_solicitation': {
148            'options': [{'src_link_addr': {'length': u("16")}}],
149        },
150        'router_advertisement': {
151            'managed_address_config_flag': True,
152            'other_config_flag': False,
153            'cur_hop_limit': 16,
154            'reachable_time': 1000,
155            'retrans_timer': 5000,
156            'options': {
157                'src_link_addr': {'length': u("8")},
158                'mtu': {'length': 8, 'mtu': 1500},
159                'prefix_info': {'link_flag': True, 'length': u("44")},
160            }
161        },
162        'neighbor_solicitation': {
163            'target_ipv6_addr': {'address_value': u("2001:db99::ff00:832"),
164                                    'category': Address.CAT_IPV6,
165                                    'xsi:type': 'AddressObjectType'},
166            'options': {'src_link_addr': {'length': u("99")}},
167        },
168        'neighbor_advertisement': {
169            'router_flag': True,
170            'solicited_flag': False,
171            'override_flag': True,
172            'target_ipv6_addr': {'address_value': u("::1"),
173                                 'category': Address.CAT_IPV6,
174                                 'xsi:type': 'AddressObjectType'},
175            'options': {'target_link_addr': {'length': u("48")}},
176        },
177        'redirect': {
178            'target_ipv6_addr': {'address_value': u("2001::1"),
179                                    'category': Address.CAT_IPV6,
180                                    'xsi:type': 'AddressObjectType'},
181            'dest_ipv6_addr': {'address_value': u("2001::dd88:1"),
182                                'category': Address.CAT_IPV6,
183                                'xsi:type': 'AddressObjectType'},
184            'options': {
185                'target_link_addr': {'length': u("48")},
186                'redirected_header': {
187                    'length': u("77"),
188                    'ipheader_and_data': '3bda4659ace',
189                },
190            },
191        },
192    }
193
194
195class TestIPv4Packet(EntityTestCase, unittest.TestCase):
196    klass = IPv4Packet
197
198    _full_dict = {
199        'ipv4_header': {
200            'ip_version': u("IPv4(4)"),
201            'header_length': 32,
202            'dscp': u("5fc1"),
203            'ecn': u("4ca6"),
204            'total_length': u("ffff"),
205            'identification': 10,
206            'flags': {
207                'reserved': 0,
208                'do_not_fragment': u("donotfragment(1)"),
209                'more_fragments': u("lastfragment(0)"),
210            },
211            'fragment_offset': u("7c"),
212            'ttl': u("fa"),
213            'protocol': u("TPC(6)"),
214            'checksum': u("0fca"),
215            'src_ipv4_addr': {'address_value': u("172.16.21.50"),
216                            'category': Address.CAT_IPV4,
217                            'xsi:type': 'AddressObjectType'},
218            'dest_ipv4_addr': {'address_value': u("172.16.21.1"),
219                            'category': Address.CAT_IPV4,
220                            'xsi:type': 'AddressObjectType'},
221            'option': [
222                {'copy_flag': u("donotcopy(0)"), 'class': u("control(0)")},
223                {'class': u("reserved(3)"), 'option': u("security(2)")},
224            ]
225        },
226        'data': u("04fc3a3f67e4"),
227    }
228
229
230class TestICMPv4(EntityTestCase, unittest.TestCase):
231    klass = ICMPv4Packet
232
233    _full_dict = {
234        'icmpv4_header': {
235            'type': u("02"),
236            'code': u("06"), 'checksum': u("06BC"), },
237        'error_msg': {
238            'destination_unreachable': {
239                'destination_network_unreachable': True,
240                'destination_host_unreachable': True,
241                'destination_protocol_unreachable': True,
242                'destination_port_unreachable': True,
243                'fragmentation_required': {
244                    'fragmentation_required': True,
245                    'next_hop_mtu': u("a150"),
246                },
247                'source_route_failed': True,
248                'destination_network_unknown': True,
249                'destination_host_unknown': True,
250                'source_host_isolated': True,
251                'network_administratively_prohibited': True,
252                'host_administratively_prohibited': True,
253                'network_unreachable_for_tos': True,
254                'host_unreachable_for_tos': True,
255                'communication_administratively_prohibited': True,
256                'host_precedence_violation': True,
257                'precedence_cutoff_in_effect': True,
258            },
259            'source_quench': {
260                'source_quench': True,
261            },
262            'redirect_message': {
263                'network_redirect': True,
264                'host_redirect': True,
265                'tos_network_redirect': True,
266                'tos_host_redirect': True,
267                'ip_address': {'address_value': u("10.3.4.5"),
268                                'category': Address.CAT_IPV4,
269                                'xsi:type': 'AddressObjectType'},
270            },
271            'time_exceeded': {
272                'ttl_exceeded_in_transit': True,
273                'frag_reassembly_time_exceeded': True,
274            },
275            'error_msg_content': {
276                'ip_header': {'ip_version': u("IPv4(4)")},
277                'first_eight_bytes': u("0123456789abcdef"),
278            },
279        },
280        'info_msg': {
281            'echo_reply': {
282                'echo_reply': True,
283                'data': u("5f3dce41")
284            },
285            'echo_request': {
286                'echo_request': True,
287                'data': u("14ecd3f5")
288            },
289            'timestamp_request': {
290                'timestamp': True,
291                'originate_timestamp': 4567812,
292            },
293            'timestamp_reply': {
294                'timestamp_reply': True,
295                'originate_timestamp': 4567811,
296                'receive_timestamp': 4567814,
297                'transmit_timestamp': 4567819,
298            },
299            'address_mask_request': {
300                'address_mask_request': True,
301                'address_mask': {'address_value': u("255.255.0.0"),
302                                    'category': Address.CAT_IPV4_NETMASK,
303                                    'xsi:type': 'AddressObjectType'},
304            },
305            'address_mask_reply': {
306                'address_mask_reply': True,
307                'address_mask': {'address_value': u("255.255.255.0"),
308                                    'category': Address.CAT_IPV4_NETMASK,
309                                    'xsi:type': 'AddressObjectType'},
310            },
311            'info_msg_content': {
312                'identifier': u("f198"),
313                'sequence_number': u("1dc9"),
314            },
315        },
316        'traceroute': {
317            'outbound_packet_forward_success': True,
318            'outbound_packet_no_route': True,
319            'identifier': u("1234"),
320            'outbound_hop_count': u("001f"),
321            'return_hop_count': u("001c"),
322            'output_link_speed': u("000f42f0"),
323            'output_link_mtu': u("00000000"),
324        },
325    }
326
327
328class TestIPv6(EntityTestCase, unittest.TestCase):
329    klass = IPv6Packet
330
331    _full_dict = {
332        'ipv6_header': {
333            'ip_version': u("IPv6(6)"),
334            'traffic_class': u("ff"),
335            'flow_label': u("abcde"),
336            'payload_length': u("1000"),
337            'next_header': u("TCP(6)"),
338            'ttl': u('fa'),
339            'src_ipv6_addr': {'address_value': u("2001:85a3::8a2e:370:734"),
340                            'category': Address.CAT_IPV6,
341                            'xsi:type': 'AddressObjectType'},
342            'dest_ipv6_addr': {'address_value': u("2001:3a58::8a2e:370:734"),
343                            'category': Address.CAT_IPV6,
344                            'xsi:type': 'AddressObjectType'},
345        },
346        'ext_headers': [
347            {
348                'hop_by_hop_options': {
349                    'next_header': u("IPv6routingheader(43)"),
350                    'header_ext_len': u("1f"),
351                    'option_data': [
352                        {
353                            'option_type': {
354                                'do_not_recogn_action': u("skipoption(00)"),
355                                'packet_change': u("change(1)"),
356                                'option_byte': u("04"),
357                            },
358                            'option_data_len': u("0100"),
359                            'pad1': {'octet': u("00")},
360                            'padn': {'octet': u("01"),
361                                        'option_data_length': 42,
362                                        'option_data': 00}
363                        },
364                        {
365                            'option_type': {'option_byte': u("02")},
366                            'option_data_len': u("007f"),
367                            'pad1': {'octet': u("00")},
368                        },
369                    ]
370                },
371            },
372            {
373                'routing': {
374                    'next_header': u("IPv6routingheader(43)"),
375                    'header_ext_len': 32,
376                    'routing_type': u("4a"),
377                    'segments_left': 13,
378                    'type_specific_data': u("AAAAAAAA")
379                },
380            },
381            {
382                'fragment': {
383                    'fragment_header': {
384                        'next_header': u("IPv6routingheader(43)"),
385                        'fragment_offset': u("01fb"),
386                        'm_flag': u("lastfragment(0)"),
387                        'identification': u("aa"),
388                    },
389                    'fragment': u("bc27648fbace")
390                },
391            },
392            {
393                'destination_options': [
394                    {
395                        'next_header': u("IPv6routingheader(43)"),
396                        'header_ext_len': u("1f"),
397                        'option_data': [
398                            {
399                                'option_data_len': u("0100"),
400                                'pad1': {'octet': u("00")},
401                            },
402                            {
403                                'option_type': {'option_byte': u("02")},
404                            },
405                        ]
406                    },
407                    {
408                        'header_ext_len': u("1f"),
409                    },
410                ],
411            },
412            {
413                'authentication_header': {
414                    'next_header': u("IPv6routingheader(43)"),
415                    'header_ext_len': u("1f"),
416                    'security_parameters_index': u("deadbeef"),
417                    'sequence_number': u("feedbacc"),
418                    'authentication_data': u("abcd0123fedc4567"),
419                },
420            },
421            {
422                'encapsulating_security_payload': {
423                    'security_parameters_index': u("deadbeef"),
424                    'sequence_number': u("feedbacc"),
425                    'payload_data': u("777788889999aaaa"),
426                    'padding': u("0000000"),
427                    'padding_len': u("7"),
428                    'next_header': u("IPv6routingheader(43)"),
429                    'authentication_data': u("abcd0123fedc4567"),
430                },
431            },
432        ],
433    }
434
435
436class TestICMPv6(EntityTestCase, unittest.TestCase):
437    klass = ICMPv6Packet
438
439    _full_dict = {
440        'icmpv6_header': {
441            'type': u("02"),
442            'code': u("06"),
443            'checksum': u("06BC"),
444        },
445        'error_msg': {
446            'destination_unreachable': {
447                'no_route': True,
448                'comm_prohibited': True,
449                'beyond_scope': True,
450                'address_unreachable': True,
451                'port_unreachable': True,
452                'src_addr_failed_policy': True,
453                'reject_route': True,
454            },
455            'packet_too_big': {
456                'packet_too_big': True,
457                'mtu': u("1f00"),
458            },
459            'time_exceeded': {
460                'hop_limit_exceeded': True,
461                'fragment_reassem_time_exceeded': True,
462            },
463            'parameter_problem': {
464                'erroneous_header_field': True,
465                'unrecognized_next_header_type': True,
466                'unrecognized_ipv6_option': True,
467                'pointer': u("7fffaabb"),
468            },
469            'invoking_packet': u("0f1e2d3c4b5a9687"),
470        },
471        'info_msg': {
472            'echo_request': {
473                'echo_request': True,
474                'data': u("2468ace0")
475            },
476            'echo_reply': {
477                'echo_reply': True,
478                'data': u("0eca8642")
479            },
480            'info_msg_content': {
481                'identifier': u("f198"),
482                'sequence_number': u("1dc9"),
483            },
484        },
485    }
486
487
488class TestTCPFlags(EntityTestCase, unittest.TestCase):
489    klass = TCPFlags
490
491    _full_dict = {
492        'ns': True,
493        'cwr': True,
494        'ece': True,
495        'urg': True,
496        'ack': True,
497        'psh': True,
498        'rst': True,
499        'syn': True,
500        'fin': True,
501    }
502
503
504class TestTCP(EntityTestCase, unittest.TestCase):
505    klass = TCP
506
507    _full_dict = {
508        'tcp_header': {
509            'src_port': {'port_value': 1444,
510                            'layer4_protocol': u('TCP'),
511                            'xsi:type': 'PortObjectType'},
512            'dest_port': {'port_value': 80,
513                            'layer4_protocol': u('TCP'),
514                            'xsi:type': 'PortObjectType'},
515            'seq_num': u("148f3b44"),
516            'ack_num': u("664d012a"),
517            'data_offset': u("20"),
518            'reserved': 1,
519            'tcp_flags': TestTCPFlags._full_dict,
520            'window': u("1460"),
521            'checksum': u("1fc1"),
522            'urg_ptr': u("0001")
523        },
524        'options': u("010305"),
525        'data': {
526            'data_format': "Text",
527            'data_size': {"value": u("100"), 'units': "Kilobytes"},
528            'data_segment': u("A long, long time ago..."),
529            'offset': 100,
530            'search_distance': 200,
531            'search_within': 40,
532        }
533    }
534
535
536class TestUDP(EntityTestCase, unittest.TestCase):
537    klass = UDP
538
539    _full_dict = {
540        'udp_header': {
541            'srcport': {'port_value': 1664,
542                            'layer4_protocol': u('UDP'),
543                            'xsi:type': 'PortObjectType'},
544            'destport': {'port_value': 53,
545                            'layer4_protocol': u('UDP'),
546                            'xsi:type': 'PortObjectType'},
547            'length': 0x18,
548            'checksum': u("1fc1"),
549        },
550        'data': {
551            'data_format': "Hex",
552            'data_size': {"value": u("16"), 'units': "Bytes"},
553            'data_segment': u("000102030405060708090a0b0c0d0e0f"),
554        }
555    }
556
557
558if __name__ == "__main__":
559    unittest.main()
560