def _issue_revocation(self, if_id, chain): """ Store a RevocationObject in ZK and send a revocation to all ERs. :param if_id: The interface that needs to be revoked. :type if_id: int :param chain: The hash chain corresponding to if_id. :type chain: :class:`lib.crypto.hash_chain.HashChain` """ # Only the master BS issues revocations. if not self.zk.have_lock(): return rev_info = RevocationInfo.from_values(chain.next_element()) logging.info("Storing revocation in ZK.") rev_obj = RevocationObject.from_values(if_id, chain.current_index(), chain.next_element()) entry_name = "%s:%s" % (chain.start_element(hex_=True), chain.next_element(hex_=True)) self.revobjs_cache.store(entry_name, rev_obj.pack()) logging.info("Issuing revocation for IF %d.", if_id) # Issue revocation to all ERs. info = IFStateInfo.from_values(if_id, False, chain.next_element()) pld = IFStatePayload.from_values([info]) state_pkt = self._build_packet() for er in self.topology.get_all_edge_routers(): state_pkt.addrs.dst.host = er.addr state_pkt.set_payload(pld.copy()) self.send(state_pkt, er.addr) self._process_revocation(rev_info, if_id)
def process_revocation(self, spkt, from_local_as): pld = spkt.get_payload() logging.info("Processing revocation: %s", pld.info) # First, forward the packet as appropriate. self.handle_data(spkt, from_local_as) if from_local_as: return # Forward to local path and beacon services if we haven't recently. rev_info = RevocationInfo.from_raw(pld.info.rev_info) if rev_info in self.revocations: return snames = [] # Fork revocation to local BS and PS if router is downstream of the # failed interface. if (spkt.addrs.src.isd_as[0] == self.addr.isd_as[0] and self._is_downstream_router()): snames.append(BEACON_SERVICE) if self.topology.path_servers: snames.append(PATH_SERVICE) # Fork revocation to local PS if router is in the AS of the source. elif (spkt.addrs.dst.isd_as == self.addr.isd_as and self.topology.path_servers): snames.append(PATH_SERVICE) self.revocations[rev_info] = True for sname in snames: try: addr, port = self.dns_query_topo(sname)[0] except SCIONServiceLookupError: logging.error("Unable to find %s to forward revocation to.", sname) continue pkt = self._build_packet(addr, dst_port=port, payload=rev_info.copy()) self.send(pkt, addr, SCION_UDP_EH_DATA_PORT)
def _parse(self, raw): """ Parses raw bytes and populates the fields. """ data = Raw(raw, self.NAME) self.if_id, self.hash_chain_idx = struct.unpack("!II", data.pop(8)) self.rev_info = RevocationInfo.from_raw(data.pop())
def get_proof(self, if_id, epoch, prev_root, next_root): """ Obtain the proof for revoking a given interface at a given epoch. :param int if_id: ID of the interface to be revoked. :param int epoch: epoch for which the interface is to be revoked. :param bytes prev_root: hash of the previous root. :param bytes next_root: hash of the next root. """ assert if_id in self._if2idx.keys(), "if_id not found in AS" relative_epoch = epoch % HASHTREE_N_EPOCHS # Obtain the nonce for the (if_id, epoch) pair using the seed. raw_nonce = self._seed + struct.pack("!qq", if_id, relative_epoch) nonce = self._hash_func(raw_nonce) # Obtain the sibling hashes along with their left/right position info. siblings = [] idx = self._if2idx[if_id] + relative_epoch while idx > 0: if idx % 2 == 0: siblings.append((True, self._nodes[idx - 1])) else: siblings.append((False, self._nodes[idx + 1])) idx = (idx - 1) // 2 # Using the above fields, construct a RevInfo capnp as the proof. return RevocationInfo.from_values( self._isd_as, if_id, epoch, nonce, siblings, prev_root, next_root, self._hash_type)
def _handle_scmp_revocation(self, pld, meta): rev_info = RevocationInfo.from_raw(pld.info.rev_info) try: rev_info.validate() except SCIONBaseError as e: logging.warning("Failed to validate SCMP RevInfo from %s: %s\n%s", meta, e, rev_info.short_desc()) return self._handle_revocation(CtrlPayload(PathMgmt(rev_info)), meta)
def test_revoked(self): pcb = self._mk_pcb() inst = Mock() rev_info = RevocationInfo.from_values(ISD_AS("1-ff00:0:300"), 1, LinkType.PARENT, 1) srev_info = SignedRevInfo.from_values(rev_info.copy().pack(), ProtoSignType.ED25519, "src".encode()) rev_cache = Mock() rev_cache.get.return_value = srev_info inst.check_revoked_interface = SCIONElement.check_revoked_interface ntools.eq_(inst.check_revoked_interface(inst, pcb, rev_cache), False)
def _rev_entries_handler(self, raw_entries): for raw in raw_entries: rev_info = RevocationInfo.from_raw(raw) try: rev_info.validate() except SCIONBaseError as e: logging.warning("Failed to validate RevInfo from zk: %s\n%s", e, rev_info.short_desc()) continue self._remove_revoked_segments(rev_info)
def _handle_scmp_revocation(self, pld, meta): rev_info = RevocationInfo.from_raw(pld.info.rev_info) logging.debug("Received revocation via SCMP: %s (from %s)", rev_info.short_desc(), meta) try: rev_info.validate() except SCIONBaseError as e: logging.warning("Failed to validate SCMP RevInfo from %s: %s\n%s", meta, e, rev_info.short_desc()) return self._process_revocation(rev_info)
def process_rev_objects(self, rev_infos): """ Processes revocation infos stored in Zookeeper. """ with self._rev_seg_lock: for raw in rev_infos: try: rev_info = RevocationInfo.from_raw(raw) except SCIONParseError as e: logging.error( "Error processing revocation info from ZK: %s", e) continue self.local_rev_cache[rev_info] = rev_info.copy()
def _handle_scmp(self, spkt): scmp_hdr = spkt.l4_hdr spkt.parse_payload() if (scmp_hdr.class_ == SCMPClass.PATH and scmp_hdr.type == SCMPPathClass.REVOKED_IF): scmp_pld = spkt.get_payload() rev_info = RevocationInfo.from_raw(scmp_pld.info.rev_info) logging.info("Received revocation for IF %d." % rev_info.p.ifID) self.sd.handle_revocation(rev_info, None) return ResponseRV.RETRY else: logging.error("Received SCMP error:\n%s", spkt) return ResponseRV.FAILURE
def from_values(cls, if_id, index, rev_token): """ Returns a RevocationInfo object with the specified values. :param int if_id: The interface id of the corresponding interface. :param int index: The index of the rev_token in the hash chain. :param bytes rev_token: revocation token of interface """ inst = cls() inst.if_id = if_id inst.hash_chain_idx = index inst.rev_info = RevocationInfo.from_values(rev_token) return inst
def _handle_scmp(self, spkt): scmp_hdr = spkt.l4_hdr spkt.parse_payload() if (scmp_hdr.class_ == SCMPClass.PATH and scmp_hdr.type == SCMPPathClass.REVOKED_IF): scmp_pld = spkt.get_payload() rev_info = RevocationInfo.from_raw(scmp_pld.info.rev_info) logging.info("Received revocation for IF %d." % rev_info.p.ifID) rev_not = SCIONDRevNotification.from_values(rev_info) self.api_socket().send(rev_not.pack_full()) return ResponseRV.RETRY else: logging.error("Received SCMP error:\n%s", spkt) return ResponseRV.FAILURE
def _handle_scmp(self, spkt): scmp_hdr = spkt.l4_hdr spkt.parse_payload() if (scmp_hdr.class_ == SCMPClass.PATH and scmp_hdr.type == SCMPPathClass.REVOKED_IF): scmp_pld = spkt.get_payload() rev_info = RevocationInfo.from_raw(scmp_pld.info.rev_info) logging.info("Received revocation: %s (from %s)", rev_info.short_desc(), spkt.addrs.src) lib_sciond.send_rev_notification( rev_info, connector=self._connector) return ResponseRV.RETRY else: logging.error("Received SCMP error:\n%s", spkt) return ResponseRV.FAILURE
def _issue_revocations(self, revoked_ifs): """ Store a RevocationInfo in ZK and send a revocation to all BRs. :param list revoked_ifs: A list of interfaces that needs to be revoked. """ # Only the master BS issues revocations. if not self.zk.have_lock(): return # Process revoked interfaces. infos = [] for if_id in revoked_ifs: br = self.ifid2br[if_id] rev_info = RevocationInfo.from_values( self.addr.isd_as, if_id, br.interfaces[if_id].link_type, int(time.time()), self.REVOCATION_TTL) logging.info("Issuing revocation: %s", rev_info.short_desc()) if self._labels: REVOCATIONS_ISSUED.labels(**self._labels).inc() chain = self._get_my_cert() _, cert_ver = chain.get_leaf_isd_as_ver() src = DefaultSignSrc.from_values( rev_info.isd_as(), cert_ver, self._get_my_trc().version).pack() srev_info = SignedRevInfo.from_values(rev_info.copy().pack(), ProtoSignType.ED25519, src) srev_info.sign(self.signing_key) # Add to revocation cache self.if_revocations[if_id] = srev_info self._process_revocation(srev_info) infos.append(IFStateInfo.from_values(if_id, False, srev_info)) border_metas = [] # Add all BRs. for br in self.topology.border_routers: br_addr, br_port = br.int_addrs.public[0] border_metas.append( UDPMetadata.from_values(host=br_addr, port=br_port)) # Add local path server. ps_meta = [] if self.topology.path_servers: try: addr, port = self.dns_query_topo(ServiceType.PS)[0] except SCIONServiceLookupError: addr, port = None, None # Create a meta if there is a local path service if addr: ps_meta.append(UDPMetadata.from_values(host=addr, port=port)) self._send_ifstate_update(infos, border_metas, ps_meta)
def process_rev_objects(self, rev_infos): """ Processes revocation infos stored in Zookeeper. """ with self._rev_seg_lock: for raw in rev_infos: try: rev_info = RevocationInfo.from_raw(raw) except SCIONParseError as e: logging.error("Error parsing revocation info from ZK: %s", e) continue try: rev_info.validate() except SCIONBaseError as e: logging.warning( "Failed to validate RevInfo from zk: %s\n%s", e, rev_info.short_desc()) continue self.local_rev_cache[rev_info] = rev_info.copy()
def rev_info(self, idx): return RevocationInfo(self.p.exts.revInfos[idx])
def _handle_scmp_revocation(self, pld, meta): rev_info = RevocationInfo.from_raw(pld.info.rev_info) self._handle_revocation(rev_info, meta)
def _rev_entries_handler(self, raw_entries): for raw in raw_entries: rev_info = RevocationInfo.from_raw(raw) self._remove_revoked_segments(rev_info)
def _handle_scmp_revocation(self, pld, meta): rev_info = RevocationInfo.from_raw(pld.info.rev_info) logging.debug("Received revocation via SCMP: %s (from %s)", rev_info.short_desc(), meta) self._process_revocation(rev_info)
def rev_info(self): if not self._rev_info: self._rev_info = RevocationInfo(self.p.revInfo) return self._rev_info
def rev_info(self): if self.p.revInfo: return RevocationInfo(self.p.revInfo) return None
def handle_scmp_revocation(self, pld, meta): rev_info = RevocationInfo.from_raw(pld.info.rev_info) self.handle_revocation(CtrlPayload(PathMgmt(rev_info)), meta)