aboutsummaryrefslogtreecommitdiff
path: root/rp
diff options
context:
space:
mode:
Diffstat (limited to 'rp')
-rwxr-xr-xrp/rcynic/rcynicng221
1 files changed, 139 insertions, 82 deletions
diff --git a/rp/rcynic/rcynicng b/rp/rcynic/rcynicng
index b9d23380..61d83dc8 100755
--- a/rp/rcynic/rcynicng
+++ b/rp/rcynic/rcynicng
@@ -21,40 +21,79 @@ import rpki.POW
from lxml.etree import ElementTree, Element, SubElement, Comment
-args = None
+class Status(object):
+ """
+ Validation status database, like validation_status_t in rcynic:tos.
+ """
+
+ db = dict()
+
+ def __init__(self, uri, generation = None):
+ assert generation in ("current", "backup", None)
+ self.uri = uri
+ self.generation = generation
+ self.timestamp = None
+ self.status = set()
+
+ def __str__(self):
+ return "{time} {self.uri} {status} {self.generation}".format(
+ self = self,
+ time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(self.timestamp)),
+ status = ",".join(s.name for s in sorted(self.status)))
+
+ @classmethod
+ def update(cls, uri, generation = None):
+ try:
+ key = (uri, generation)
+ self = cls.db[key]
+ except KeyError:
+ self = cls.db[key] = cls(uri, generation)
+ self.timestamp = time.time()
+ return self.status
+
-def check_dir(s):
- if not os.path.isdir(s):
- raise argparse.ArgumentTypeError("%r is not a directory" % s)
- return s
+def parse_arguments():
+
+ def check_dir(s):
+ if not os.path.isdir(s):
+ raise argparse.ArgumentTypeError("%r is not a directory" % s)
+ return s
-def parse_options():
- global args # pylint: disable=W0603
parser = argparse.ArgumentParser(description = __doc__)
parser.add_argument("--unauthenticated", type = check_dir, default = "rcynic-data/unauthenticated")
parser.add_argument("--old-authenticated", type = check_dir, default = "rcynic-data/authenticated.old")
parser.add_argument("--tals", type = check_dir, default = "sample-trust-anchors")
parser.add_argument("--output", default = "rcynic-data/rcynicng-output")
- args = parser.parse_args()
+ return parser.parse_args()
def read_tals():
for root, dirs, files in os.walk(args.tals):
for fn in files:
if fn.endswith(".tal"):
- with open(os.path.join(root, fn), "r") as f:
- lines = f.readlines()
- uri = lines.pop(0).strip()
- b64 = "".join(lines[lines.index("\n"):])
- key = rpki.POW.Asymmetric.derReadPublic(b64.decode("base64"))
- yield uri, key
-
-def uri_to_fn(uri, base = None):
+ furi = "file://" + os.path.abspath(os.path.join(root, fn))
+ try:
+ with open(os.path.join(root, fn), "r") as f:
+ lines = f.readlines()
+ uri = lines.pop(0).strip()
+ b64 = "".join(lines[lines.index("\n"):])
+ key = rpki.POW.Asymmetric.derReadPublic(b64.decode("base64"))
+ if not uri.endswith(".cer"):
+ Status.update(furi).add(rpki.POW.validation_status.MALFORMED_TAL_URI)
+ yield uri, key
+ except:
+ Status.update(furi).add(rpki.POW.validation_status.UNREADABLE_TRUST_ANCHOR_LOCATOR)
+
+
+def uri_to_filename(uri, base = None):
fn = uri[uri.index("://")+3:]
if base is not None:
fn = os.path.join(base, fn)
return fn
+def uri_to_basename(uri):
+ return uri.rpartition("/")[2]
+
def first_uri(uris, scheme):
for uri in uris:
if uri.startswith(scheme):
@@ -64,89 +103,107 @@ def first_uri(uris, scheme):
def first_rsync_uri(uris):
return first_uri(uris, "rsync://")
+def sha256(bytes):
+ d = rpki.POW.Digest(rpki.POW.SHA256_DIGEST)
+ d.update(bytes)
+ return d.digest()
-def walk_tree(ca, trusted, crl, basedir):
- ca_status = set()
- ca.verify(trusted = trusted, crl = crl, status = ca_status)
+
+def walk_tree(cauri, ca, trusted, crl, basedir):
trusted.insert(0, ca)
- diruri, mfturi = [first_rsync_uri(uri) for uri in ca.getSIA()[:2]]
- mft = rpki.POW.Manifest.derReadFile(uri_to_fn(mfturi, basedir))
- ee = mft.certs()[0]
- crldp = first_rsync_uri(ee.getCRLDP())
- crl = rpki.POW.CRL.derReadFile(uri_to_fn(crldp, basedir))
- crl_status = set()
- mft_status = set()
+
+ sia = ca.getSIA()
+ diruri = first_rsync_uri(sia[0])
+ mfturi = first_rsync_uri(sia[1])
+ try:
+ mft = rpki.POW.Manifest.derReadFile(uri_to_filename(mfturi, basedir))
+ except rpki.POW.Error as e:
+ print mfturi, e
+ return
+ ee = mft.certs()[0]
+ crldp = ee.getCRLDP()
+ crluri = first_rsync_uri(crldp)
+ try:
+ crl = rpki.POW.CRL.derReadFile(uri_to_filename(crluri, basedir))
+ except rpki.POW.Error as e:
+ print crluri, e
+ return
+
+ crl_status = Status.update(crluri)
crl.verify(ca, crl_status)
+
+ mft_status = Status.update(mfturi)
ee.verify(trusted = trusted, crl = crl, status = mft_status)
mft.verify(status = mft_status)
- print "CA status: ", ", ".join(str(s) for s in ca_status)
- print "CRL status:", ", ".join(str(s) for s in crl_status)
- print "MFT status:", ", ".join(str(s) for s in mft_status)
+ crl_status.add(rpki.POW.validation_status.CRL_NOT_IN_MANIFEST)
for fn, digest in mft.getFiles():
+ uri = diruri + fn
+ status = Status.update(uri)
- with open(os.path.join(uri_to_fn(diruri, basedir), fn), "rb") as f:
- obj = f.read()
- dgst = rpki.POW.Digest(rpki.POW.SHA256_DIGEST)
- dgst.update(obj)
- print fn, digest.encode("hex"), "OK hash" if dgst.digest() == digest else "Bad hash"
-
- if fn.endswith(".crl") and obj != crl.derWrite():
- print "CRL mismatch"
- if fn.endswith(".crl"):
+ if uri == crluri:
+ if digest != sha256(crl.derWrite()):
+ status.add(rpki.POW.validation_status.DIGEST_MISMATCH)
+ status.remove(rpki.POW.validation_status.CRL_NOT_IN_MANIFEST)
continue
+ with open(os.path.join(uri_to_filename(diruri, basedir), fn), "rb") as f:
+ der = f.read()
+ if sha256(der) != digest:
+ status.add(rpki.POW.validation_status.DIGEST_MISMATCH)
+
if fn.endswith(".roa"):
- roa = rpki.POW.ROA.derRead(obj)
- roa_status = set()
+ roa = rpki.POW.ROA.derRead(der)
ee = roa.certs()[0]
- ee.verify(trusted = trusted, crl = crl, status = roa_status)
- roa.verify(status = roa_status)
+ ee.verify(trusted = trusted, crl = crl, status = status)
+ roa.verify(status = status)
continue
if fn.endswith(".gbr"):
- gbr = rpki.POW.CMS.derRead(obj)
- gbr_status = set()
+ gbr = rpki.POW.CMS.derRead(der)
ee = gbr.certs()[0]
- ee.verify(trusted = trusted, crl = crl, status = gbr_status)
- vcard = gbr.verify(status = gbr_status)
- print vcard
+ ee.verify(trusted = trusted, crl = crl, status = status)
+ vcard = gbr.verify(status = status)
continue
- cer = rpki.POW.X509.derRead(obj)
- bc = cer.getBasicConstraints()
- if bc and bc[0]:
- try:
- walk_tree(cer, trusted, crl, basedir)
- except rpki.POW.Error as e:
- print "CA", diruri + fn, "failed:", e
- else:
- cer_status = set()
- cer.verify(trusted = trusted, crl = crl, status = cer_status)
-
-
-def main():
-
- os.putenv("TZ", "UTC")
- time.tzset()
-
- parse_options()
-
- basedir = args.unauthenticated
+ if fn.endswith(".cer"):
+ cer = rpki.POW.X509.derRead(der)
+ cer.verify(trusted = trusted, crl = crl, status = status)
+ is_ca = (cer.getBasicConstraints() or (False, None))[0]
+ if is_ca:
+ walk_tree(diruri + fn, cer, trusted, crl, basedir)
+ continue
- for uri, pk in read_tals():
- print
- try:
- x = rpki.POW.X509.derReadFile(uri_to_fn(uri, basedir))
- except rpki.POW.OpenSSLError:
- print "Couldn't open TA {}".format(uri)
- else:
- ok = pk.derWritePublic() == x.getPublicKey().derWritePublic()
- print "OK " if ok else "Bad", uri
- if ok:
- walk_tree(x, [x], None, basedir)
-
-
-if __name__ == "__main__":
- main()
+ status.add(rpki.POW.validation_status.UNKNOWN_OBJECT_TYPE_SKIPPED)
+
+
+os.putenv("TZ", "UTC")
+time.tzset()
+
+args = parse_arguments()
+
+basedir = args.unauthenticated
+
+for uri, key in read_tals():
+ status = Status.update(uri)
+ status.add(rpki.POW.validation_status.OBJECT_REJECTED)
+ try:
+ cer = rpki.POW.X509.derReadFile(uri_to_filename(uri, basedir))
+ except rpki.POW.OpenSSLError:
+ status.add(rpki.POW.validation_status.UNREADABLE_TRUST_ANCHOR)
+ continue
+ if key.derWritePublic() != cer.getPublicKey().derWritePublic():
+ status.add(rpki.POW.validation_status.TRUST_ANCHOR_KEY_MISMATCH)
+ continue
+ trusted = [cer]
+ try:
+ cer.verify(trusted = trusted, status = status)
+ except:
+ continue
+ else:
+ status.remove(rpki.POW.validation_status.OBJECT_REJECTED)
+ walk_tree(uri, cer, trusted, None, basedir)
+
+for uri in sorted(Status.db):
+ print Status.db[uri]