aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/rpki-db-schema.sql2
-rw-r--r--rpkid/rpki/left_right.py28
-rw-r--r--rpkid/rpki/sql.py10
-rw-r--r--rpkid/rpki/x509.py73
-rw-r--r--rpkid/testbed.py14
5 files changed, 81 insertions, 46 deletions
diff --git a/docs/rpki-db-schema.sql b/docs/rpki-db-schema.sql
index 6a436e80..87cb552b 100644
--- a/docs/rpki-db-schema.sql
+++ b/docs/rpki-db-schema.sql
@@ -166,7 +166,7 @@ DROP TABLE IF EXISTS route_origin;
CREATE TABLE route_origin (
route_origin_id SERIAL NOT NULL,
as_number DECIMAL(24,0),
- exact_match BOOLEAN NOT NULL,
+ exact_match BOOLEAN,
cert LONGBLOB,
roa LONGBLOB,
self_id BIGINT unsigned NOT NULL,
diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py
index 3803d5f4..17f92ddd 100644
--- a/rpkid/rpki/left_right.py
+++ b/rpkid/rpki/left_right.py
@@ -91,6 +91,8 @@ class data_elt(base_elt, rpki.sql.sql_persistant):
"""Generic left-right PDU constructor."""
self = cls()
for k,v in kargs.items():
+ if isinstance(v, bool):
+ v = 1 if v else 0
setattr(self, k, v)
return self
@@ -820,8 +822,9 @@ class route_origin_elt(data_elt):
attributes = ("action", "type", "tag", "self_id", "route_origin_id", "as_number", "exact_match", "ipv4", "ipv6")
booleans = ("suppress_publication",)
- sql_template = rpki.sql.template("route_origin", "route_origin_id", "self_id", "as_number", "exact_match",
- "ca_detail_id", "roa",
+ sql_template = rpki.sql.template("route_origin", "route_origin_id", "ca_detail_id",
+ "self_id", "as_number", "exact_match",
+ ("roa", rpki.x509.ROA),
("cert", rpki.x509.X509))
ca_detail_id = None
@@ -856,11 +859,6 @@ class route_origin_elt(data_elt):
"""Fetch all ca_detail objects that link to this route_origin object."""
return rpki.sql.ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
- def serve_pre_save_hook(self, q_pdu, r_pdu):
- """Extra server actions for route_origin_elt -- normalize exact_match."""
- if self.exact_match is None:
- self.exact_match = False
-
def serve_post_save_hook(self, q_pdu, r_pdu):
"""Extra server actions for route_origin_elt."""
self.unimplemented_control("suppress_publication")
@@ -905,6 +903,10 @@ class route_origin_elt(data_elt):
/dev/random, but there is not much we can do about that.
"""
+ if self.exact_match is None:
+ rpki.log.warn("Can't generate ROA with undefined exactMatch")
+ return
+
if self.ipv4 is None and self.ipv6 is None:
rpki.log.warn("Can't generate ROA for empty address list")
return
@@ -934,19 +936,13 @@ class route_origin_elt(data_elt):
resources = rpki.resource_set.resource_bag(v4 = self.ipv4, v6 = self.ipv6)
- payload = rpki.roa.RouteOriginAttestation()
- payload.version.set(0)
- payload.asID.set(self.as_number)
- payload.exactMatch.set(self.exact_match)
- payload.ipAddrBlocks.set((a.to_roa_tuple() for a in (self.ipv4, self.ipv6) if a))
-
keypair = rpki.x509.RSA()
keypair.generate()
sia = ((rpki.oids.name2oid["id-ad-signedObject"], ("uri", self.roa_uri(ca, keypair))),)
self.cert = ca_detail.issue_ee(ca, resources, keypair.get_RSApublic(), sia = sia)
- self.roa = rpki.cms.sign(payload.toString(), keypair, (self.cert,))
+ self.roa = rpki.x509.ROA.build(self.as_number, self.exact_match, self.ipv4, self.ipv6, keypair, (self.cert,))
self.ca_detail_id = ca_detail.ca_detail_id
self.sql_store()
@@ -957,8 +953,6 @@ class route_origin_elt(data_elt):
ca_detail.generate_manifest()
- raise rpki.exceptions.NotImplementedYet
-
def roa_uri(self, ca, key = None):
"""Return the publication URI for this route_origin's ROA."""
return ca.sia_uri + (key or self.cert).gSKI() + ".roa"
@@ -969,7 +963,7 @@ class route_origin_elt(data_elt):
def ee_uri(self, ca):
"""Return the publication URI for this route_origin's ROA's EE certificate."""
- return ca.sia_uri + self.uri_tail()
+ return ca.sia_uri + self.ee_uri_tail()
class list_resources_elt(base_elt):
"""<list_resources/> element."""
diff --git a/rpkid/rpki/sql.py b/rpkid/rpki/sql.py
index dad37606..f9f58e4f 100644
--- a/rpkid/rpki/sql.py
+++ b/rpkid/rpki/sql.py
@@ -449,7 +449,8 @@ class ca_detail_obj(sql_persistant):
for child_cert in predecessor.child_certs():
child_cert.reissue(self)
for route_origin in predecessor.route_origins():
- raise rpki.exceptions.NotImplementedYet, "Don't (yet) know how to reissue ROAs"
+ if route_origin.roa:
+ raise rpki.exceptions.NotImplementedYet, "Don't (yet) know how to reissue ROAs"
def delete(self, ca, repository):
"""Delete this ca_detail and all of the certs it issued."""
@@ -460,7 +461,8 @@ class ca_detail_obj(sql_persistant):
for revoked__cert in self.revoked_certs():
revoked_cert.sql_delete()
for route_origin in self.route_origins():
- raise rpki.exceptions.NotImplementedYet, "Don't (yet) know how to withdraw ROAs"
+ if route_origin.roa:
+ raise rpki.exceptions.NotImplementedYet, "Don't (yet) know how to withdraw ROAs"
repository.withdraw(self.latest_manifest, self.manifest_uri(ca))
repository.withdraw(self.latest_crl, self.crl_uri())
self.sql_delete()
@@ -669,15 +671,13 @@ class ca_detail_obj(sql_persistant):
certs = [(c.uri_tail(), c.cert) for c in self.child_certs()] + \
[(r.ee_uri_tail(), r.cert) for r in self.route_origins() if r.cert is not None]
- m = rpki.x509.SignedManifest()
- m.build(
+ self.latest_manifest = rpki.x509.SignedManifest.build(
serial = ca.next_manifest_number(),
thisUpdate = now,
nextUpdate = nextUpdate,
names_and_objs = certs,
keypair = self.manifest_private_key_id,
certs = rpki.x509.X509_chain(self.latest_manifest_cert))
- self.latest_manifest = m
repository.publish(self.latest_manifest, self.manifest_uri(ca))
diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py
index 3636ece0..c9ed2864 100644
--- a/rpkid/rpki/x509.py
+++ b/rpkid/rpki/x509.py
@@ -27,7 +27,8 @@ some of the nasty details. This involves a lot of format conversion.
"""
import POW, tlslite.api, POW.pkix, base64, time
-import rpki.exceptions, rpki.resource_set, rpki.manifest, rpki.cms, rpki.oids, rpki.sundial
+import rpki.exceptions, rpki.resource_set, rpki.cms, rpki.oids, rpki.sundial
+import rpki.manifest, rpki.roa
def calculate_SKI(public_key_der):
"""Calculate the SKI value given the DER representation of a public
@@ -578,39 +579,53 @@ class RSApublic(DER_object):
"""Calculate the SKI of this public key."""
return calculate_SKI(self.get_DER())
-class SignedManifest(DER_object):
- """Class to hold a signed manifest.
+class CMS_object(DER_object):
+ """Class to hold a CMS-wrapped object.
- Signed manifests are a little different from the other DER_object
+ CMS-wrapped objects are a little different from the other DER_object
types because the signed object is CMS wrapping inner content that's
also ASN.1, and due to our current minimal support for CMS we can't
just handle this as a pretty composite object. So, for now anyway,
- this SignedManifest object refers to the outer CMS wrapped manifest
- so that the usual DER and PEM operations do the obvious things, and
- the inner content is handle via separate methods using rpki.manifest.
+ a CMS_object is the outer CMS wrapped object so that the usual DER
+ and PEM operations do the obvious things, and the inner content is
+ handle via separate methods.
"""
formats = ("DER",)
other_clear = ("content",)
- pem_converter = PEM_converter("RPKI MANIFEST")
def get_DER(self):
- """Get the DER value of this manifest."""
+ """Get the DER value of this CMS_object."""
assert not self.empty()
if self.DER:
return self.DER
raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available"
def get_content(self):
- """Get the inner content of this manifest."""
+ """Get the inner content of this CMS_object."""
assert self.content is not None
return self.content
def set_content(self, content):
- """Set the (inner) content of this manifest, clearing the wrapper."""
+ """Set the (inner) content of this CMS_object, clearing the wrapper."""
self.clear()
self.content = content
+ def verify_and_store(self, ta, obj):
+ """Verify CMS wrapper and store inner content."""
+ s = rpki.cms.verify(self.get_DER(), ta)
+ obj.fromString(s)
+ self.content = obj
+
+ def sign(self, keypair, certs):
+ """Sign and wrap inner content."""
+ self.DER = rpki.cms.sign(self.get_content().toString(), keypair, certs)
+
+class SignedManifest(CMS_object):
+ """Class to hold a signed manifest."""
+
+ pem_converter = PEM_converter("RPKI MANIFEST")
+
def getThisUpdate(self):
"""Get thisUpdate value from this manifest."""
return rpki.sundial.datetime.fromGeneralizedTime(self.get_content().thisUpdate.get())
@@ -621,13 +636,12 @@ class SignedManifest(DER_object):
def verify(self, ta):
"""Verify this manifest."""
- m = rpki.manifest.Manifest()
- s = rpki.cms.verify(self.get_DER(), ta)
- m.fromString(s)
- self.content = m
+ self.verify_and_store(ta, rpki.manifest.Manifest())
- def build(self, serial, thisUpdate, nextUpdate, names_and_objs, keypair, certs, version = 0):
- """Build the inner content of this manifest and sign it with CMS."""
+ @classmethod
+ def build(cls, serial, thisUpdate, nextUpdate, names_and_objs, keypair, certs, version = 0):
+ """Build a signed manifest."""
+ self = cls()
filelist = []
for name, obj in names_and_objs:
d = POW.Digest(POW.SHA256_DIGEST)
@@ -642,7 +656,30 @@ class SignedManifest(DER_object):
m.fileHashAlg.set((2, 16, 840, 1, 101, 3, 4, 2, 1)) # id-sha256
m.fileList.set(filelist)
self.set_content(m)
- self.DER = rpki.cms.sign(m.toString(), keypair, certs)
+ self.sign(keypair, certs)
+ return self
+
+class ROA(CMS_object):
+ """Class to hold a signed ROA."""
+
+ pem_converter = PEM_converter("ROUTE ORIGIN ATTESTATION")
+
+ def verify(self, ta):
+ """Verify this ROA."""
+ self.verify_and_store(ta, rpki.roa.RouteOriginAttestation())
+
+ @classmethod
+ def build(cls, as_number, exact_match, ipv4, ipv6, keypair, certs, version = 0):
+ """Build a ROA."""
+ self = cls()
+ r = rpki.roa.RouteOriginAttestation()
+ r.version.set(version)
+ r.asID.set(as_number)
+ r.exactMatch.set(exact_match)
+ r.ipAddrBlocks.set((a.to_roa_tuple() for a in (ipv4, ipv6) if a))
+ self.set_content(r)
+ self.sign(keypair, certs)
+ return self
class CRL(DER_object):
"""Class to hold a Certificate Revocation List."""
diff --git a/rpkid/testbed.py b/rpkid/testbed.py
index 5d1a8e7e..eebafe87 100644
--- a/rpkid/testbed.py
+++ b/rpkid/testbed.py
@@ -302,10 +302,11 @@ cmds = { "sleep" : cmd_sleep,
class route_origin(object):
"""Representation for a route_origin object."""
- def __init__(self, asn, ipv4, ipv6):
+ def __init__(self, asn, ipv4, ipv6, exact_match):
self.asn = asn
self.v4 = rpki.resource_set.resource_set_ipv4("".join(ipv4.split())) if ipv4 else None
self.v6 = rpki.resource_set.resource_set_ipv6("".join(ipv6.split())) if ipv6 else None
+ self.exact_match = exact_match
def __eq__(self, other):
return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
@@ -323,7 +324,7 @@ class route_origin(object):
@classmethod
def parse(cls, yaml):
- return cls(yaml.get("asn"), yaml.get("ipv4"), yaml.get("ipv6"))
+ return cls(yaml.get("asn"), yaml.get("ipv4"), yaml.get("ipv6"), yaml.get("exact_match", False))
class allocation_db(list):
"""Representation of all the entities and allocations in the test system.
@@ -559,8 +560,8 @@ class allocation(object):
rpki.log.info("Calling rpkid for %s" % self.name)
pdu.type = "query"
elt = rpki.left_right.msg((pdu,)).toXML()
- rpki.relaxng.left_right.assertValid(elt)
rpki.log.debug(lxml.etree.tostring(elt, pretty_print = True, encoding = "us-ascii"))
+ rpki.relaxng.left_right.assertValid(elt)
cms = rpki.cms.xml_sign(
elt = elt,
key = testbed_key,
@@ -574,8 +575,8 @@ class allocation(object):
url = url,
msg = cms)
elt = rpki.cms.xml_verify(der = cms, ta = self.rpkid_ta)
- rpki.relaxng.left_right.assertValid(elt)
rpki.log.debug(lxml.etree.tostring(elt, pretty_print = True, encoding = "us-ascii"))
+ rpki.relaxng.left_right.assertValid(elt)
pdu = rpki.left_right.sax_handler.saxify(elt)[0]
assert pdu.type == "reply" and not isinstance(pdu, rpki.left_right.report_error_elt)
return pdu
@@ -641,7 +642,10 @@ class allocation(object):
rpki.log.info("Creating rpkid route_origin objects for %s" % self.name)
for ro in self.route_origins:
ro.route_origin_id = self.call_rpkid(rpki.left_right.route_origin_elt.make_pdu(
- action = "create", self_id = self.self_id, as_number = ro.asn, ipv4 = ro.v4, ipv6 = ro.v6)).route_origin_id
+ action = "create", self_id = self.self_id, as_number = ro.asn,
+ exact_match = ro.exact_match, ipv4 = ro.v4, ipv6 = ro.v6)).route_origin_id
+
+# exact_match = 1 if ro.exact_match else 0
def write_leaf_yaml(self):
"""Write YAML scripts for leaf nodes. Only supports list requests