diff --git a/scapy/contrib/macsec.py b/scapy/contrib/macsec.py new file mode 100644 index 0000000000000000000000000000000000000000..b038d38460e4d04aef01ea2b8d241cc9cfa3dff4 --- /dev/null +++ b/scapy/contrib/macsec.py @@ -0,0 +1,215 @@ +# This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Sabrina Dubroca <sd@queasysnail.net> +## This program is published under a GPLv2 license + +""" +Classes and functions for MACsec. +""" + +from __future__ import absolute_import +from __future__ import print_function +import struct + +from scapy.config import conf +from scapy.fields import * +from scapy.packet import Packet, Raw, bind_layers +from scapy.layers.l2 import Ether, Dot1AD, Dot1Q +from scapy.layers.eap import MACsecSCI +from scapy.layers.inet import IP +from scapy.layers.inet6 import IPv6 + +if conf.crypto_valid: + from cryptography.exceptions import InvalidTag + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import interfaces + from cryptography.hazmat.primitives.ciphers import ( + Cipher, + algorithms, + modes, + ) +else: + log_loading.info("Can't import python-cryptography v1.7+. " + "Disabled MACsec encryption/authentication.") + + +NOSCI_LEN = 14 + 6 +SCI_LEN = 8 +DEFAULT_ICV_LEN = 16 + + +class MACsecSA(object): + """Representation of a MACsec Secure Association + + Provides encapsulation, decapsulation, encryption, and decryption + of MACsec frames + """ + def __init__(self, sci, an, pn, key, icvlen, encrypt, send_sci): + if isinstance(sci, int) or isinstance(sci, long): + self.sci = struct.pack('!Q', sci) + elif isinstance(sci, str): + self.sci = sci + else: + raise TypeError("SCI must be either str or int") + self.an = an + self.pn = pn + self.key = key + self.icvlen = icvlen + self.do_encrypt = encrypt + self.send_sci = send_sci + + def make_iv(self, pkt): + """generate an IV for the packet""" + return self.sci + struct.pack('!I', pkt[MACsec].pn) + + @staticmethod + def split_pkt(pkt, assoclen, icvlen=0): + """ + split the packet into associated data, plaintext or ciphertext, and + optional ICV + """ + data = raw(pkt) + assoc = data[:assoclen] + if icvlen: + icv = data[-icvlen:] + enc = data[assoclen:-icvlen] + else: + icv = b'' + enc = data[assoclen:] + return assoc, enc, icv + + def e_bit(self): + """returns the value of the E bit for packets sent through this SA""" + return self.do_encrypt + + def c_bit(self): + """returns the value of the C bit for packets sent through this SA""" + return self.do_encrypt or self.icvlen != DEFAULT_ICV_LEN + + @staticmethod + def shortlen(pkt): + """determine shortlen for a raw packet (not encapsulated yet)""" + datalen = len(pkt) - 2*6 + if datalen < 48: + return datalen + return 0 + + def encap(self, pkt): + """encapsulate a frame using this Secure Association""" + if pkt.name != Ether().name: + raise TypeError('cannot encapsulate packet in MACsec, must be Ethernet') + hdr = copy.deepcopy(pkt) + payload = hdr.payload + del hdr.payload + tag = MACsec(sci=self.sci, an=self.an, + SC=self.send_sci, + E=self.e_bit(), C=self.c_bit(), + shortlen=MACsecSA.shortlen(pkt), + pn=self.pn, type=pkt.type) + hdr.type = ETH_P_MACSEC + return hdr/tag/payload + + # this doesn't really need to be a method, but for symmetry with + # encap(), it is + def decap(self, orig_pkt): + """decapsulate a MACsec frame""" + if orig_pkt.name != Ether().name or orig_pkt.payload.name != MACsec().name: + raise TypeError('cannot decapsulate MACsec packet, must be Ethernet/MACsec') + packet = copy.deepcopy(orig_pkt) + prev_layer = packet[MACsec].underlayer + prev_layer.type = packet[MACsec].type + next_layer = packet[MACsec].payload + del prev_layer.payload + if prev_layer.name == Ether().name: + return Ether(raw(prev_layer/next_layer)) + return prev_layer/next_layer + + def encrypt(self, orig_pkt, assoclen=None): + """encrypt a MACsec frame for this Secure Association""" + hdr = copy.deepcopy(orig_pkt) + del hdr[MACsec].payload + del hdr[MACsec].type + pktlen = len(orig_pkt) + if self.send_sci: + hdrlen = NOSCI_LEN + SCI_LEN + else: + hdrlen = NOSCI_LEN + if assoclen is None or not self.do_encrypt: + if self.do_encrypt: + assoclen = hdrlen + else: + assoclen = pktlen + iv = self.make_iv(orig_pkt) + assoc, pt, _ = MACsecSA.split_pkt(orig_pkt, assoclen) + encryptor = Cipher( + algorithms.AES(self.key), + modes.GCM(iv), + backend=default_backend() + ).encryptor() + encryptor.authenticate_additional_data(assoc) + ct = encryptor.update(pt) + encryptor.finalize() + hdr[MACsec].payload = Raw(assoc[hdrlen:assoclen] + ct + encryptor.tag) + return hdr + + def decrypt(self, orig_pkt, assoclen=None): + """decrypt a MACsec frame for this Secure Association""" + hdr = copy.deepcopy(orig_pkt) + del hdr[MACsec].payload + pktlen = len(orig_pkt) + if self.send_sci: + hdrlen = NOSCI_LEN + SCI_LEN + else: + hdrlen = NOSCI_LEN + if assoclen is None or not self.do_encrypt: + if self.do_encrypt: + assoclen = hdrlen + else: + assoclen = pktlen - self.icvlen + iv = self.make_iv(hdr) + assoc, ct, icv = MACsecSA.split_pkt(orig_pkt, assoclen, self.icvlen) + decryptor = Cipher( + algorithms.AES(self.key), + modes.GCM(iv, icv), + backend=default_backend() + ).decryptor() + decryptor.authenticate_additional_data(assoc) + pt = assoc[hdrlen:assoclen] + pt += decryptor.update(ct) + pt += decryptor.finalize() + hdr[MACsec].type = struct.unpack('!H', pt[0:2])[0] + hdr[MACsec].payload = Raw(pt[2:]) + return hdr + + +class MACsec(Packet): + """representation of one MACsec frame""" + name = '802.1AE' + fields_desc = [BitField('Ver', 0, 1), + BitField('ES', 0, 1), + BitField('SC', 0, 1), + BitField('SCB', 0, 1), + BitField('E', 0, 1), + BitField('C', 0, 1), + BitField('an', 0, 2), + BitField('reserved', 0, 2), + BitField('shortlen', 0, 6), + IntField("pn", 1), + ConditionalField(PacketField("sci", None, MACsecSCI), lambda pkt: pkt.SC), + ConditionalField(XShortEnumField("type", None, ETHER_TYPES), + lambda pkt: pkt.type is not None)] + + def mysummary(self): + summary = self.sprintf("an=%MACsec.an%, pn=%MACsec.pn%") + if self.SC: + summary += self.sprintf(", sci=%MACsec.sci%") + if self.type is not None: + summary += self.sprintf(", %MACsec.type%") + return summary + + +bind_layers(MACsec, IP, type=ETH_P_IP) +bind_layers(MACsec, IPv6, type=ETH_P_IPV6) + +bind_layers( Dot1AD, MACsec, type=ETH_P_MACSEC) +bind_layers( Dot1Q, MACsec, type=ETH_P_MACSEC) +bind_layers( Ether, MACsec, type=ETH_P_MACSEC) diff --git a/scapy/contrib/macsec.uts b/scapy/contrib/macsec.uts new file mode 100644 index 0000000000000000000000000000000000000000..32ed859012550b7d70444a2d26a3bb61505492e2 --- /dev/null +++ b/scapy/contrib/macsec.uts @@ -0,0 +1,269 @@ +# MACsec unit tests +# run with: +# test/run_tests -P "load_contrib('macsec')" -t scapy/contrib/macsec.uts -F + ++ MACsec +~ crypto not_pypy +# Note: these tests are disabled with pypy, as the cryptography module does +# not currently work with the pypy version used by Travis CI. + += MACsec - basic encap - encrypted +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +assert(m.type == ETH_P_MACSEC) +assert(m[MACsec].type == ETH_P_IP) +assert(len(m) == len(p) + 16) +assert(m[MACsec].an == 0) +assert(m[MACsec].pn == 100) +assert(m[MACsec].shortlen == 0) +assert(m[MACsec].SC) +assert(m[MACsec].E) +assert(m[MACsec].C) +assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') + += MACsec - basic encryption - encrypted +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +e = sa.encrypt(m) +assert(e.type == ETH_P_MACSEC) +assert(e[MACsec].type == None) +assert(len(e) == len(p) + 16 + 16) +assert(e[MACsec].an == 0) +assert(e[MACsec].pn == 100) +assert(e[MACsec].shortlen == 0) +assert(e[MACsec].SC) +assert(e[MACsec].E) +assert(e[MACsec].C) +assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') + += MACsec - basic decryption - encrypted +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +e = sa.encrypt(m) +d = sa.decrypt(e) +assert(d.type == ETH_P_MACSEC) +assert(d[MACsec].type == ETH_P_IP) +assert(len(d) == len(m)) +assert(d[MACsec].an == 0) +assert(d[MACsec].pn == 100) +assert(d[MACsec].shortlen == 0) +assert(d[MACsec].SC) +assert(d[MACsec].E) +assert(d[MACsec].C) +assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') +assert(raw(d) == raw(m)) + += MACsec - basic decap - decrypted +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +e = sa.encrypt(m) +d = sa.decrypt(e) +r = sa.decap(d) +assert(raw(r) == raw(p)) + + + += MACsec - basic encap - integrity only +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +assert(m.type == ETH_P_MACSEC) +assert(m[MACsec].type == ETH_P_IP) +assert(len(m) == len(p) + 16) +assert(m[MACsec].an == 0) +assert(m[MACsec].pn == 200) +assert(m[MACsec].shortlen == 0) +assert(m[MACsec].SC) +assert(not m[MACsec].E) +assert(not m[MACsec].C) +assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') + += MACsec - basic encryption - integrity only +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +e = sa.encrypt(m) +assert(m.type == ETH_P_MACSEC) +assert(e[MACsec].type == None) +assert(len(e) == len(p) + 16 + 16) +assert(e[MACsec].an == 0) +assert(e[MACsec].pn == 200) +assert(e[MACsec].shortlen == 0) +assert(e[MACsec].SC) +assert(not e[MACsec].E) +assert(not e[MACsec].C) +assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') +assert(raw(e)[:-16] == raw(m)) + += MACsec - basic decryption - integrity only +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +e = sa.encrypt(m) +d = sa.decrypt(e) +assert(d.type == ETH_P_MACSEC) +assert(d[MACsec].type == ETH_P_IP) +assert(len(d) == len(m)) +assert(d[MACsec].an == 0) +assert(d[MACsec].pn == 200) +assert(d[MACsec].shortlen == 0) +assert(d[MACsec].SC) +assert(not d[MACsec].E) +assert(not d[MACsec].C) +assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01') +assert(raw(d) == raw(m)) + += MACsec - basic decap - integrity only +sa = MACsecSA(sci='\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +m = sa.encap(p) +e = sa.encrypt(m) +d = sa.decrypt(e) +r = sa.decap(d) +assert(raw(r) == raw(p)) + += MACsec - encap - shortlen 2 +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd') +m = sa.encap(p) +assert(m[MACsec].shortlen == 2) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 10 +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 8) +m = sa.encap(p) +assert(m[MACsec].shortlen == 10) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 18 +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 16) +m = sa.encap(p) +assert(m[MACsec].shortlen == 18) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 32 +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30) +m = sa.encap(p) +assert(m[MACsec].shortlen == 32) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 40 +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 38) +m = sa.encap(p) +assert(m[MACsec].shortlen == 40) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 47 +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45) +m = sa.encap(p) +assert(m[MACsec].shortlen == 47) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 0 (48) +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45 + "y") +m = sa.encap(p) +assert(m[MACsec].shortlen == 0) + + += MACsec - encap - shortlen 2/nosci +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd') +m = sa.encap(p) +assert(m[MACsec].shortlen == 2) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 32/nosci +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30) +m = sa.encap(p) +assert(m[MACsec].shortlen == 32) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + += MACsec - encap - shortlen 47/nosci +sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0) +p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45) +m = sa.encap(p) +assert(m[MACsec].shortlen == 47) +assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0)) + + += MACsec - authenticate +tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5 \x00\x00\x00\x00\rRT\x00\x13\x01V\x00\x01\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\xf1\xb8\xe4,b\xb00\x98L\x85m1Q9\t:") +rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01") +rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1) +txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1) +txdec = txsa.decap(txsa.decrypt(tx)) +rxdec = rxsa.decap(rxsa.decrypt(rx)) +txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37" +rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37" +assert(raw(txdec) == raw(txref)) +assert(raw(rxdec) == raw(rxref)) + + + += MACsec - authenticate - invalid packet +rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\xba\xdb\xba\xdb\xad\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01") +txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1) +try: + rxdec = rxsa.decap(rxsa.decrypt(rx)) + assert(not "This packet shouldn't have been authenticated as correct") +except InvalidTag: + pass + + + += MACsec - decrypt +tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5,\x00\x00\x00\x00\x1fRT\x00\x13\x01V\x00\x01\xf1\xd6\xf7\x03\xf0%\x19\x8e\x88\xb0\xac\xa1\x82\x98\x94]\x85&\xc4U*\x84kX#O\xb6\x8f\xf1\x9d\xc5\xdc\xe0\x80,\x98\x8e_\xd53e\x16b0\xaf\xd9\x9e;A\x8aM\xfe\x16\xf6j\xe6\xea\xb7\x9c\xf3\x9bCc#,\x93\xf7\xc0\xdb\x9a\xd0\x0c\xfd?\xcbd\xe5D\xb7\xc9\xbb\xf5\x93M\xc5\x1d'LR\xed\xf3\xbc\xa0\xdf\x86\xf7\xc2JN\xcd\x19\xc1?\xf7\xd2\xbe\x00\x0f`\xe0\x04\xcfX5\xdc\xe7\xb6\xe6\x82\xc4\xac\xd7\x06\xe31\xbe|\x98l\xc8\xc1#*n+x|\xad\x0b<\xfd\xb8\xceoR\x1e") +rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xef\xf4\xee\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.") +rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=1, send_sci=1) +txdec = txsa.decap(txsa.decrypt(tx)) +rxdec = rxsa.decap(rxsa.decrypt(rx)) +txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00\x80#D@\x00@\x01\x93\xe5\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00E\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc" +rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00\x80\x05\xab\x00\x00@\x01\xf1~\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00M\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc" +assert(raw(txdec) == raw(txref)) +assert(raw(rxdec) == raw(rxref)) + + + += MACsec - decrypt - invalid packet +rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xba\xdb\xad\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.") +rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +try: + rxdec = rxsa.decap(rxsa.decrypt(rx)) + assert(not "This packet shouldn't have been decrypted correctly") +except InvalidTag: + pass + + + += MACsec - decap - non-Ethernet +rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +try: + rxsa.decap(IP()) +except TypeError as e: + assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec") + += MACsec - decap - non-MACsec +rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +try: + rxsa.decap(Ether()/IP()) +except TypeError as e: + assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec") + += MACsec - encap - non-Ethernet +txsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1) +try: + txsa.encap(IP()) +except TypeError as e: + assert(str(e) == "cannot encapsulate packet in MACsec, must be Ethernet") diff --git a/scapy/data.py b/scapy/data.py index 70fb35d186d410791a15d7f48573240352a795d3..435134d225d8e9d5eaf4a569a29e35fbe2399847 100644 --- a/scapy/data.py +++ b/scapy/data.py @@ -31,6 +31,7 @@ ETH_P_ALL = 3 ETH_P_IP = 0x800 ETH_P_ARP = 0x806 ETH_P_IPV6 = 0x86dd +ETH_P_MACSEC = 0x88e5 # From net/if_arp.h ARPHDR_ETHER = 1 diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py index 3aa148adcdcb28f00ecff0742253caabc355e32f..d461c0419eaeed00962fc2aa9644c42855096db8 100644 --- a/scapy/layers/l2.py +++ b/scapy/layers/l2.py @@ -143,6 +143,7 @@ class ARPSourceMACField(SourceMACField): ### Layers ETHER_TYPES['802_AD'] = 0x88a8 +ETHER_TYPES['802_1AE'] = ETH_P_MACSEC class Ether(Packet): name = "Ethernet" @@ -410,6 +411,7 @@ class Loopback(Packet): class Dot1AD(Dot1Q): name = '802_1AD' + bind_layers( Dot3, LLC, ) bind_layers( Ether, LLC, type=122) bind_layers( Ether, LLC, type=34928)