Beispiel #1
0
class PathSegment(SCIONPayloadBaseProto):
    NAME = "PathSegment"
    PAYLOAD_CLASS = PayloadClass.PCB
    P_CLS = P.PathSegment
    VER = len(P_CLS.schema.fields) - 1

    def __init__(self, p):  # pragma: no cover
        super().__init__(p)
        self._min_exp = float("inf")
        self._setup()

    def _setup(self):
        self.info = InfoOpaqueField(self.p.info)
        self._calc_min_exp()
        self.sibra_ext = None
        if self.is_sibra():
            self.sibra_ext = SibraPCBExt(self.p.exts.sibra)

    @classmethod
    def from_values(cls, info, sibra_ext=None):  # pragma: no cover
        p = cls.P_CLS.new_message(info=info.pack())
        if sibra_ext:
            p.exts.sibra = sibra_ext.p
        return cls(p)

    def _calc_min_exp(self):
        # NB: only the expiration time of the first pcbm is considered.
        for asm in self.iter_asms():
            self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time)

    def asm(self, idx):  # pragma: no cover
        return ASMarking(self.p.asms[idx])

    def iter_asms(self, start=0):  # pragma: no cover
        for i in range(start, len(self.p.asms)):
            yield self.asm(i)

    def is_sibra(self):  # pragma: no cover
        return bool(self.p.exts.sibra.id)

    def sig_pack3(self):
        """
        Pack for signing version 3 (defined by highest field number).
        """
        if self.VER != 3:
            raise SCIONSigVerError("PathSegment.sig_pack3 cannot support version %s", self.VER)
        b = []
        b.append(self.p.info)
        # ifID field is changed on the fly, and so is ignored.
        for asm in self.iter_asms():
            b.append(asm.sig_pack8())
        if self.is_sibra():
            b.append(self.sibra_ext.sig_pack3())
        return b"".join(b)

    def sign(self, key, set_=True):  # pragma: no cover
        sig = sign(self.sig_pack3(), key)
        if set_:
            self.p.asms[-1].sig = sig
        return sig

    def add_asm(self, asm):  # pragma: no cover
        """
        Appends a new ASMarking block.
        """
        d = self.p.to_dict()
        d.setdefault('asms', []).append(asm.p)
        self.p.from_dict(d)
        self._update_info()
        self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time)

    def _update_info(self):  # pragma: no cover
        self.info.hops = len(self.p.asms)
        self.p.info = self.info.pack()

    def add_sibra_ext(self, ext_p):  # pragma: no cover
        self.p.exts.sibra = ext_p.copy()
        self.sibra_ext = SibraPCBExt(self.p.exts.sibra)

    def get_trcs_certs(self):
        """
        Returns a dict of all trcs' versions and a dict of all certificates'
        versions used in this PCB.
        """
        trcs = defaultdict(set)
        certs = defaultdict(set)
        for asm in self.iter_asms():
            isd_as = asm.isd_as()
            isd = isd_as[0]
            trcs[isd].add(asm.p.trcVer)
            certs[isd_as].add(asm.p.certVer)
        return trcs, certs

    def get_path(self, reverse_direction=False):
        """
        Returns the list of HopOpaqueFields in the path.
        """
        hofs = []
        info = InfoOpaqueField(self.p.info)
        asms = list(self.iter_asms())
        if reverse_direction:
            asms = reversed(asms)
            info.up_flag ^= True
        for asm in asms:
            hofs.append(asm.pcbm(0).hof())
        return SCIONPath.from_values(info, hofs)

    def first_ia(self):  # pragma: no cover
        return self.asm(0).isd_as()

    def last_ia(self):  # pragma: no cover
        return self.asm(-1).isd_as()

    def last_hof(self):  # pragma: no cover
        if self.p.asms:
            return self.asm(-1).pcbm(0).hof()
        return None

    def get_hops_hash(self, hex=False):  # pragma: no cover
        """
        Returns the hash over all triples (ISD_AS, IG_IF, EG_IF) included in
        the path segment.
        """
        data = []
        for asm in self.iter_asms():
            pcbm = asm.pcbm(0)
            data.append(asm.isd_as().pack())
            data.append(struct.pack("!QQ", pcbm.p.inIF, pcbm.p.outIF))
        data = b"".join(data)
        if hex:
            return crypto_hash(data).hex()
        return crypto_hash(data)

    def get_n_peer_links(self):  # pragma: no cover
        """Return the total number of peer links in the PathSegment."""
        n = 0
        for asm in self.p.asms:
            n += len(asm.pcbms) - 1
        return n

    def get_n_hops(self):  # pragma: no cover
        """Return the number of hops in the PathSegment."""
        return len(self.p.asms)

    def get_timestamp(self):  # pragma: no cover
        """Returns the creation timestamp of this PathSegment."""
        return self.info.timestamp

    def set_timestamp(self, timestamp):  # pragma: no cover
        """Updates the timestamp in the IOF."""
        assert timestamp < 2 ** 32 - 1
        self.info.timestamp = timestamp
        self._update_info()

    def get_expiration_time(self):  # pragma: no cover
        """
        Returns the expiration time of the path segment in real time. If a PCB
        extension in the last ASMarking supplies an expiration time, use that.
        Otherwise fall-back to the standard expiration time calculation.
        """
        if self.is_sibra():
            return self.sibra_ext.exp_ts()
        return self.info.timestamp + int(self._min_exp * EXP_TIME_UNIT)

    def flags(self):  # pragma: no cover
        f = 0
        if self.is_sibra():
            f |= PSF.SIBRA
        return f

    def short_id(self):  # pragma: no cover
        """
        Return a 12-byte hex ID identifying the PCB (mostly for logging purposes).
        """
        return self.get_hops_hash(hex=True)[:12]

    def short_desc(self):  # pragma: no cover
        """
        Return a short description string of the PathSegment, consisting of a
        truncated hash, the IOF timestamp, and the list of hops.
        """
        desc = []
        desc.append("%s, %s, " % (self.short_id(), iso_timestamp(self.get_timestamp())))
        hops = []
        for asm in self.iter_asms():
            hop = []
            hof = asm.pcbm(0).hof()
            if hof.ingress_if:
                hop.append("%d " % hof.ingress_if)
            hop.append("%s" % asm.isd_as())
            if hof.egress_if:
                hop.append(" %d" % hof.egress_if)
            hops.append("".join(hop))
        exts = []
        if self.is_sibra():
            exts.append("  %s" % self.sibra_ext.short_desc())
        desc.append(">".join(hops))
        if exts:
            return "%s\n%s" % ("".join(desc), "\n".join(exts))
        return "".join(desc)

    def __str__(self):
        s = []
        s.append("%s:" % self.NAME)
        s.append("  %s" % self.info)
        for asm in self.iter_asms():
            for line in asm.short_desc().splitlines():
                s.append("  %s" % line)
        if self.sibra_ext:
            for line in str(self.sibra_ext).splitlines():
                s.append("  %s" % line)
        return "\n".join(s)

    def __eq__(self, other):
        return self.__hash__() == hash(other)

    def __hash__(self):  # pragma: no cover
        return hash(self.get_hops_hash())  # FIMXE(PSz): should add timestamp?
Beispiel #2
0
class PathSegment(SCIONPayloadBaseProto):
    NAME = "PathSegment"
    PAYLOAD_CLASS = PayloadClass.PCB
    P_CLS = P.PathSegment

    def __init__(self, p):  # pragma: no cover
        super().__init__(p)
        self._min_exp = float("inf")
        self._setup()

    def _setup(self):
        self.info = InfoOpaqueField(self.p.info)
        self._calc_min_exp()
        self.sibra_ext = None
        if self.is_sibra():
            self.sibra_ext = SibraPCBExt(self.p.exts.sibra)

    @classmethod
    def from_values(cls, info, rev_infos=None,
                    sibra_ext=None):  # pragma: no cover
        p = cls.P_CLS.new_message(info=info.pack())
        if sibra_ext:
            p.exts.sibra = sibra_ext.p
        if rev_infos:
            p.exts.init("revInfos", len(rev_infos))
            for i, info in enumerate(rev_infos):
                p.exts.revInfos[i] = info.copy()
        return cls(p)

    def _calc_min_exp(self):
        # NB: only the expiration time of the first pcbm is considered.
        for asm in self.iter_asms():
            self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time)

    def asm(self, idx):  # pragma: no cover
        return ASMarking(self.p.asms[idx])

    def iter_asms(self, start=0):  # pragma: no cover
        for i in range(start, len(self.p.asms)):
            yield self.asm(i)

    def is_sibra(self):  # pragma: no cover
        return bool(self.p.exts.sibra.id)

    def sig_pack(self, ver=3):
        b = []
        if ver >= 3:
            b.append(self.p.info)
            # ifID field is changed on the fly, and so is ignored.
            for asm in self.iter_asms():
                b.append(asm.sig_pack(9))
            if self.is_sibra():
                b.append(self.sibra_ext.sig_pack(2))
        return b"".join(b)

    def sign(self, key, set_=True):  # pragma: no cover
        assert not self.p.asms[-1].sig
        sig = sign(self.sig_pack(3), key)
        if set_:
            self.p.asms[-1].sig = sig
        return sig

    def add_asm(self, asm):  # pragma: no cover
        """
        Appends a new ASMarking block.
        """
        d = self.p.to_dict()
        d.setdefault('asms', []).append(asm.p)
        self.p.from_dict(d)
        self._update_info()
        self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time)

    def _update_info(self):  # pragma: no cover
        self.info.hops = len(self.p.asms)
        self.p.info = self.info.pack()

    def add_sibra_ext(self, ext_p):  # pragma: no cover
        self.p.exts.sibra = ext_p.copy()
        self.sibra_ext = SibraPCBExt(self.p.exts.sibra)

    def add_rev_infos(self, rev_infos):  # pragma: no cover
        """
        Appends a list of revocations to the PCB. Replaces existing
        revocations with newer ones.
        """
        if not rev_infos:
            return
        existing = {}
        current_epoch = ConnectedHashTree.get_current_epoch()
        for i in range(len(self.p.exts.revInfos)):
            orphan = self.p.exts.revInfos.disown(i)
            info_p = orphan.get()
            if info_p.epoch >= current_epoch:
                existing[(info_p.isdas, info_p.ifID)] = orphan
        # Remove revocations for which we already have a newer one.
        filtered = []
        for info in rev_infos:
            if (info.p.epoch >= current_epoch and
                    (info.p.isdas, info.p.ifID) not in existing):
                filtered.append(info)
        self.p.exts.init("revInfos", len(existing) + len(filtered))
        for i, orphan in enumerate(existing.values()):
            self.p.exts.revInfos.adopt(i, orphan)
        n_existing = len(existing)
        for i, info in enumerate(filtered):
            self.p.exts.revInfos[n_existing + i] = info.p

    def remove_crypto(self):  # pragma: no cover
        """
        Removes the signatures and certificates from each AS block.
        """
        for asm in self.iter_asms():
            asm.remove_sig()
            asm.remove_chain()

    def get_path(self, reverse_direction=False):
        """
        Returns the list of HopOpaqueFields in the path.
        """
        hofs = []
        info = InfoOpaqueField(self.p.info)
        asms = list(self.iter_asms())
        if reverse_direction:
            asms = reversed(asms)
            info.up_flag ^= True
        for asm in asms:
            hofs.append(asm.pcbm(0).hof())
        return SCIONPath.from_values(info, hofs)

    def first_ia(self):  # pragma: no cover
        return self.asm(0).isd_as()

    def last_ia(self):  # pragma: no cover
        return self.asm(-1).isd_as()

    def last_hof(self):  # pragma: no cover
        if self.p.asms:
            return self.asm(-1).pcbm(0).hof()
        return None

    def get_hops_hash(self, hex=False):  # pragma: no cover
        """
        Returns the hash over all triples (ISD_AS, IG_IF, EG_IF) included in
        the path segment.
        """
        h = SHA256.new()
        for asm in self.iter_asms():
            pcbm = asm.pcbm(0)
            h.update(asm.isd_as().pack() +
                     struct.pack("!QQ", pcbm.p.inIF, pcbm.p.outIF))
        if hex:
            return h.hexdigest()
        return h.digest()

    def get_n_peer_links(self):  # pragma: no cover
        """Return the total number of peer links in the PathSegment."""
        n = 0
        for asm in self.p.asms:
            n += len(asm.pcbms) - 1
        return n

    def get_n_hops(self):  # pragma: no cover
        """Return the number of hops in the PathSegment."""
        return len(self.p.asms)

    def get_timestamp(self):  # pragma: no cover
        """Returns the creation timestamp of this PathSegment."""
        return self.info.timestamp

    def set_timestamp(self, timestamp):  # pragma: no cover
        """Updates the timestamp in the IOF."""
        assert timestamp < 2 ** 32 - 1
        self.info.timestamp = timestamp
        self._update_info()

    def get_expiration_time(self):  # pragma: no cover
        """
        Returns the expiration time of the path segment in real time. If a PCB
        extension in the last ASMarking supplies an expiration time, use that.
        Otherwise fall-back to the standard expiration time calculation.
        """
        if self.is_sibra():
            return self.sibra_ext.exp_ts()
        return self.info.timestamp + int(self._min_exp * EXP_TIME_UNIT)

    def flags(self):  # pragma: no cover
        f = 0
        if self.is_sibra():
            f |= PSF.SIBRA
        return f

    def rev_info(self, idx):
        return RevocationInfo(self.p.exts.revInfos[idx])

    def iter_rev_infos(self, start=0):
        for i in range(start, len(self.p.exts.revInfos)):
            yield self.rev_info(i)

    def get_rev_map(self):
        """
        Returns a dict (ISD_AS, IF) -> RevocationInfo, if there are any
        revocations in the PCB extensions, otherwise an empty dict.
        """
        result = {}
        for rev_info in self.iter_rev_infos():
            key = (rev_info.isd_as(), rev_info.p.ifID)
            result[key] = rev_info

        return result

    def short_desc(self):  # pragma: no cover
        """
        Return a short description string of the PathSegment, consisting of a
        truncated hash, the IOF timestamp, and the list of hops.
        """
        desc = []
        desc.append("%s, %s, " % (
            self.get_hops_hash(hex=True)[:12],
            iso_timestamp(self.get_timestamp()),
        ))
        hops = []
        for asm in self.iter_asms():
            hops.append(str(asm.isd_as()))
        exts = []
        if self.is_sibra():
            exts.append("  %s" % self.sibra_ext.short_desc())
        for rev_info in self.iter_rev_infos():
            exts.append("  %s" % rev_info.short_desc())
        desc.append(" > ".join(hops))
        if exts:
            return "%s\n%s" % ("".join(desc), "\n".join(exts))
        return "".join(desc)

    def __str__(self):
        s = []
        s.append("%s:" % self.NAME)
        s.append("  %s" % self.info)
        for asm in self.iter_asms():
            for line in asm.short_desc().splitlines():
                s.append("  %s" % line)
        if self.sibra_ext:
            for line in str(self.sibra_ext).splitlines():
                s.append("  %s" % line)
        for rev_info in self.iter_rev_infos():
            for line in rev_info.short_desc().splitlines():
                s.append("  %s" % line)
        return "\n".join(s)

    def __hash__(self):  # pragma: no cover
        return hash(self.get_hops_hash())  # FIMXE(PSz): should add timestamp?
Beispiel #3
0
class PathSegment(SCIONPayloadBaseProto):
    NAME = "PathSegment"
    PAYLOAD_CLASS = PayloadClass.PCB
    PAYLOAD_TYPE = PCBType.SEGMENT
    P_CLS = P.PathSegment

    def __init__(self, p):  # pragma: no cover
        super().__init__(p)
        self._min_exp = float("inf")
        self._setup()

    def _setup(self):
        self.info = InfoOpaqueField(self.p.info)
        self._calc_min_exp()
        self.sibra_ext = None
        if self.is_sibra():
            self.sibra_ext = SibraPCBExt(self.p.exts.sibra)

    @classmethod
    def from_values(cls, info, sibra_ext=None):  # pragma: no cover
        p = cls.P_CLS.new_message(info=info.pack())
        if sibra_ext:
            p.exts.sibra = sibra_ext.p
        return cls(p)

    def _calc_min_exp(self):
        # NB: only the expiration time of the first pcbm is considered.
        for asm in self.iter_asms():
            self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time)

    def asm(self, idx):  # pragma: no cover
        return ASMarking(self.p.asms[idx])

    def iter_asms(self, start=0):  # pragma: no cover
        for i in range(start, len(self.p.asms)):
            yield self.asm(i)

    def is_sibra(self):  # pragma: no cover
        return bool(self.p.exts.sibra.id)

    def sig_pack(self, ver=3):
        b = []
        if ver >= 3:
            b.append(self.p.info)
            # ifID field is changed on the fly, and so is ignored.
            for asm in self.iter_asms():
                b.append(asm.sig_pack(9))
            if self.is_sibra():
                b.append(self.sibra_ext.sig_pack(2))
        return b"".join(b)

    def sign(self, key, set_=True):  # pragma: no cover
        assert not self.p.asms[-1].sig
        sig = sign(self.sig_pack(3), key)
        if set_:
            self.p.asms[-1].sig = sig
        return sig

    def add_asm(self, asm):  # pragma: no cover
        """
        Appends a new ASMarking block.
        """
        d = self.p.to_dict()
        d.setdefault('asms', []).append(asm.p)
        self.p.from_dict(d)
        self._update_info()
        self._min_exp = min(self._min_exp, asm.pcbm(0).hof().exp_time)

    def _update_info(self):  # pragma: no cover
        self.info.hops = len(self.p.asms)
        self.p.info = self.info.pack()

    def add_sibra_ext(self, ext_p):  # pragma: no cover
        self.p.exts.sibra = ext_p.copy()
        self.sibra_ext = SibraPCBExt(self.p.exts.sibra)

    def remove_crypto(self):  # pragma: no cover
        """
        Remover the signatures and certificates from each AS block.
        """
        for asm in self.iter_asms():
            asm.remove_sig()
            asm.remove_chain()

    def get_path(self, reverse_direction=False):
        """
        Returns the list of HopOpaqueFields in the path.
        """
        hofs = []
        info = InfoOpaqueField(self.p.info)
        asms = list(self.iter_asms())
        if reverse_direction:
            asms = reversed(asms)
            info.up_flag ^= True
        for asm in asms:
            hofs.append(asm.pcbm(0).hof())
        return SCIONPath.from_values(info, hofs)

    def first_ia(self):  # pragma: no cover
        return self.asm(0).isd_as()

    def last_ia(self):  # pragma: no cover
        return self.asm(-1).isd_as()

    def last_hof(self):  # pragma: no cover
        if self.p.asms:
            return self.asm(-1).pcbm(0).hof()
        return None

    def get_hops_hash(self, hex=False):
        """
        Returns the hash over all the interface revocation tokens included in
        the path segment.
        """
        h = SHA256.new()
        for token in self.get_all_iftokens():
            h.update(token)
        if hex:
            return h.hexdigest()
        return h.digest()

    def get_n_peer_links(self):  # pragma: no cover
        """Return the total number of peer links in the PathSegment."""
        n = 0
        for asm in self.p.asms:
            n += len(asm.pcbms) - 1
        return n

    def get_n_hops(self):  # pragma: no cover
        """Return the number of hops in the PathSegment."""
        return len(self.p.asms)

    def get_timestamp(self):  # pragma: no cover
        """Returns the creation timestamp of this PathSegment."""
        return self.info.timestamp

    def set_timestamp(self, timestamp):  # pragma: no cover
        """Updates the timestamp in the IOF."""
        assert timestamp < 2**32 - 1
        self.info.timestamp = timestamp
        self._update_info()

    def get_expiration_time(self):  # pragma: no cover
        """
        Returns the expiration time of the path segment in real time. If a PCB
        extension in the last ASMarking supplies an expiration time, use that.
        Otherwise fall-back to the standard expiration time calculation.
        """
        if self.is_sibra():
            return self.sibra_ext.exp_ts()
        return self.info.timestamp + int(self._min_exp * EXP_TIME_UNIT)

    def get_all_iftokens(self):
        """
        Returns all interface revocation tokens included in the path segment.
        """
        tokens = []
        for asm in self.p.asms:
            for pcbm in asm.pcbms:
                tokens.append(pcbm.igRevToken)
            tokens.append(asm.egRevToken)
        return tokens

    def flags(self):  # pragma: no cover
        f = 0
        if self.is_sibra():
            f |= PSF.SIBRA
        return f

    def short_desc(self):  # pragma: no cover
        """
        Return a short description string of the PathSegment, consisting of a
        truncated hash, the IOF timestamp, and the list of hops.
        """
        desc = []
        desc.append("%s, %s, " % (
            self.get_hops_hash(hex=True)[:12],
            iso_timestamp(self.get_timestamp()),
        ))
        hops = []
        for asm in self.iter_asms():
            hops.append(str(asm.isd_as()))
        exts = []
        if self.is_sibra():
            exts.append("  %s" % self.sibra_ext.short_desc())
        desc.append(" > ".join(hops))
        if exts:
            return "%s\n%s" % ("".join(desc), "\n".join(exts))
        return "".join(desc)

    def __str__(self):
        s = []
        s.append("%s:" % self.NAME)
        s.append("  %s" % self.info)
        for asm in self.iter_asms():
            for line in asm.short_desc().splitlines():
                s.append("  %s" % line)
        if self.sibra_ext:
            for line in str(self.sibra_ext).splitlines():
                s.append("  %s" % line)
        return "\n".join(s)

    def __hash__(self):  # pragma: no cover
        return hash(self.get_hops_hash())  # FIMXE(PSz): should add timestamp?