Skip to content

Commit

Permalink
Revocation epoch tolerance window (#1083)
Browse files Browse the repository at this point in the history
* BR uses now a tolerance window when verifying a revocation epoch. Reduce tolerance window to 2s (from 5s).
* Include hash function type in revinfo proto.
* Changed epoch in revinfos to be relative to zero-unix-time (instead of beginning of current ttl window)
* cache only flag is now always set for trc/cert requests from SCIONElem
* CS now replies to requests if it receives the missing piece through ZK
  • Loading branch information
shitz authored May 24, 2017
1 parent 56144a7 commit 17dedbb
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 122 deletions.
2 changes: 1 addition & 1 deletion go/border/rpkt/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (rp *RtrPkt) validateLocalIF(ifid *spath.IntfID) *common.Error {
return nil
}
// Check that we have a revocation for the current epoch.
if revInfo.Epoch() < crypto.GetCurrentHashTreeEpoch() {
if !crypto.VerifyHashTreeEpoch(revInfo.Epoch()) {
// If the BR does not have a revocation for the current epoch, it considers
// the interface as active until it receives a new revocation.
ifstate.Activate(*ifid)
Expand Down
36 changes: 27 additions & 9 deletions go/lib/crypto/htree.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,33 @@ import (
"time"
)

// HashTreeTTL is the TTL of one hash tree (in seconds).
// FIXME(shitz): This should really be matching spath.MaxTTL, but more importantly,
// it needs to match the hash tree ttl used by the BS, which is currently set to 30 mins.
const HashTreeTTL = 30 * 60
const (
// HashTreeTTL is the TTL of one hash tree (in seconds).
// FIXME(shitz): This should really be matching spath.MaxTTL, but more importantly,
// it needs to match the hash tree ttl used by the BS, which is currently set to 30 mins.
HashTreeTTL = 30 * 60 * time.Second

// HashTreeEpochTime is the duration of one epoch (in seconds).
const HashTreeEpochTime = 10
// HashTreeEpochTime is the duration of one epoch (in seconds).
HashTreeEpochTime = 10 * time.Second

func GetCurrentHashTreeEpoch() uint16 {
window := time.Now().Unix() % HashTreeTTL
return uint16(window / HashTreeEpochTime)
// HashTreeEpochTolerance is the duration after a revocation expired within which a
// revocation is still accepted by a verifier.
HashTreeEpochTolerance = 2 * time.Second
)

// GetCurrentHashTreeEpoch returns the current epoch ID.
func GetCurrentHashTreeEpoch() uint64 {
return uint64(time.Now().Unix() / int64(HashTreeEpochTime.Seconds()))
}

// GetTimeSinceHashTreeEpoch returns the time since the start of epoch.
func GetTimeSinceHashTreeEpoch(epoch uint64) time.Duration {
epochStart := time.Unix(0, int64(epoch)*HashTreeEpochTime.Nanoseconds())
return time.Since(epochStart)
}

// VerifyHashTreeEpoch verifies a given hash tree epoch. An epoch is valid if it is
// equal to the current epoch or within the tolerance limit of the next epoch.
func VerifyHashTreeEpoch(epoch uint64) bool {
return GetTimeSinceHashTreeEpoch(epoch) < (HashTreeEpochTime + HashTreeEpochTolerance)
}
9 changes: 5 additions & 4 deletions infrastructure/beacon_server/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
from lib.thread import thread_safety_net, kill_self
from lib.types import (
CertMgmtType,
HashType,
PathMgmtType as PMT,
PayloadClass,
)
Expand Down Expand Up @@ -165,8 +166,8 @@ def __init__(self, server_id, conf_dir):

def _init_hash_tree(self):
ifs = list(self.ifid2br.keys())
self._hash_tree = ConnectedHashTree(self.addr.isd_as,
ifs, self.hashtree_gen_key)
self._hash_tree = ConnectedHashTree(
self.addr.isd_as, ifs, self.hashtree_gen_key, HashType.SHA256)

def _get_ht_proof(self, if_id):
with self._hash_tree_lock:
Expand Down Expand Up @@ -439,8 +440,8 @@ def _create_next_tree(self):

ht_start = time.time()
ifs = list(self.ifid2br.keys())
tree = ConnectedHashTree.get_next_tree(self.addr.isd_as, ifs,
self.hashtree_gen_key)
tree = ConnectedHashTree.get_next_tree(
self.addr.isd_as, ifs, self.hashtree_gen_key, HashType.SHA256)
ht_end = time.time()
with self._hash_tree_lock:
self._next_tree = tree
Expand Down
88 changes: 50 additions & 38 deletions infrastructure/cert_server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,18 +174,18 @@ def process_cert_chain_request(self, req, meta):
"""Process a certificate chain request."""
assert isinstance(req, CertChainRequest)
key = req.isd_as(), req.p.version
logging.info("Cert chain request received for %sv%s", *key)
logging.info("Cert chain request received for %sv%s from %s", *key, meta)
local = meta.ia == self.addr.isd_as
if not self._check_cc(key) and not local:
logging.warning(
"Dropping CC request from %s for %sv%s: "
"CC not found && requester is not local)",
meta.get_addr(), *key)
return
if req.p.cacheOnly:
self._reply_cc(key, meta)
if not self._check_cc(key):
if not local:
logging.warning(
"Dropping CC request from %s for %sv%s: "
"CC not found && requester is not local)",
meta.get_addr(), *key)
else:
self.cc_requests.put((key, (meta, req)))
return
self.cc_requests.put((key, meta))
self._reply_cc(key, (meta, req))

def process_cert_chain_reply(self, rep, meta, from_zk=False):
"""Process a certificate chain reply."""
Expand All @@ -206,19 +206,26 @@ def _check_cc(self, key):
logging.debug('Cert chain not found for %sv%s', *key)
return False

def _fetch_cc(self, key, _):
def _fetch_cc(self, key, req_info):
# Do not attempt to fetch the CertChain from a remote AS if the cacheOnly flag is set.
_, orig_req = req_info
if orig_req.p.cacheOnly:
return
isd_as, ver = key
req = CertChainRequest.from_values(isd_as, ver, cache_only=True)
path = self._get_path_via_api(isd_as)
meta = self._build_meta(isd_as, host=SVCType.CS_A, path=path)
if path and self.send_meta(req, meta):
logging.info("Cert chain request sent: %s", req.short_desc())
path_meta = self._get_path_via_api(isd_as)
if path_meta:
meta = self._build_meta(isd_as, host=SVCType.CS_A, path=path_meta.fwd_path())
self.send_meta(req, meta)
logging.info("Cert chain request sent to %s via [%s]: %s",
meta, path_meta.short_desc(), req.short_desc())
else:
logging.warning("Cert chain request (for %s) not sent: "
"no destination found", req.short_desc())
"no path found", req.short_desc())

def _reply_cc(self, key, meta):
def _reply_cc(self, key, req_info):
isd_as, ver = key
meta = req_info[0]
cert_chain = self.trust_store.get_cert(isd_as, ver)
self.send_meta(CertChainReply.from_values(cert_chain), meta)
logging.info("Cert chain for %sv%s sent to %s:%s", isd_as, ver, meta.get_addr(), meta.port)
Expand All @@ -227,18 +234,18 @@ def process_trc_request(self, req, meta):
"""Process a TRC request."""
assert isinstance(req, TRCRequest)
key = req.isd_as()[0], req.p.version
logging.info("TRC request received for %sv%s", *key)
logging.info("TRC request received for %sv%s from %s", *key, meta)
local = meta.ia == self.addr.isd_as
if not self._check_trc(key) and not local:
logging.warning(
"Dropping TRC request from %s for %sv%s: "
"TRC not found && requester is not local)",
meta.get_addr(), *key)
return
if req.p.cacheOnly:
self._reply_trc(key, meta)
if not self._check_trc(key):
if not local:
logging.warning(
"Dropping TRC request from %s for %sv%s: "
"TRC not found && requester is not local)",
meta.get_addr(), *key)
else:
self.trc_requests.put((key, (meta, req)))
return
self.trc_requests.put((key, (meta, req.isd_as()[1]),))
self._reply_trc(key, (meta, req))

def process_trc_reply(self, trc_rep, meta, from_zk=False):
"""
Expand All @@ -264,21 +271,26 @@ def _check_trc(self, key):
logging.debug('TRC not found for %sv%s', *key)
return False

def _fetch_trc(self, key, info):
def _fetch_trc(self, key, req_info):
# Do not attempt to fetch the TRC from a remote AS if the cacheOnly flag is set.
_, orig_req = req_info
if orig_req.p.cacheOnly:
return
isd, ver = key
isd_as = ISD_AS.from_values(isd, info[1])
isd_as = ISD_AS.from_values(isd, orig_req.isd_as()[1])
trc_req = TRCRequest.from_values(isd_as, ver, cache_only=True)
path = self._get_path_via_api(isd_as)
meta = self._build_meta(isd_as, host=SVCType.CS_A, path=path)
if path and self.send_meta(trc_req, meta):
logging.info("TRC request sent for %sv%s.", *key)
path_meta = self._get_path_via_api(isd_as)
if path_meta:
meta = self._build_meta(isd_as, host=SVCType.CS_A, path=path_meta.fwd_path())
self.send_meta(trc_req, meta)
logging.info("TRC request sent to %s via [%s]: %s",
meta, path_meta.short_desc(), trc_req.short_desc())
else:
logging.warning("TRC request not sent for %sv%s: "
"no destination found.", *key)
logging.warning("TRC request not sent for %s: no path found.", trc_req.short_desc())

def _reply_trc(self, key, info):
def _reply_trc(self, key, req_info):
isd, ver = key
meta = info[0]
meta = req_info[0]
trc = self.trust_store.get_trc(isd, ver)
self.send_meta(TRCReply.from_values(trc), meta)
logging.info("TRC for %sv%s sent to %s:%s", isd, ver, meta.get_addr(), meta.port)
Expand All @@ -294,7 +306,7 @@ def _get_path_via_api(self, isd_as, flush=False):
logging.error("Error during path lookup: %s" % e)
continue
if path_entries:
return path_entries[0].path().fwd_path()
return path_entries[0].path()
logging.warning("Unable to get path to %s from local api.", isd_as)
return None

Expand Down
4 changes: 2 additions & 2 deletions infrastructure/path_server/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,8 @@ def _send_waiting_queries(self, dst_isd, pcb):
(seg_req, logger) = targets.pop(0)
meta = self._build_meta(ia=src_ia, path=path, host=SVCType.PS_A, reuse=True)
self.send_meta(seg_req, meta)
logger.info("Waiting request (%s) sent via %s",
seg_req.short_desc(), pcb.short_desc())
logger.info("Waiting request (%s) sent to %s via %s",
seg_req.short_desc(), meta, pcb.short_desc())

def _share_via_zk(self):
if not self._segs_to_zk:
Expand Down
4 changes: 2 additions & 2 deletions infrastructure/scion_elem.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ def request_missing_trcs(self, seg_meta):
continue
self.requested_trcs.add((isd, ver))
isd_as = ISD_AS.from_values(isd, 0)
trc_req = TRCRequest.from_values(isd_as, ver)
trc_req = TRCRequest.from_values(isd_as, ver, cache_only=True)
logging.info("Requesting %sv%s TRC for PCB %s", isd, ver, seg_meta.seg.short_id())
if not seg_meta.meta:
meta = self.get_cs()
Expand All @@ -367,7 +367,7 @@ def request_missing_certs(self, seg_meta):
if (isd_as, ver) in self.requested_certs:
continue
self.requested_certs.add((isd_as, ver))
cert_req = CertChainRequest.from_values(isd_as, ver)
cert_req = CertChainRequest.from_values(isd_as, ver, cache_only=True)
meta = seg_meta.meta
if not meta:
meta = self.get_cs()
Expand Down
Loading

0 comments on commit 17dedbb

Please sign in to comment.