aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid/rpki')
-rw-r--r--rpkid/rpki/exceptions.py15
-rw-r--r--rpkid/rpki/irdb/models.py110
-rw-r--r--rpkid/rpki/irdb/zookeeper.py92
-rw-r--r--rpkid/rpki/irdbd.py29
-rw-r--r--rpkid/rpki/left_right.py98
-rw-r--r--rpkid/rpki/oids.py203
-rw-r--r--rpkid/rpki/old_irdbd.py117
-rw-r--r--rpkid/rpki/relaxng.py321
-rw-r--r--rpkid/rpki/resource_set.py1
-rw-r--r--rpkid/rpki/rootd.py5
-rw-r--r--rpkid/rpki/rpkic.py60
-rw-r--r--rpkid/rpki/rpkid.py350
-rw-r--r--rpkid/rpki/rpkid_tasks.py152
-rw-r--r--rpkid/rpki/sql_schemas.py15
-rw-r--r--rpkid/rpki/up_down.py2
-rw-r--r--rpkid/rpki/x509.py419
16 files changed, 1576 insertions, 413 deletions
diff --git a/rpkid/rpki/exceptions.py b/rpkid/rpki/exceptions.py
index d390d67b..d8d3774e 100644
--- a/rpkid/rpki/exceptions.py
+++ b/rpkid/rpki/exceptions.py
@@ -350,3 +350,18 @@ class NullValidityInterval(RPKI_Exception):
"""
Requested validity interval is null.
"""
+
+class BadX510DN(RPKI_Exception):
+ """
+ X.510 distinguished name does not match profile.
+ """
+
+class BadAutonomousSystemNumber(RPKI_Exception):
+ """
+ Bad AutonomousSystem number.
+ """
+
+class WrongEKU(RPKI_Exception):
+ """
+ Extended Key Usage extension does not match profile.
+ """
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py
index c795e21c..1ad9b4e3 100644
--- a/rpkid/rpki/irdb/models.py
+++ b/rpkid/rpki/irdb/models.py
@@ -401,7 +401,7 @@ class EECertificate(Certificate):
self.private_key = rpki.x509.RSA.generate(quiet = True)
self.certificate = self.issuer.certify(
subject_name = self.subject_name,
- subject_key = self.private_key.get_RSApublic(),
+ subject_key = self.private_key.get_public(),
validity_interval = ee_certificate_lifetime,
is_ca = False)
@@ -451,58 +451,76 @@ class BSC(Certificate):
def __unicode__(self):
return self.handle
-class Child(CrossCertification):
- issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "children")
- name = django.db.models.TextField(null = True, blank = True)
+class ResourceSet(django.db.models.Model):
valid_until = SundialField()
+ class Meta:
+ abstract = True
+
@property
def resource_bag(self):
- child_asn = rpki.irdb.ChildASN.objects.raw("""
- SELECT *
- FROM irdb_childasn
- WHERE child_id = %s
- """, [self.id])
- child_net = list(rpki.irdb.ChildNet.objects.raw("""
- SELECT *
- FROM irdb_childnet
- WHERE child_id = %s
- """, [self.id]))
+ raw_asn, raw_net = self._select_resource_bag()
asns = rpki.resource_set.resource_set_as.from_django(
- (a.start_as, a.end_as) for a in child_asn)
+ (a.start_as, a.end_as) for a in raw_asn)
ipv4 = rpki.resource_set.resource_set_ipv4.from_django(
- (a.start_ip, a.end_ip) for a in child_net if a.version == "IPv4")
+ (a.start_ip, a.end_ip) for a in raw_net if a.version == "IPv4")
ipv6 = rpki.resource_set.resource_set_ipv6.from_django(
- (a.start_ip, a.end_ip) for a in child_net if a.version == "IPv6")
+ (a.start_ip, a.end_ip) for a in raw_net if a.version == "IPv6")
return rpki.resource_set.resource_bag(
valid_until = self.valid_until, asn = asns, v4 = ipv4, v6 = ipv6)
# Writing of .setter method deferred until something needs it.
- # This shouldn't be necessary
- class Meta:
- unique_together = ("issuer", "handle")
-
-class ChildASN(django.db.models.Model):
- child = django.db.models.ForeignKey(Child, related_name = "asns")
+class ResourceSetASN(django.db.models.Model):
start_as = django.db.models.BigIntegerField()
end_as = django.db.models.BigIntegerField()
+ class Meta:
+ abstract = True
+
def as_resource_range(self):
return rpki.resource_set.resource_range_as(self.start_as, self.end_as)
- class Meta:
- unique_together = ("child", "start_as", "end_as")
-
-class ChildNet(django.db.models.Model):
- child = django.db.models.ForeignKey(Child, related_name = "address_ranges")
+class ResourceSetNet(django.db.models.Model):
start_ip = django.db.models.CharField(max_length = 40)
end_ip = django.db.models.CharField(max_length = 40)
version = EnumField(choices = ip_version_choices)
+ class Meta:
+ abstract = True
+
def as_resource_range(self):
return rpki.resource_set.resource_range_ip.from_strings(self.start_ip, self.end_ip)
+class Child(CrossCertification, ResourceSet):
+ issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "children")
+ name = django.db.models.TextField(null = True, blank = True)
+
+ def _select_resource_bag(self):
+ child_asn = rpki.irdb.ChildASN.objects.raw("""
+ SELECT *
+ FROM irdb_childasn
+ WHERE child_id = %s
+ """, [self.id])
+ child_net = list(rpki.irdb.ChildNet.objects.raw("""
+ SELECT *
+ FROM irdb_childnet
+ WHERE child_id = %s
+ """, [self.id]))
+ return child_asn, child_net
+
+ class Meta:
+ unique_together = ("issuer", "handle")
+
+class ChildASN(ResourceSetASN):
+ child = django.db.models.ForeignKey(Child, related_name = "asns")
+
+ class Meta:
+ unique_together = ("child", "start_as", "end_as")
+
+class ChildNet(ResourceSetNet):
+ child = django.db.models.ForeignKey(Child, related_name = "address_ranges")
+
class Meta:
unique_together = ("child", "start_ip", "end_ip", "version")
@@ -561,6 +579,42 @@ class GhostbusterRequest(django.db.models.Model):
parent = django.db.models.ForeignKey(Parent, related_name = "ghostbuster_requests", null = True)
vcard = django.db.models.TextField()
+class EECertificateRequest(ResourceSet):
+ issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "ee_certificate_requests")
+ pkcs10 = PKCS10Field()
+ gski = django.db.models.CharField(max_length = 27)
+ cn = django.db.models.CharField(max_length = 64)
+ sn = django.db.models.CharField(max_length = 64)
+ eku = django.db.models.TextField(null = True)
+
+ def _select_resource_bag(self):
+ ee_asn = rpki.irdb.EECertificateRequestASN.objects.raw("""
+ SELECT *
+ FROM irdb_eecertificaterequestasn
+ WHERE ee_certificate_request_id = %s
+ """, [self.id])
+ ee_net = rpki.irdb.EECertificateRequestNet.objects.raw("""
+ SELECT *
+ FROM irdb_eecertificaterequestnet
+ WHERE ee_certificate_request_id = %s
+ """, [self.id])
+ return ee_asn, ee_net
+
+ class Meta:
+ unique_together = ("issuer", "gski")
+
+class EECertificateRequestASN(ResourceSetASN):
+ ee_certificate_request = django.db.models.ForeignKey(EECertificateRequest, related_name = "asns")
+
+ class Meta:
+ unique_together = ("ee_certificate_request", "start_as", "end_as")
+
+class EECertificateRequestNet(ResourceSetNet):
+ ee_certificate_request = django.db.models.ForeignKey(EECertificateRequest, related_name = "address_ranges")
+
+ class Meta:
+ unique_together = ("ee_certificate_request", "start_ip", "end_ip", "version")
+
class Repository(CrossCertification):
issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "repositories")
client_handle = HandleField()
diff --git a/rpkid/rpki/irdb/zookeeper.py b/rpkid/rpki/irdb/zookeeper.py
index 2bae0e8b..f99dc9f0 100644
--- a/rpkid/rpki/irdb/zookeeper.py
+++ b/rpkid/rpki/irdb/zookeeper.py
@@ -55,6 +55,16 @@ myrpki_namespace = "http://www.hactrn.net/uris/rpki/myrpki/"
myrpki_version = "2"
myrpki_namespaceQName = "{" + myrpki_namespace + "}"
+# XML namespace and protocol version for router certificate requests.
+# We probably ought to be pulling this sort of thing from the schema,
+# with an assertion to make sure that we understand the current
+# protocol version number, but just copy what we did for myrpki until
+# I'm ready to rewrite the rpki.relaxng code.
+
+routercert_namespace = "http://www.hactrn.net/uris/rpki/router-certificate/"
+routercert_version = "1"
+routercert_namespaceQName = "{" + routercert_namespace + "}"
+
myrpki_section = "myrpki"
irdbd_section = "irdbd"
rpkid_section = "rpkid"
@@ -1588,3 +1598,85 @@ class Zookeeper(object):
if rpkid_query:
rpkid_reply = self.call_rpkid(rpkid_query)
self.check_error_report(rpkid_reply)
+
+
+ @django.db.transaction.commit_on_success
+ def add_ee_certificate_request(self, pkcs10, resources):
+ """
+ Check a PKCS #10 request to see if it complies with the
+ specification for a RPKI EE certificate; if it does, add an
+ EECertificateRequest for it to the IRDB.
+
+ Not yet sure what we want for update and delete semantics here, so
+ for the moment this is straight addition. See methods like
+ .load_asns() and .load_prefixes() for other strategies.
+ """
+
+ pkcs10.check_valid_request_ee()
+ ee_request = self.resource_ca.ee_certificate_requests.create(
+ pkcs10 = pkcs10,
+ gski = pkcs10.gSKI(),
+ valid_until = resources.valid_until)
+ for range in resources.asn:
+ ee_request.asns.create(start_as = str(range.min), end_as = str(range.max))
+ for range in resources.v4:
+ ee_request.address_ranges.create(start_ip = str(range.min), end_ip = str(range.max), version = 4)
+ for range in resources.v6:
+ ee_request.address_ranges.create(start_ip = str(range.min), end_ip = str(range.max), version = 6)
+
+
+ @django.db.transaction.commit_on_success
+ def add_router_certificate_request(self, router_certificate_request_xml, valid_until = None):
+ """
+ Read XML file containing one or more router certificate requests,
+ attempt to add request(s) to IRDB.
+
+ Check each PKCS #10 request to see if it complies with the
+ specification for a router certificate; if it does, create an EE
+ certificate request for it along with the ASN resources and
+ router-ID supplied in the XML.
+ """
+
+ xml = ElementTree(file = router_certificate_request_xml).getroot()
+ rpki.relaxng.router_certificate.assertValid(xml)
+
+ for req in xml.getiterator(routercert_namespaceQName + "router_certificate_request"):
+
+ pkcs10 = rpki.x509.PKCS10(Base64 = req.text)
+ router_id = long(req.get("router_id"))
+ asns = rpki.resource_set.resource_set_as(req.get("asn"))
+ if not valid_until:
+ valid_until = req.get("valid_until")
+
+ if valid_until and isinstance(valid_until, (str, unicode)):
+ valid_until = rpki.sundial.datetime.fromXMLtime(valid_until)
+
+ if not valid_until:
+ valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 365)
+ elif valid_until < rpki.sundial.now():
+ raise PastExpiration, "Specified expiration date %s has already passed" % valid_until
+
+ pkcs10.check_valid_request_router()
+
+ cn = "ROUTER-%08x" % asns[0].min
+ sn = "%08x" % router_id
+
+ ee_request = self.resource_ca.ee_certificate_requests.create(
+ pkcs10 = pkcs10,
+ gski = pkcs10.gSKI(),
+ valid_until = valid_until,
+ cn = cn,
+ sn = sn,
+ eku = rpki.oids.id_kp_bgpsec_router)
+
+ for range in asns:
+ ee_request.asns.create(start_as = str(range.min), end_as = str(range.max))
+
+
+ @django.db.transaction.commit_on_success
+ def delete_router_certificate_request(self, gski):
+ """
+ Delete a router certificate request from this RPKI entity.
+ """
+
+ self.resource_ca.ee_certificate_requests.get(gski = gski).delete()
diff --git a/rpkid/rpki/irdbd.py b/rpkid/rpki/irdbd.py
index c27995e7..41739dc4 100644
--- a/rpkid/rpki/irdbd.py
+++ b/rpkid/rpki/irdbd.py
@@ -39,8 +39,9 @@ import rpki.daemonize
class main(object):
def handle_list_resources(self, q_pdu, r_msg):
- child = rpki.irdb.Child.objects.get(issuer__handle__exact = q_pdu.self_handle,
- handle = q_pdu.child_handle)
+ child = rpki.irdb.Child.objects.get(
+ issuer__handle__exact = q_pdu.self_handle,
+ handle = q_pdu.child_handle)
resources = child.resource_bag
r_pdu = rpki.left_right.list_resources_elt()
r_pdu.tag = q_pdu.tag
@@ -84,6 +85,23 @@ class main(object):
r_pdu.vcard = ghostbuster.vcard
r_msg.append(r_pdu)
+ def handle_list_ee_certificate_requests(self, q_pdu, r_msg):
+ for ee_req in rpki.irdb.EECertificateRequest.objects.filter(issuer__handle__exact = q_pdu.self_handle):
+ resources = ee_req.resource_bag
+ r_pdu = rpki.left_right.list_ee_certificate_requests_elt()
+ r_pdu.tag = q_pdu.tag
+ r_pdu.self_handle = q_pdu.self_handle
+ r_pdu.gski = ee_req.gski
+ r_pdu.valid_until = ee_req.valid_until.strftime("%Y-%m-%dT%H:%M:%SZ")
+ r_pdu.asn = resources.asn
+ r_pdu.ipv4 = resources.v4
+ r_pdu.ipv6 = resources.v6
+ r_pdu.cn = ee_req.cn
+ r_pdu.sn = ee_req.sn
+ r_pdu.eku = ee_req.eku
+ r_pdu.pkcs10 = ee_req.pkcs10
+ r_msg.append(r_pdu)
+
def handler(self, query, path, cb):
try:
q_pdu = None
@@ -219,9 +237,10 @@ class main(object):
self.start_new_transaction = django.db.transaction.commit_manually(django.db.transaction.commit)
self.dispatch_vector = {
- rpki.left_right.list_resources_elt : self.handle_list_resources,
- rpki.left_right.list_roa_requests_elt : self.handle_list_roa_requests,
- rpki.left_right.list_ghostbuster_requests_elt : self.handle_list_ghostbuster_requests }
+ rpki.left_right.list_resources_elt : self.handle_list_resources,
+ rpki.left_right.list_roa_requests_elt : self.handle_list_roa_requests,
+ rpki.left_right.list_ghostbuster_requests_elt : self.handle_list_ghostbuster_requests,
+ rpki.left_right.list_ee_certificate_requests_elt : self.handle_list_ee_certificate_requests}
try:
self.http_server_host = self.cfg.get("server-host", "")
diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py
index 9be927f3..2d46cdfa 100644
--- a/rpkid/rpki/left_right.py
+++ b/rpkid/rpki/left_right.py
@@ -205,6 +205,13 @@ class self_elt(data_elt):
"""
return rpki.rpkid.ghostbuster_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
+ @property
+ def ee_certificates(self):
+ """
+ Fetch all EE certificate objects that link to this self object.
+ """
+ return rpki.rpkid.ee_cert_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
+
def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
"""
@@ -362,19 +369,33 @@ class self_elt(data_elt):
"""
if self.cron_tasks is None:
- self.cron_tasks = (
- rpki.rpkid_tasks.PollParentTask(self),
- rpki.rpkid_tasks.UpdateChildrenTask(self),
- rpki.rpkid_tasks.UpdateROAsTask(self),
- rpki.rpkid_tasks.UpdateGhostbustersTask(self),
- rpki.rpkid_tasks.RegenerateCRLsAndManifestsTask(self),
- rpki.rpkid_tasks.CheckFailedPublication(self))
+ self.cron_tasks = tuple(task(self) for task in rpki.rpkid_tasks.task_classes)
for task in self.cron_tasks:
self.gctx.task_add(task)
completion.register(task)
+ def find_covering_ca_details(self, resources):
+ """
+ Return all active ca_detail_objs for this <self/> which cover a
+ particular set of resources.
+
+ If we expected there to be a large number of ca_detail_objs, we
+ could add index tables and write fancy SQL query to do this, but
+ for the expected common case where there are only one or two
+ active ca_detail_objs per <self/>, it's probably not worth it. In
+ any case, this is an optimization we can leave for later.
+ """
+
+ results = set()
+ for parent in self.parents:
+ for ca in parent.cas:
+ ca_detail = ca.active_ca_detail
+ if ca_detail is not None and ca_detail.covers(resources):
+ results.add(ca_detail)
+ return results
+
class bsc_elt(data_elt):
"""
<bsc/> (Business Signing Context) element.
@@ -1036,6 +1057,66 @@ class list_ghostbuster_requests_elt(rpki.xml_utils.text_elt, left_right_namespac
def __repr__(self):
return rpki.log.log_repr(self, self.self_handle, self.parent_handle)
+class list_ee_certificate_requests_elt(rpki.xml_utils.base_elt, left_right_namespace):
+ """
+ <list_ee_certificate_requests/> element.
+ """
+
+ element_name = "list_ee_certificate_requests"
+ attributes = ("self_handle", "tag", "gski", "valid_until", "asn", "ipv4", "ipv6", "cn", "sn", "eku")
+ elements = ("pkcs10",)
+
+ pkcs10 = None
+ valid_until = None
+ eku = None
+
+ def __repr__(self):
+ return rpki.log.log_repr(self, self.self_handle, self.gski, self.cn, self.sn, self.asn, self.ipv4, self.ipv6)
+
+ def startElement(self, stack, name, attrs):
+ """
+ Handle <list_ee_certificate_requests/> element. This requires special
+ handling due to the data types of some of the attributes.
+ """
+ if name not in self.elements:
+ assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
+ self.read_attrs(attrs)
+ if isinstance(self.valid_until, str):
+ self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until)
+ if self.asn is not None:
+ self.asn = rpki.resource_set.resource_set_as(self.asn)
+ if self.ipv4 is not None:
+ self.ipv4 = rpki.resource_set.resource_set_ipv4(self.ipv4)
+ if self.ipv6 is not None:
+ self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6)
+ if self.eku is not None:
+ self.eku = self.eku.split(",")
+
+ def endElement(self, stack, name, text):
+ """
+ Handle <pkcs10/> sub-element.
+ """
+ assert len(self.elements) == 1
+ if name == self.elements[0]:
+ self.pkcs10 = rpki.x509.PKCS10(Base64 = text)
+ else:
+ assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
+ stack.pop()
+
+ def toXML(self):
+ """
+ Generate <list_ee_certificate_requests/> element. This requires special
+ handling due to the data types of some of the attributes.
+ """
+ if isinstance(self.eku, (tuple, list)):
+ self.eku = ",".join(self.eku)
+ elt = self.make_elt()
+ for i in self.elements:
+ self.make_b64elt(elt, i, getattr(self, i, None))
+ if isinstance(self.valid_until, int):
+ elt.set("valid_until", self.valid_until.toXMLtime())
+ return elt
+
class list_published_objects_elt(rpki.xml_utils.text_elt, left_right_namespace):
"""
<list_published_objects/> element.
@@ -1069,6 +1150,8 @@ class list_published_objects_elt(rpki.xml_utils.text_elt, left_right_namespace):
for r in ca_detail.roas if r.roa is not None)
r_msg.extend(self.make_reply(g.uri, g.ghostbuster)
for g in ca_detail.ghostbusters)
+ r_msg.extend(self.make_reply(c.uri, c.cert)
+ for c in ca_detail.ee_certificates)
cb()
def make_reply(self, uri, obj, child_handle = None):
@@ -1165,6 +1248,7 @@ class msg(rpki.xml_utils.msg, left_right_namespace):
for x in (self_elt, child_elt, parent_elt, bsc_elt,
repository_elt, list_resources_elt,
list_roa_requests_elt, list_ghostbuster_requests_elt,
+ list_ee_certificate_requests_elt,
list_published_objects_elt,
list_received_resources_elt, report_error_elt))
diff --git a/rpkid/rpki/oids.py b/rpkid/rpki/oids.py
index 094fa1a2..a97df6a7 100644
--- a/rpkid/rpki/oids.py
+++ b/rpkid/rpki/oids.py
@@ -1,140 +1,101 @@
# $Id$
#
-# Copyright (C) 2009--2012 Internet Systems Consortium ("ISC")
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-#
+# Copyright (C) 2013--2014 Dragon Research Labs ("DRL")
+# Portions copyright (C) 2009--2012 Internet Systems Consortium ("ISC")
# Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
+# copyright notices and this permission notice appear in all copies.
#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
+# THE SOFTWARE IS PROVIDED "AS IS" AND DRL, ISC, AND ARIN DISCLAIM ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL,
+# ISC, OR ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""
OID database.
-"""
-
-## @var oid2name
-# Mapping table of OIDs to conventional string names.
-oid2name = {
- (1, 2, 840, 113549, 1, 1, 11) : "sha256WithRSAEncryption",
- (1, 2, 840, 113549, 1, 1, 12) : "sha384WithRSAEncryption",
- (1, 2, 840, 113549, 1, 1, 13) : "sha512WithRSAEncryption",
- (1, 2, 840, 113549, 1, 7, 1) : "id-data",
- (1, 2, 840, 113549, 1, 9, 16) : "id-smime",
- (1, 2, 840, 113549, 1, 9, 16, 1) : "id-ct",
- (1, 2, 840, 113549, 1, 9, 16, 1, 24) : "id-ct-routeOriginAttestation",
- (1, 2, 840, 113549, 1, 9, 16, 1, 26) : "id-ct-rpkiManifest",
- (1, 2, 840, 113549, 1, 9, 16, 1, 28) : "id-ct-xml",
- (1, 2, 840, 113549, 1, 9, 16, 1, 35) : "id-ct-rpkiGhostbusters",
- (1, 3, 6, 1, 5, 5, 7, 1, 1) : "authorityInfoAccess",
- (1, 3, 6, 1, 5, 5, 7, 1, 11) : "subjectInfoAccess",
- (1, 3, 6, 1, 5, 5, 7, 1, 7) : "sbgp-ipAddrBlock",
- (1, 3, 6, 1, 5, 5, 7, 1, 8) : "sbgp-autonomousSysNum",
- (1, 3, 6, 1, 5, 5, 7, 14, 2) : "id-cp-ipAddr-asNumber",
- (1, 3, 6, 1, 5, 5, 7, 48, 2) : "id-ad-caIssuers",
- (1, 3, 6, 1, 5, 5, 7, 48, 5) : "id-ad-caRepository",
- (1, 3, 6, 1, 5, 5, 7, 48, 9) : "id-ad-signedObjectRepository",
- (1, 3, 6, 1, 5, 5, 7, 48, 10) : "id-ad-rpkiManifest",
- (1, 3, 6, 1, 5, 5, 7, 48, 11) : "id-ad-signedObject",
- (2, 16, 840, 1, 101, 3, 4, 2, 1) : "id-sha256",
- (2, 5, 29, 14) : "subjectKeyIdentifier",
- (2, 5, 29, 15) : "keyUsage",
- (2, 5, 29, 19) : "basicConstraints",
- (2, 5, 29, 20) : "cRLNumber",
- (2, 5, 29, 31) : "cRLDistributionPoints",
- (2, 5, 29, 32) : "certificatePolicies",
- (2, 5, 29, 35) : "authorityKeyIdentifier",
- (2, 5, 29, 37) : "extendedKeyUsage",
- (2, 5, 4, 3) : "commonName",
- (2, 5, 4, 5) : "serialNumber",
- (2, 5, 4, 6) : "countryName",
- (2, 5, 4, 7) : "localityName",
- (2, 5, 4, 8) : "stateOrProvinceName",
- (2, 5, 4, 9) : "streetAddress",
- (2, 5, 4, 10) : "organizationName",
- (2, 5, 4, 11) : "organizationalUnitName",
-}
+This used to be fairly complicated, with multiple representations and
+a collection of conversion functions, but now it is very simple:
-## @var name2oid
-# Mapping table of string names to OIDs
+- We represent OIDs as Python strings, holding the dotted-decimal
+ form of an OID. Nothing but decimal digits and "." is legal.
+ This is compatible with the format that rpki.POW uses.
-name2oid = dict((v, k) for k, v in oid2name.items())
+- We define symbols in this module whose values are OIDs.
-def safe_name2oid(name):
- """
- Map name to OID, also parsing numeric (dotted decimal) format.
- """
-
- try:
- return name2oid[name]
- except KeyError:
- fields = name.split(".")
- if all(field.isdigit() for field in fields):
- return tuple(int(field) for field in fields)
- raise
-
-def safe_oid2name(oid):
- """
- Map OID to name. If we have no mapping, generate numeric (dotted
- decimal) format.
- """
-
- try:
- return oid2name[oid]
- except KeyError:
- return oid2dotted(oid)
-
-def oid2dotted(oid):
- """
- Convert OID to numeric (dotted decimal) format.
- """
-
- return ".".join(str(field) for field in oid)
-
-def dotted2oid(dotted):
- """
- Convert dotted decimal format to OID tuple.
- """
-
- fields = dotted.split(".")
- if all(field.isdigit() for field in fields):
- return tuple(int(field) for field in fields)
- raise ValueError("%r is not a dotted decimal OID" % dotted)
+That's pretty much it. There's a bit of code at the end which checks
+the syntax of the defined strings and provides a pretty-print function
+for the rare occasion when we need to print an OID, but other than
+that this is just a collection of symbolic names for text strings.
+"""
-def safe_name2dotted(name):
+ecdsa_with_SHA256 = "1.2.840.10045.4.3.2"
+sha256WithRSAEncryption = "1.2.840.113549.1.1.11"
+sha384WithRSAEncryption = "1.2.840.113549.1.1.12"
+sha512WithRSAEncryption = "1.2.840.113549.1.1.13"
+id_data = "1.2.840.113549.1.7.1"
+id_smime = "1.2.840.113549.1.9.16"
+id_ct = "1.2.840.113549.1.9.16.1"
+id_ct_routeOriginAttestation = "1.2.840.113549.1.9.16.1.24"
+id_ct_rpkiManifest = "1.2.840.113549.1.9.16.1.26"
+id_ct_xml = "1.2.840.113549.1.9.16.1.28"
+id_ct_rpkiGhostbusters = "1.2.840.113549.1.9.16.1.35"
+authorityInfoAccess = "1.3.6.1.5.5.7.1.1"
+sbgp_ipAddrBlock = "1.3.6.1.5.5.7.1.7"
+sbgp_autonomousSysNum = "1.3.6.1.5.5.7.1.8"
+subjectInfoAccess = "1.3.6.1.5.5.7.1.11"
+id_kp_bgpsec_router = "1.3.6.1.5.5.7.3.30"
+id_cp_ipAddr_asNumber = "1.3.6.1.5.5.7.14.2"
+id_ad_caIssuers = "1.3.6.1.5.5.7.48.2"
+id_ad_caRepository = "1.3.6.1.5.5.7.48.5"
+id_ad_signedObjectRepository = "1.3.6.1.5.5.7.48.9"
+id_ad_rpkiManifest = "1.3.6.1.5.5.7.48.10"
+id_ad_signedObject = "1.3.6.1.5.5.7.48.11"
+commonName = "2.5.4.3"
+serialNumber = "2.5.4.5"
+countryName = "2.5.4.6"
+localityName = "2.5.4.7"
+stateOrProvinceName = "2.5.4.8"
+streetAddress = "2.5.4.9"
+organizationName = "2.5.4.10"
+organizationalUnitName = "2.5.4.11"
+subjectKeyIdentifier = "2.5.29.14"
+keyUsage = "2.5.29.15"
+basicConstraints = "2.5.29.19"
+cRLNumber = "2.5.29.20"
+cRLDistributionPoints = "2.5.29.31"
+certificatePolicies = "2.5.29.32"
+authorityKeyIdentifier = "2.5.29.35"
+extendedKeyUsage = "2.5.29.37"
+id_sha256 = "2.16.840.1.101.3.4.2.1"
+
+# Make sure all symbols exported so far look like OIDs, and build a
+# dictionary to use when pretty-printing.
+
+_oid2name = {}
+
+for _sym in dir():
+ if not _sym.startswith("_"):
+ _val = globals()[_sym]
+ if not isinstance(_val, str) or not all(_v.isdigit() for _v in _val.split(".")):
+ raise ValueError("Bad OID definition: %s = %r" % (_sym, _val))
+ _oid2name[_val] = _sym.replace("_", "-")
+
+del _sym
+del _val
+
+def oid2name(oid):
"""
- Convert name to dotted decimal format.
+ Translate an OID into a string suitable for printing.
"""
- return oid2dotted(safe_name2oid(name))
-
-def safe_dotted2name(dotted):
- """
- Convert dotted decimal to name if we know one,
- otherwise just return dotted.
- """
+ if not isinstance(oid, (str, unicode)) or not all(o.isdigit() for o in oid.split(".")):
+ raise ValueError("Parameter does not look like an OID string: " + repr(oid))
- try:
- return oid2name[dotted2oid(dotted)]
- except KeyError:
- return dotted
+ return _oid2name.get(oid, oid)
diff --git a/rpkid/rpki/old_irdbd.py b/rpkid/rpki/old_irdbd.py
index 10796711..41060344 100644
--- a/rpkid/rpki/old_irdbd.py
+++ b/rpkid/rpki/old_irdbd.py
@@ -52,13 +52,18 @@ class main(object):
r_pdu.child_handle = q_pdu.child_handle
self.cur.execute(
- "SELECT registrant_id, valid_until FROM registrant WHERE registry_handle = %s AND registrant_handle = %s",
+ """
+ SELECT registrant_id, valid_until
+ FROM registrant
+ WHERE registry_handle = %s AND registrant_handle = %s
+ """,
(q_pdu.self_handle, q_pdu.child_handle))
if self.cur.rowcount != 1:
- raise rpki.exceptions.NotInDatabase, \
- "This query should have produced a single exact match, something's messed up (rowcount = %d, self_handle = %s, child_handle = %s)" \
- % (self.cur.rowcount, q_pdu.self_handle, q_pdu.child_handle)
+ raise rpki.exceptions.NotInDatabase(
+ "This query should have produced a single exact match, something's messed up"
+ " (rowcount = %d, self_handle = %s, child_handle = %s)"
+ % (self.cur.rowcount, q_pdu.self_handle, q_pdu.child_handle))
registrant_id, valid_until = self.cur.fetchone()
@@ -66,17 +71,29 @@ class main(object):
r_pdu.asn = rpki.resource_set.resource_set_as.from_sql(
self.cur,
- "SELECT start_as, end_as FROM registrant_asn WHERE registrant_id = %s",
+ """
+ SELECT start_as, end_as
+ FROM registrant_asn
+ WHERE registrant_id = %s
+ """,
(registrant_id,))
r_pdu.ipv4 = rpki.resource_set.resource_set_ipv4.from_sql(
self.cur,
- "SELECT start_ip, end_ip FROM registrant_net WHERE registrant_id = %s AND version = 4",
+ """
+ SELECT start_ip, end_ip
+ FROM registrant_net
+ WHERE registrant_id = %s AND version = 4
+ """,
(registrant_id,))
r_pdu.ipv6 = rpki.resource_set.resource_set_ipv6.from_sql(
self.cur,
- "SELECT start_ip, end_ip FROM registrant_net WHERE registrant_id = %s AND version = 6",
+ """
+ SELECT start_ip, end_ip
+ FROM registrant_net
+ WHERE registrant_id = %s AND version = 6
+ """,
(registrant_id,))
r_msg.append(r_pdu)
@@ -85,7 +102,7 @@ class main(object):
def handle_list_roa_requests(self, q_pdu, r_msg):
self.cur.execute(
- "SELECT roa_request_id, asn FROM roa_request WHERE roa_request_handle = %s",
+ "SELECT roa_request_id, asn FROM roa_request WHERE self_handle = %s",
(q_pdu.self_handle,))
for roa_request_id, asn in self.cur.fetchall():
@@ -97,12 +114,20 @@ class main(object):
r_pdu.ipv4 = rpki.resource_set.roa_prefix_set_ipv4.from_sql(
self.cur,
- "SELECT prefix, prefixlen, max_prefixlen FROM roa_request_prefix WHERE roa_request_id = %s AND version = 4",
+ """
+ SELECT prefix, prefixlen, max_prefixlen
+ FROM roa_request_prefix
+ WHERE roa_request_id = %s AND version = 4
+ """,
(roa_request_id,))
r_pdu.ipv6 = rpki.resource_set.roa_prefix_set_ipv6.from_sql(
self.cur,
- "SELECT prefix, prefixlen, max_prefixlen FROM roa_request_prefix WHERE roa_request_id = %s AND version = 6",
+ """
+ SELECT prefix, prefixlen, max_prefixlen
+ FROM roa_request_prefix
+ WHERE roa_request_id = %s AND version = 6
+ """,
(roa_request_id,))
r_msg.append(r_pdu)
@@ -111,7 +136,11 @@ class main(object):
def handle_list_ghostbuster_requests(self, q_pdu, r_msg):
self.cur.execute(
- "SELECT vcard FROM ghostbuster_request WHERE self_handle = %s AND parent_handle = %s",
+ """
+ SELECT vcard
+ FROM ghostbuster_request
+ WHERE self_handle = %s AND parent_handle = %s
+ """,
(q_pdu.self_handle, q_pdu.parent_handle))
vcards = [result[0] for result in self.cur.fetchall()]
@@ -119,7 +148,11 @@ class main(object):
if not vcards:
self.cur.execute(
- "SELECT vcard FROM ghostbuster_request WHERE self_handle = %s AND parent_handle IS NULL",
+ """
+ SELECT vcard
+ FROM ghostbuster_request
+ WHERE self_handle = %s AND parent_handle IS NULL
+ """,
(q_pdu.self_handle,))
vcards = [result[0] for result in self.cur.fetchall()]
@@ -133,11 +166,63 @@ class main(object):
r_msg.append(r_pdu)
- handle_dispatch = {
- rpki.left_right.list_resources_elt : handle_list_resources,
- rpki.left_right.list_roa_requests_elt : handle_list_roa_requests,
- rpki.left_right.list_ghostbuster_requests_elt : handle_list_ghostbuster_requests}
+ def handle_list_ee_certificate_requests(self, q_pdu, r_msg):
+
+ self.cur.execute(
+ """
+ SELECT ee_certificate_id, pkcs10, gski, cn, sn, eku, valid_until
+ FROM ee_certificate
+ WHERE self_handle = %s
+ """,
+ (q_pdu.self_handle,))
+ for ee_certificate_id, pkcs10, gski, cn, sn, eku, valid_until in self.cur.fetchall():
+
+ r_pdu = rpki.left_right.list_ee_certificate_requests_elt()
+ r_pdu.tag = q_pdu.tag
+ r_pdu.self_handle = q_pdu.self_handle
+ r_pdu.valid_until = valid_until.strftime("%Y-%m-%dT%H:%M:%SZ")
+ r_pdu.pkcs10 = rpki.x509.PKCS10(DER = pkcs10)
+ r_pdu.gski = gski
+ r_pdu.cn = cn
+ r_pdu.sn = sn
+ r_pdu.eku = eku
+
+ r_pdu.asn = rpki.resource_set.resource_set_as.from_sql(
+ self.cur,
+ """
+ SELECT start_as, end_as
+ FROM ee_certificate_asn
+ WHERE ee_certificate_id = %s
+ """,
+ (ee_certificate_id,))
+
+ r_pdu.ipv4 = rpki.resource_set.resource_set_ipv4.from_sql(
+ self.cur,
+ """
+ SELECT start_ip, end_ip
+ FROM ee_certificate_net
+ WHERE ee_certificate_id = %s AND version = 4
+ """,
+ (ee_certificate_id,))
+
+ r_pdu.ipv6 = rpki.resource_set.resource_set_ipv6.from_sql(
+ self.cur,
+ """
+ SELECT start_ip, end_ip
+ FROM ee_certificate_net
+ WHERE ee_certificate_id = %s AND version = 6
+ """,
+ (ee_certificate_id,))
+
+ r_msg.append(r_pdu)
+
+
+ handle_dispatch = {
+ rpki.left_right.list_resources_elt : handle_list_resources,
+ rpki.left_right.list_roa_requests_elt : handle_list_roa_requests,
+ rpki.left_right.list_ghostbuster_requests_elt : handle_list_ghostbuster_requests,
+ rpki.left_right.list_ee_certificate_requests_elt : handle_list_ee_certificate_requests }
def handler(self, query, path, cb):
try:
diff --git a/rpkid/rpki/relaxng.py b/rpkid/rpki/relaxng.py
index 962858c7..bc1f57c6 100644
--- a/rpkid/rpki/relaxng.py
+++ b/rpkid/rpki/relaxng.py
@@ -6,40 +6,26 @@ import lxml.etree
## Parsed RelaxNG left_right schema
left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encoding="UTF-8"?>
<!--
- $Id: left-right-schema.rnc 4588 2012-07-06 19:43:56Z sra $
+ $Id: left-right-schema.rnc 5746 2014-04-04 02:00:06Z sra $
- RelaxNG Schema for RPKI left-right protocol.
+ RelaxNG schema for RPKI left-right protocol.
- libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so
- run the compact syntax through trang to get XML syntax.
-
- Copyright (C) 2009-2011 Internet Systems Consortium ("ISC")
+ Copyright (C) 2012- -2014 Dragon Research Labs ("DRL")
+ Portions copyright (C) 2009- -2011 Internet Systems Consortium ("ISC")
+ Portions copyright (C) 2007- -2008 American Registry for Internet Numbers ("ARIN")
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
+ copyright notices and this permission notice appear in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- PERFORMANCE OF THIS SOFTWARE.
-
- Portions copyright (C) 2007-2008 American Registry for Internet Numbers ("ARIN")
-
- Permission to use, copy, modify, and distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- PERFORMANCE OF THIS SOFTWARE.
+ THE SOFTWARE IS PROVIDED "AS IS" AND DRL, ISC, AND ARIN DISCLAIM ALL
+ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL,
+ ISC, OR ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-->
<grammar ns="http://www.hactrn.net/uris/rpki/left-right-spec/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<!-- Top level PDU -->
@@ -93,6 +79,9 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" en
<ref name="list_ghostbuster_requests_query"/>
</define>
<define name="query_elt" combine="choice">
+ <ref name="list_ee_certificate_requests_query"/>
+ </define>
+ <define name="query_elt" combine="choice">
<ref name="list_resources_query"/>
</define>
<define name="query_elt" combine="choice">
@@ -127,6 +116,9 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" en
<ref name="list_ghostbuster_requests_reply"/>
</define>
<define name="reply_elt" combine="choice">
+ <ref name="list_ee_certificate_requests_reply"/>
+ </define>
+ <define name="reply_elt" combine="choice">
<ref name="list_published_objects_reply"/>
</define>
<define name="reply_elt" combine="choice">
@@ -198,7 +190,7 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" en
<define name="object_handle">
<data type="string">
<param name="maxLength">255</param>
- <param name="pattern">[\-_A-Za-z0-9]*</param>
+ <param name="pattern">[\-_A-Za-z0-9]+</param>
</data>
</define>
<!-- URIs -->
@@ -935,6 +927,72 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" en
<data type="string"/>
</element>
</define>
+ <!-- <list_ee_certificate_requests/> element -->
+ <define name="list_ee_certificate_requests_query">
+ <element name="list_ee_certificate_requests">
+ <ref name="tag"/>
+ <ref name="self_handle"/>
+ </element>
+ </define>
+ <define name="list_ee_certificate_requests_reply">
+ <element name="list_ee_certificate_requests">
+ <ref name="tag"/>
+ <ref name="self_handle"/>
+ <attribute name="gski">
+ <data type="token">
+ <param name="minLength">27</param>
+ <param name="maxLength">27</param>
+ </data>
+ </attribute>
+ <attribute name="valid_until">
+ <data type="dateTime">
+ <param name="pattern">.*Z</param>
+ </data>
+ </attribute>
+ <optional>
+ <attribute name="asn">
+ <ref name="asn_list"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ipv4">
+ <ref name="ipv4_list"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="ipv6">
+ <ref name="ipv6_list"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="cn">
+ <data type="string">
+ <param name="maxLength">64</param>
+ <param name="pattern">[\-0-9A-Za-z_ ]+</param>
+ </data>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="sn">
+ <data type="string">
+ <param name="maxLength">64</param>
+ <param name="pattern">[0-9A-Fa-f]+</param>
+ </data>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="eku">
+ <data type="string">
+ <param name="maxLength">512000</param>
+ <param name="pattern">[.,0-9]+</param>
+ </data>
+ </attribute>
+ </optional>
+ <element name="pkcs10">
+ <ref name="base64"/>
+ </element>
+ </element>
+ </define>
<!-- <list_published_objects/> element -->
<define name="list_published_objects_query">
<element name="list_published_objects">
@@ -1031,6 +1089,8 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" en
<!--
Local Variables:
indent-tabs-mode: nil
+ comment-start: "# "
+ comment-start-skip: "#[ \t]*"
End:
-->
'''))
@@ -1039,13 +1099,42 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" en
## Parsed RelaxNG up_down schema
up_down = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encoding="UTF-8"?>
<!--
- $Id: up-down-schema.rnc 3913 2011-07-01 17:04:18Z sra $
+ $Id: up-down-schema.rnc 5748 2014-04-04 16:30:30Z sra $
- RelaxNG Scheme for up-down protocol, extracted from
- draft-ietf-sidr-rescerts-provisioning-10.txt.
+ RelaxNG schema for the up-down protocol, extracted from RFC 6492.
- libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so
- run the compact syntax through trang to get XML syntax.
+ Copyright (c) 2012 IETF Trust and the persons identified as authors
+ of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Internet Society, IETF or IETF Trust, nor the
+ names of specific contributors, may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
-->
<grammar ns="http://www.apnic.net/specs/rescerts/up-down/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<define name="resource_set_as">
@@ -1291,40 +1380,26 @@ up_down = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encod
## Parsed RelaxNG publication schema
publication = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encoding="UTF-8"?>
<!--
- $Id: publication-schema.rnc 4588 2012-07-06 19:43:56Z sra $
-
- RelaxNG Schema for RPKI publication protocol.
-
- libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so
- run the compact syntax through trang to get XML syntax.
-
- Copyright (C) 2009-2010 Internet Systems Consortium ("ISC")
-
- Permission to use, copy, modify, and distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
+ $Id: publication-schema.rnc 5746 2014-04-04 02:00:06Z sra $
- THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- PERFORMANCE OF THIS SOFTWARE.
+ RelaxNG schema for RPKI publication protocol.
- Portions copyright (C) 2007-2008 American Registry for Internet Numbers ("ARIN")
+ Copyright (C) 2012- -2014 Dragon Research Labs ("DRL")
+ Portions copyright (C) 2009- -2011 Internet Systems Consortium ("ISC")
+ Portions copyright (C) 2007- -2008 American Registry for Internet Numbers ("ARIN")
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
+ copyright notices and this permission notice appear in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- PERFORMANCE OF THIS SOFTWARE.
+ THE SOFTWARE IS PROVIDED "AS IS" AND DRL, ISC, AND ARIN DISCLAIM ALL
+ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL,
+ ISC, OR ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-->
<grammar ns="http://www.hactrn.net/uris/rpki/publication-spec/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<!-- Top level PDU -->
@@ -1417,7 +1492,7 @@ publication = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" e
<define name="object_handle">
<data type="string">
<param name="maxLength">255</param>
- <param name="pattern">[\-_A-Za-z0-9/]*</param>
+ <param name="pattern">[\-_A-Za-z0-9/]+</param>
</data>
</define>
<!--
@@ -1873,6 +1948,8 @@ publication = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" e
<!--
Local Variables:
indent-tabs-mode: nil
+ comment-start: "# "
+ comment-start-skip: "#[ \t]*"
End:
-->
'''))
@@ -1881,9 +1958,9 @@ publication = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" e
## Parsed RelaxNG myrpki schema
myrpki = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encoding="UTF-8"?>
<!--
- $Id: myrpki.rnc 4430 2012-04-17 16:00:14Z sra $
+ $Id: myrpki.rnc 5746 2014-04-04 02:00:06Z sra $
- RelaxNG Schema for MyRPKI XML messages.
+ RelaxNG schema for MyRPKI XML messages.
This message protocol is on its way out, as we're in the process of
moving on from the user interface model that produced it, but even
@@ -1919,13 +1996,13 @@ myrpki = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encodi
<define name="object_handle">
<data type="string">
<param name="maxLength">255</param>
- <param name="pattern">[\-_A-Za-z0-9]*</param>
+ <param name="pattern">[\-_A-Za-z0-9]+</param>
</data>
</define>
<define name="pubd_handle">
<data type="string">
<param name="maxLength">255</param>
- <param name="pattern">[\-_A-Za-z0-9/]*</param>
+ <param name="pattern">[\-_A-Za-z0-9/]+</param>
</data>
</define>
<define name="uri">
@@ -1939,19 +2016,19 @@ myrpki = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encodi
<define name="asn_list">
<data type="string">
<param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9]*</param>
+ <param name="pattern">[\-,0-9]+</param>
</data>
</define>
<define name="ipv4_list">
<data type="string">
<param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9/.]*</param>
+ <param name="pattern">[\-,0-9/.]+</param>
</data>
</define>
<define name="ipv6_list">
<data type="string">
<param name="maxLength">512000</param>
- <param name="pattern">[\-,0-9/:a-fA-F]*</param>
+ <param name="pattern">[\-,0-9/:a-fA-F]+</param>
</data>
</define>
<define name="timestamp">
@@ -2254,6 +2331,110 @@ myrpki = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encodi
<!--
Local Variables:
indent-tabs-mode: nil
+ comment-start: "# "
+ comment-start-skip: "#[ \t]*"
+ End:
+-->
+'''))
+
+## @var router_certificate
+## Parsed RelaxNG router_certificate schema
+router_certificate = lxml.etree.RelaxNG(lxml.etree.fromstring(r'''<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ $Id: router-certificate-schema.rnc 5746 2014-04-04 02:00:06Z sra $
+
+ RelaxNG schema for BGPSEC router certificate interchange format.
+
+ At least for now, this is a trivial encapsulation of a PKCS #10
+ request, a set (usually containing exactly one member) of autonomous
+ system numbers, and a router-id. Be warned that this could change
+ radically by the time we have any real operational understanding of
+ how these things will be used, this is just our current best guess
+ to let us move forward on initial coding.
+
+ Copyright (C) 2014 Dragon Research Labs ("DRL")
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND DRL DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL DRL BE LIABLE FOR ANY SPECIAL, DIRECT,
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
+-->
+<grammar ns="http://www.hactrn.net/uris/rpki/router-certificate/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <define name="version">
+ <value>1</value>
+ </define>
+ <define name="base64">
+ <data type="base64Binary">
+ <param name="maxLength">512000</param>
+ </data>
+ </define>
+ <define name="router_id">
+ <data type="unsignedInt"/>
+ </define>
+ <define name="asn_list">
+ <data type="string">
+ <param name="maxLength">512000</param>
+ <param name="pattern">[0-9][\-,0-9]*</param>
+ </data>
+ </define>
+ <define name="timestamp">
+ <data type="dateTime">
+ <param name="pattern">.*Z</param>
+ </data>
+ </define>
+ <!-- Core payload used in this schema. -->
+ <define name="payload">
+ <attribute name="router_id">
+ <ref name="router_id"/>
+ </attribute>
+ <attribute name="asn">
+ <ref name="asn_list"/>
+ </attribute>
+ <optional>
+ <attribute name="valid_until">
+ <ref name="timestamp"/>
+ </attribute>
+ </optional>
+ <ref name="base64"/>
+ </define>
+ <!--
+ We allow two forms, one with a wrapper to allow multiple requests in
+ a single file, one without for brevity; the version attribute goes
+ in the outermost element in either case.
+ -->
+ <start combine="choice">
+ <element name="router_certificate_request">
+ <attribute name="version">
+ <ref name="version"/>
+ </attribute>
+ <ref name="payload"/>
+ </element>
+ </start>
+ <start combine="choice">
+ <element name="router_certificate_requests">
+ <attribute name="version">
+ <ref name="version"/>
+ </attribute>
+ <zeroOrMore>
+ <element name="router_certificate_request">
+ <ref name="payload"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </start>
+</grammar>
+<!--
+ Local Variables:
+ indent-tabs-mode: nil
+ comment-start: "# "
+ comment-start-skip: "#[ \t]*"
End:
-->
'''))
diff --git a/rpkid/rpki/resource_set.py b/rpkid/rpki/resource_set.py
index f78d37fd..2ec19cab 100644
--- a/rpkid/rpki/resource_set.py
+++ b/rpkid/rpki/resource_set.py
@@ -30,7 +30,6 @@ We also provide some basic set operations (union, intersection, etc).
import re
import math
-import rpki.oids
import rpki.exceptions
import rpki.POW
diff --git a/rpkid/rpki/rootd.py b/rpkid/rpki/rootd.py
index 6723813c..43e84873 100644
--- a/rpkid/rpki/rootd.py
+++ b/rpkid/rpki/rootd.py
@@ -37,7 +37,6 @@ import rpki.exceptions
import rpki.relaxng
import rpki.sundial
import rpki.log
-import rpki.oids
import rpki.daemonize
rootd = None
@@ -50,7 +49,7 @@ class list_pdu(rpki.up_down.list_pdu):
class issue_pdu(rpki.up_down.issue_pdu):
def serve_pdu(self, q_msg, r_msg, ignored, callback, errback):
- self.pkcs10.check_valid_rpki()
+ self.pkcs10.check_valid_request_ca()
r_msg.payload = rpki.up_down.issue_response_pdu()
rootd.compose_response(r_msg, self.pkcs10)
callback()
@@ -230,7 +229,7 @@ class main(object):
manifest_keypair = rpki.x509.RSA.generate()
manifest_cert = self.rpki_root_cert.issue(
keypair = self.rpki_root_key,
- subject_key = manifest_keypair.get_RSApublic(),
+ subject_key = manifest_keypair.get_public(),
serial = self.serial_number,
sia = (None, None, self.rpki_base_uri + self.rpki_root_manifest),
aia = self.rpki_root_cert_uri,
diff --git a/rpkid/rpki/rpkic.py b/rpkid/rpki/rpkic.py
index 5914dfc1..d5339f5b 100644
--- a/rpkid/rpki/rpkic.py
+++ b/rpkid/rpki/rpkic.py
@@ -38,7 +38,6 @@ import time
import rpki.config
import rpki.sundial
import rpki.log
-import rpki.oids
import rpki.http
import rpki.resource_set
import rpki.relaxng
@@ -706,6 +705,54 @@ class main(Cmd):
self.zoo.run_rpkid_now()
+ @parsecmd(argsubparsers,
+ cmdarg("--valid_until", help = "override default validity interval"),
+ cmdarg("router_certificate_request_xml", help = "file containing XML router certificate request"))
+ def do_add_router_certificate_request(self, args):
+ """
+ Load router certificate request(s) into IRDB from XML file.
+ """
+
+ self.zoo.add_router_certificate_request(args.router_certificate_request_xml, args.valid_until)
+ if self.autosync:
+ self.zoo.run_rpkid_now()
+
+ @parsecmd(argsubparsers,
+ cmdarg("gski", help = "g(SKI) of router certificate request to delete"))
+ def do_delete_router_certificate_request(self, args):
+ """
+ Delete a router certificate request from the IRDB.
+ """
+
+ try:
+ self.zoo.delete_router_certificate_request(args.gski)
+ if self.autosync:
+ self.zoo.run_rpkid_now()
+ except rpki.irdb.ResourceHolderCA.DoesNotExist:
+ print "No such resource holder \"%s\"" % self.zoo.handle
+ except rpki.irdb.EECertificateRequest.DoesNotExist:
+ print "No certificate request matching g(SKI) \"%s\"" % args.gski
+
+ def complete_delete_router_certificate_request(self, text, line, begidx, endidx):
+ return [obj.gski for obj in self.zoo.resource_ca.ee_certificate_requests.all()
+ if obj.gski and obj.gski.startswith(text)]
+
+
+ @parsecmd(argsubparsers)
+ def do_show_router_certificate_requests(self, args):
+ """
+ Show this entity's router certificate requests.
+ """
+
+ for req in self.zoo.resource_ca.ee_certificate_requests.all():
+ print "%s %s %s %s" % (req.gski, req.valid_until, req.cn, req.sn)
+
+
+ # What about updates? Validity interval, change router-id, change
+ # ASNs. Not sure what this looks like yet, blunder ahead with the
+ # core code while mulling over the UI.
+
+
@parsecmd(argsubparsers)
def do_synchronize(self, args):
"""
@@ -817,3 +864,14 @@ class main(Cmd):
"""
print rpki.version.VERSION
+
+
+ @parsecmd(argsubparsers)
+ def do_list_self_handles(self, args):
+ """
+ List all <self/> handles in this rpkid instance.
+ """
+
+ for ca in rpki.irdb.ResourceHolderCA.objects.all():
+ print ca.handle
+
diff --git a/rpkid/rpki/rpkid.py b/rpkid/rpki/rpkid.py
index cbb36ada..d6163bee 100644
--- a/rpkid/rpki/rpkid.py
+++ b/rpkid/rpki/rpkid.py
@@ -27,6 +27,7 @@ import argparse
import sys
import re
import random
+import base64
import rpki.resource_set
import rpki.up_down
import rpki.left_right
@@ -116,9 +117,14 @@ class main(object):
# Icky hack to let Iain do some testing quickly, should go away
# once we sort out whether we can make this change permanent.
+ #
+ # OK, the stuff to add router certificate support makes enough
+ # other changes that we're going to need a migration program in
+ # any case, so might as well throw the switch here too, or at
+ # least find out if it (still) works as expected.
self.merge_publication_directories = self.cfg.getboolean("merge_publication_directories",
- False)
+ True)
self.use_internal_cron = self.cfg.getboolean("use-internal-cron", True)
@@ -249,6 +255,18 @@ class main(object):
self.irdb_query(callback, errback, *q_pdus)
+ def irdb_query_ee_certificate_requests(self, self_handle, callback, errback):
+ """
+ Ask IRDB about self's EE certificate requests.
+ """
+
+ rpki.log.trace()
+
+ q_pdu = rpki.left_right.list_ee_certificate_requests_elt()
+ q_pdu.self_handle = self_handle
+
+ self.irdb_query(callback, errback, q_pdu)
+
def left_right_handler(self, query, path, cb):
"""
Process one left-right PDU.
@@ -522,6 +540,7 @@ class ca_obj(rpki.sql.sql_persistent):
sia_uri = self.construct_sia_uri(parent, rc)
sia_uri_changed = self.sia_uri != sia_uri
if sia_uri_changed:
+ rpki.log.debug("SIA changed: was %s now %s" % (self.sia_uri, sia_uri))
self.sia_uri = sia_uri
self.sql_mark_dirty()
@@ -544,6 +563,11 @@ class ca_obj(rpki.sql.sql_persistent):
else:
+ if ca_detail.state == "active" and ca_detail.ca_cert_uri != rc_cert.cert_url.rsync():
+ rpki.log.debug("AIA changed: was %s now %s" % (ca_detail.ca_cert_uri, rc_cert.cert_url.rsync()))
+ ca_detail.ca_cert_uri = rc_cert.cert_url.rsync()
+ ca_detail.sql_mark_dirty()
+
if ca_detail.state in ("pending", "active"):
if ca_detail.state == "pending":
@@ -570,10 +594,12 @@ class ca_obj(rpki.sql.sql_persistent):
def done():
if cert_map:
- rpki.log.warn("Unknown certificate SKI%s %s in resource class %s in list_response to %s from %s, maybe you want to \"revoke_forgotten\"?"
+ rpki.log.warn("Unknown certificate SKI%s %s in resource class %s in list_response "
+ "to %s from %s, maybe you want to \"revoke_forgotten\"?"
% ("" if len(cert_map) == 1 else "s",
", ".join(c.cert.gSKI() for c in cert_map.values()),
rc.class_name, parent.self.self_handle, parent.parent_handle))
+ self.gctx.sql.sweep()
self.gctx.checkpoint()
cb()
@@ -598,7 +624,8 @@ class ca_obj(rpki.sql.sql_persistent):
if ca_details:
rpki.async.iterator(ca_details, loop, done)
else:
- rpki.log.warn("Existing resource class %s to %s from %s with no certificates, rekeying" % (rc.class_name, parent.self.self_handle, parent.parent_handle))
+ rpki.log.warn("Existing resource class %s to %s from %s with no certificates, rekeying" %
+ (rc.class_name, parent.self.self_handle, parent.parent_handle))
self.gctx.checkpoint()
self.rekey(cb, eb)
@@ -748,10 +775,10 @@ class ca_detail_obj(rpki.sql.sql_persistent):
"ca_detail",
"ca_detail_id",
("private_key_id", rpki.x509.RSA),
- ("public_key", rpki.x509.RSApublic),
+ ("public_key", rpki.x509.PublicKey),
("latest_ca_cert", rpki.x509.X509),
("manifest_private_key_id", rpki.x509.RSA),
- ("manifest_public_key", rpki.x509.RSApublic),
+ ("manifest_public_key", rpki.x509.PublicKey),
("latest_manifest_cert", rpki.x509.X509),
("latest_manifest", rpki.x509.SignedManifest),
("latest_crl", rpki.x509.CRL),
@@ -835,6 +862,13 @@ class ca_detail_obj(rpki.sql.sql_persistent):
"""
return rpki.rpkid.ghostbuster_obj.sql_fetch_where(self.gctx, "ca_detail_id = %s", (self.ca_detail_id,))
+ @property
+ def ee_certificates(self):
+ """
+ Fetch all EE certificate objects that link to this ca_detail.
+ """
+ return rpki.rpkid.ee_cert_obj.sql_fetch_where(self.gctx, "ca_detail_id = %s", (self.ca_detail_id,))
+
def unpublished_ghostbusters(self, when):
"""
Fetch all unpublished Ghostbusters objects linked to this
@@ -869,6 +903,15 @@ class ca_detail_obj(rpki.sql.sql_persistent):
"""
return self.latest_ca_cert.getNotAfter() <= rpki.sundial.now()
+ def covers(self, target):
+ """
+ Test whether this ca-detail covers a given set of resources.
+ """
+
+ assert not target.asn.inherit and not target.v4.inherit and not target.v6.inherit
+ me = self.latest_ca_cert.get_3779resources()
+ return target.asn <= me.asn and target.v4 <= me.v4 and target.v6 <= me.v6
+
def activate(self, ca, cert, uri, callback, errback, predecessor = None):
"""
Activate this ca_detail.
@@ -1082,19 +1125,23 @@ class ca_detail_obj(rpki.sql.sql_persistent):
self.state = "pending"
self.private_key_id = rpki.x509.RSA.generate()
- self.public_key = self.private_key_id.get_RSApublic()
+ self.public_key = self.private_key_id.get_public()
self.manifest_private_key_id = rpki.x509.RSA.generate()
- self.manifest_public_key = self.manifest_private_key_id.get_RSApublic()
+ self.manifest_public_key = self.manifest_private_key_id.get_public()
self.sql_store()
return self
- def issue_ee(self, ca, resources, subject_key, sia):
+ def issue_ee(self, ca, resources, subject_key, sia,
+ cn = None, sn = None, notAfter = None, eku = None):
"""
Issue a new EE certificate.
"""
+ if notAfter is None:
+ notAfter = self.latest_ca_cert.getNotAfter()
+
return self.latest_ca_cert.issue(
keypair = self.private_key_id,
subject_key = subject_key,
@@ -1103,8 +1150,11 @@ class ca_detail_obj(rpki.sql.sql_persistent):
aia = self.ca_cert_uri,
crldp = self.crl_uri,
resources = resources,
- notAfter = self.latest_ca_cert.getNotAfter(),
- is_ca = False)
+ notAfter = notAfter,
+ is_ca = False,
+ cn = cn,
+ sn = sn,
+ eku = eku)
def generate_manifest_cert(self):
"""
@@ -1200,8 +1250,12 @@ class ca_detail_obj(rpki.sql.sql_persistent):
self.crl_published = rpki.sundial.now()
self.sql_mark_dirty()
- publisher.publish(cls = rpki.publication.crl_elt, uri = self.crl_uri, obj = self.latest_crl, repository = parent.repository,
- handler = self.crl_published_callback)
+ publisher.publish(
+ cls = rpki.publication.crl_elt,
+ uri = self.crl_uri,
+ obj = self.latest_crl,
+ repository = parent.repository,
+ handler = self.crl_published_callback)
def crl_published_callback(self, pdu):
"""
@@ -1238,6 +1292,7 @@ class ca_detail_obj(rpki.sql.sql_persistent):
objs.extend((c.uri_tail, c.cert) for c in self.child_certs)
objs.extend((r.uri_tail, r.roa) for r in self.roas if r.roa is not None)
objs.extend((g.uri_tail, g.ghostbuster) for g in self.ghostbusters)
+ objs.extend((e.uri_tail, e.cert) for e in self.ee_certificates)
rpki.log.debug("Building manifest object %s" % uri)
self.latest_manifest = rpki.x509.SignedManifest.build(
@@ -1277,6 +1332,8 @@ class ca_detail_obj(rpki.sql.sql_persistent):
roa.regenerate(publisher, fast = True)
for ghostbuster in self.ghostbusters:
ghostbuster.regenerate(publisher, fast = True)
+ for ee_certificate in self.ee_certificates:
+ ee_certificate.reissue(publisher, force = True)
for child_cert in self.child_certs:
child_cert.reissue(self, publisher, force = True)
self.gctx.sql.sweep()
@@ -1451,7 +1508,11 @@ class child_cert_obj(rpki.sql.sql_persistent):
ca = ca_detail.ca
rpki.log.debug("Revoking %r %r" % (self, self.uri))
revoked_cert_obj.revoke(cert = self.cert, ca_detail = ca_detail)
- publisher.withdraw(cls = rpki.publication.certificate_elt, uri = self.uri, obj = self.cert, repository = ca.parent.repository)
+ publisher.withdraw(
+ cls = rpki.publication.certificate_elt,
+ uri = self.uri,
+ obj = self.cert,
+ repository = ca.parent.repository)
self.gctx.sql.sweep()
self.sql_delete()
if generate_crl_and_manifest:
@@ -1473,6 +1534,7 @@ class child_cert_obj(rpki.sql.sql_persistent):
old_resources = self.cert.get_3779resources()
old_sia = self.cert.get_SIA()
+ old_aia = self.cert.get_AIA()[0]
old_ca_detail = self.ca_detail
needed = False
@@ -1490,7 +1552,8 @@ class child_cert_obj(rpki.sql.sql_persistent):
needed = True
if resources.valid_until != old_resources.valid_until:
- rpki.log.debug("Validity changed for %r: old %s new %s" % (self, old_resources.valid_until, resources.valid_until))
+ rpki.log.debug("Validity changed for %r: old %s new %s" % (
+ self, old_resources.valid_until, resources.valid_until))
needed = True
if sia != old_sia:
@@ -1498,7 +1561,11 @@ class child_cert_obj(rpki.sql.sql_persistent):
needed = True
if ca_detail != old_ca_detail:
- rpki.log.debug("Issuer changed for %r %s: old %r new %r" % (self, self.uri, old_ca_detail, ca_detail))
+ rpki.log.debug("Issuer changed for %r: old %r new %r" % (self, old_ca_detail, ca_detail))
+ needed = True
+
+ if ca_detail.ca_cert_uri != old_aia:
+ rpki.log.debug("AIA changed for %r: old %r new %r" % (self, old_aia, ca_detail.ca_cert_uri))
needed = True
must_revoke = old_resources.oversized(resources) or old_resources.valid_until > resources.valid_until
@@ -1765,6 +1832,10 @@ class roa_obj(rpki.sql.sql_persistent):
rpki.log.debug("%r resources do not match EE, regenerating" % self)
return self.regenerate(publisher = publisher, fast = fast)
+ if self.cert.get_AIA()[0] != ca_detail.ca_cert_uri:
+ rpki.log.debug("%r AIA changed, regenerating" % self)
+ return self.regenerate(publisher = publisher, fast = fast)
+
def generate(self, publisher, fast = False):
"""
Generate a ROA.
@@ -1829,7 +1900,7 @@ class roa_obj(rpki.sql.sql_persistent):
self.cert = ca_detail.issue_ee(
ca = ca,
resources = resources,
- subject_key = keypair.get_RSApublic(),
+ subject_key = keypair.get_public(),
sia = (None, None, self.uri_from_key(keypair)))
self.roa = rpki.x509.ROA.build(self.asn, self.ipv4, self.ipv6, keypair, (self.cert,))
self.published = rpki.sundial.now()
@@ -2001,6 +2072,10 @@ class ghostbuster_obj(rpki.sql.sql_persistent):
rpki.log.debug("%r past threshold %s, regenerating" % (self, regen_time))
return self.regenerate(publisher = publisher, fast = fast)
+ if self.cert.get_AIA()[0] != self.ca_detail.ca_cert_uri:
+ rpki.log.debug("%r AIA changed, regenerating" % self)
+ return self.regenerate(publisher = publisher, fast = fast)
+
def generate(self, publisher, fast = False):
"""
Generate a Ghostbuster record
@@ -2024,7 +2099,7 @@ class ghostbuster_obj(rpki.sql.sql_persistent):
self.cert = ca_detail.issue_ee(
ca = ca,
resources = resources,
- subject_key = keypair.get_RSApublic(),
+ subject_key = keypair.get_public(),
sia = (None, None, self.uri_from_key(keypair)))
self.ghostbuster = rpki.x509.Ghostbuster.build(self.vcard, keypair, (self.cert,))
self.published = rpki.sundial.now()
@@ -2121,6 +2196,247 @@ class ghostbuster_obj(rpki.sql.sql_persistent):
return self.cert.gSKI() + ".gbr"
+class ee_cert_obj(rpki.sql.sql_persistent):
+ """
+ EE certificate (router certificate or generic).
+ """
+
+ sql_template = rpki.sql.template(
+ "ee_cert",
+ "ee_cert_id",
+ "self_id",
+ "ca_detail_id",
+ "ski",
+ ("cert", rpki.x509.X509),
+ ("published", rpki.sundial.datetime))
+
+ def __repr__(self):
+ return rpki.log.log_repr(self, self.cert.getSubject(), self.uri)
+
+ def __init__(self, gctx = None, self_id = None, ca_detail_id = None, cert = None):
+ rpki.sql.sql_persistent.__init__(self)
+ self.gctx = gctx
+ self.self_id = self_id
+ self.ca_detail_id = ca_detail_id
+ self.cert = cert
+ self.ski = None if cert is None else cert.get_SKI()
+ self.published = None
+ if self_id or ca_detail_id or cert:
+ self.sql_mark_dirty()
+
+ @property
+ @rpki.sql.cache_reference
+ def self(self):
+ """
+ Fetch self object to which this ee_cert_obj links.
+ """
+ return rpki.left_right.self_elt.sql_fetch(self.gctx, self.self_id)
+
+ @property
+ @rpki.sql.cache_reference
+ def ca_detail(self):
+ """
+ Fetch ca_detail object to which this ee_cert_obj links.
+ """
+ return rpki.rpkid.ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
+
+ @ca_detail.deleter
+ def ca_detail(self):
+ try:
+ del self._ca_detail
+ except AttributeError:
+ pass
+
+ @property
+ def gski(self):
+ """
+ Calculate g(SKI), for ease of comparison with XML.
+
+ Although, really, one has to ask why we don't just store g(SKI)
+ in rpkid.sql instead of ski....
+ """
+ return base64.urlsafe_b64encode(self.ski).rstrip("=")
+
+ @gski.setter
+ def gski(self, val):
+ self.ski = base64.urlsafe_b64decode(s + ("=" * ((4 - len(s)) % 4)))
+
+ @property
+ def uri(self):
+ """
+ Return the publication URI for this ee_cert_obj.
+ """
+ return self.ca_detail.ca.sia_uri + self.uri_tail
+
+ @property
+ def uri_tail(self):
+ """
+ Return the tail (filename portion) of the publication URI for this
+ ee_cert_obj.
+ """
+ return self.cert.gSKI() + ".cer"
+
+ @classmethod
+ def create(cls, ca_detail, subject_name, subject_key, resources, publisher, eku = None):
+ """
+ Generate a new certificate and stuff it in a new ee_cert_obj.
+ """
+
+ cn, sn = subject_name.extract_cn_and_sn()
+ ca = ca_detail.ca
+
+ cert = ca_detail.issue_ee(
+ ca = ca,
+ subject_key = subject_key,
+ sia = None,
+ resources = resources,
+ notAfter = resources.valid_until,
+ cn = cn,
+ sn = sn,
+ eku = eku)
+
+ self = cls(
+ gctx = ca_detail.gctx,
+ self_id = ca.parent.self.self_id,
+ ca_detail_id = ca_detail.ca_detail_id,
+ cert = cert)
+
+ publisher.publish(
+ cls = rpki.publication.certificate_elt,
+ uri = self.uri,
+ obj = self.cert,
+ repository = ca.parent.repository,
+ handler = self.published_callback)
+
+ self.sql_store()
+
+ ca_detail.generate_manifest(publisher = publisher)
+
+ rpki.log.debug("New ee_cert %r" % self)
+
+ return self
+
+ def revoke(self, publisher, generate_crl_and_manifest = True):
+ """
+ Revoke and withdraw an EE certificate.
+ """
+
+ ca_detail = self.ca_detail
+ ca = ca_detail.ca
+ rpki.log.debug("Revoking %r %r" % (self, self.uri))
+ revoked_cert_obj.revoke(cert = self.cert, ca_detail = ca_detail)
+ publisher.withdraw(cls = rpki.publication.certificate_elt,
+ uri = self.uri,
+ obj = self.cert,
+ repository = ca.parent.repository)
+ self.gctx.sql.sweep()
+ self.sql_delete()
+ if generate_crl_and_manifest:
+ ca_detail.generate_crl(publisher = publisher)
+ ca_detail.generate_manifest(publisher = publisher)
+
+ def reissue(self, publisher, ca_detail = None, resources = None, force = False):
+ """
+ Reissue an existing EE cert, reusing the public key. If the EE
+ cert we would generate is identical to the one we already have, we
+ just return; if we need to reissue, we reuse this ee_cert_obj and
+ just update its contents, as the publication URI will not have
+ changed.
+ """
+
+ needed = False
+
+ old_cert = self.cert
+
+ old_ca_detail = self.ca_detail
+ if ca_detail is None:
+ ca_detail = old_ca_detail
+
+ assert ca_detail.ca is old_ca_detail.ca
+
+ old_resources = old_cert.get_3779resources()
+ if resources is None:
+ resources = old_resources
+
+ assert resources.valid_until is not None and old_resources.valid_until is not None
+
+ assert ca_detail.covers(resources)
+
+ if ca_detail != self.ca_detail:
+ rpki.log.debug("ca_detail changed for %r: old %r new %r" % (
+ self, self.ca_detail, ca_detail))
+ needed = True
+
+ if ca_detail.ca_cert_uri != old_cert.get_AIA()[0]:
+ rpki.log.debug("AIA changed for %r: old %s new %s" % (
+ self, old_cert.get_AIA()[0], ca_detail.ca_cert_uri))
+ needed = True
+
+ if resources.valid_until != old_resources.valid_until:
+ rpki.log.debug("Validity changed for %r: old %s new %s" % (
+ self, old_resources.valid_until, resources.valid_until))
+ needed = True
+
+ if resources.asn != old_resources.asn or resources.v4 != old_resources.v4 or resources.v6 != old_resources.v6:
+ rpki.log.debug("Resources changed for %r: old %s new %s" % (
+ self, old_resources, resources))
+ needed = True
+
+ must_revoke = (old_resources.oversized(resources) or
+ old_resources.valid_until > resources.valid_until)
+ if must_revoke:
+ rpki.log.debug("Must revoke existing cert(s) for %r" % self)
+ needed = True
+
+ if not needed and force:
+ rpki.log.debug("No change needed for %r, forcing reissuance anyway" % self)
+ needed = True
+
+ if not needed:
+ rpki.log.debug("No change to %r" % self)
+ return
+
+ cn, sn = self.cert.getSubject().extract_cn_and_sn()
+
+ self.cert = ca_detail.issue_ee(
+ ca = ca_detail.ca,
+ subject_key = self.cert.getPublicKey(),
+ eku = self.cert.get_EKU(),
+ sia = None,
+ resources = resources,
+ notAfter = resources.valid_until,
+ cn = cn,
+ sn = sn)
+
+ self.sql_mark_dirty()
+
+ publisher.publish(
+ cls = rpki.publication.certificate_elt,
+ uri = self.uri,
+ obj = self.cert,
+ repository = ca_detail.ca.parent.repository,
+ handler = self.published_callback)
+
+ if must_revoke:
+ revoked_cert_obj.revoke(cert = old_cert.cert, ca_detail = old_ca_detail)
+
+ self.gctx.sql.sweep()
+
+ if must_revoke:
+ ca_detail.generate_crl(publisher = publisher)
+ self.gctx.sql.sweep()
+
+ ca_detail.generate_manifest(publisher = publisher)
+
+ def published_callback(self, pdu):
+ """
+ Publication callback: check result and mark published.
+ """
+ pdu.raise_if_error()
+ self.published = None
+ self.sql_mark_dirty()
+
+
class publication_queue(object):
"""
Utility to simplify publication from within rpkid.
diff --git a/rpkid/rpki/rpkid_tasks.py b/rpkid/rpki/rpkid_tasks.py
index a1657d97..04e1c0df 100644
--- a/rpkid/rpki/rpkid_tasks.py
+++ b/rpkid/rpki/rpkid_tasks.py
@@ -1,17 +1,19 @@
# $Id$
#
-# Copyright (C) 2012-2013 Internet Systems Consortium ("ISC")
+# Copyright (C) 2014 Dragon Research Labs ("DRL")
+# Portions copyright (C) 2012--2013 Internet Systems Consortium ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
+# copyright notices and this permission notice appear in all copies.
#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# THE SOFTWARE IS PROVIDED "AS IS" AND DRL AND ISC DISCLAIM ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL OR
+# ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
"""
@@ -27,6 +29,18 @@ import rpki.sundial
import rpki.publication
import rpki.exceptions
+task_classes = ()
+
+def queue_task(cls):
+ """
+ Class decorator to add a new task class to task_classes.
+ """
+
+ global task_classes
+ task_classes += (cls,)
+ return cls
+
+
class CompletionHandler(object):
"""
Track one or more scheduled rpkid tasks and execute a callback when
@@ -136,6 +150,7 @@ class AbstractTask(object):
pass
+@queue_task
class PollParentTask(AbstractTask):
"""
Run the regular client poll cycle with each of this self's
@@ -203,6 +218,7 @@ class PollParentTask(AbstractTask):
self.parent_iterator()
+@queue_task
class UpdateChildrenTask(AbstractTask):
"""
Check for updated IRDB data for all of this self's children and
@@ -258,6 +274,8 @@ class UpdateChildrenTask(AbstractTask):
if ca_detail.state == "active":
old_resources = child_cert.cert.get_3779resources()
new_resources = old_resources & irdb_resources & ca_detail.latest_ca_cert.get_3779resources()
+ old_aia = child_cert.cert.get_AIA()[0]
+ new_aia = ca_detail.ca_cert_uri
if new_resources.empty():
rpki.log.debug("Resources shrank to the null set, "
@@ -267,9 +285,11 @@ class UpdateChildrenTask(AbstractTask):
ca_detail.generate_crl(publisher = self.publisher)
ca_detail.generate_manifest(publisher = self.publisher)
- elif old_resources != new_resources or (old_resources.valid_until < self.rsn and
- irdb_resources.valid_until > self.now and
- old_resources.valid_until != irdb_resources.valid_until):
+ elif (old_resources != new_resources or
+ old_aia != new_aia or
+ (old_resources.valid_until < self.rsn and
+ irdb_resources.valid_until > self.now and
+ old_resources.valid_until != irdb_resources.valid_until)):
rpki.log.debug("Need to reissue child %s certificate SKI %s" % (
self.child.child_handle, child_cert.cert.gSKI()))
@@ -321,6 +341,7 @@ class UpdateChildrenTask(AbstractTask):
self.exit()
+@queue_task
class UpdateROAsTask(AbstractTask):
"""
Generate or update ROAs for this self.
@@ -450,6 +471,7 @@ class UpdateROAsTask(AbstractTask):
self.exit()
+@queue_task
class UpdateGhostbustersTask(AbstractTask):
"""
Generate or update Ghostbuster records for this self.
@@ -547,6 +569,112 @@ class UpdateGhostbustersTask(AbstractTask):
rpki.log.warn("Could not fetch Ghostbuster record requests for %s, skipping: %s" % (self.self_handle, e))
self.exit()
+
+@queue_task
+class UpdateEECertificatesTask(AbstractTask):
+ """
+ Generate or update EE certificates for this self.
+
+ Not yet sure what kind of scaling constraints this task might have,
+ so keeping it simple for initial version, we can optimize later.
+ """
+
+ def start(self):
+ rpki.log.trace()
+ self.gctx.checkpoint()
+ rpki.log.debug("Self %s[%d] updating EE certificates" % (self.self_handle, self.self_id))
+
+ self.gctx.irdb_query_ee_certificate_requests(self.self_handle,
+ self.got_requests,
+ self.get_requests_failed)
+
+ def got_requests(self, requests):
+
+ try:
+ self.gctx.checkpoint()
+ if self.gctx.sql.dirty:
+ rpki.log.warn("Unexpected dirty SQL cache, flushing")
+ self.gctx.sql.sweep()
+
+ publisher = rpki.rpkid.publication_queue()
+
+ existing = dict()
+ for ee in self.ee_certificates:
+ gski = ee.gski
+ if gski not in existing:
+ existing[gski] = set()
+ existing[gski].add(ee)
+
+ ca_details = set()
+
+ for req in requests:
+ ees = existing.pop(req.gski, ())
+ resources = rpki.resource_set.resource_bag(
+ asn = req.asn,
+ v4 = req.ipv4,
+ v6 = req.ipv6,
+ valid_until = req.valid_until)
+ covering = self.find_covering_ca_details(resources)
+ ca_details.update(covering)
+
+ for ee in ees:
+ if ee.ca_detail in covering:
+ rpki.log.debug("Updating existing EE certificate for %s %s" % (req.gski, resources))
+ ee.reissue(
+ resources = resources,
+ publisher = publisher)
+ covering.remove(ee.ca_detail)
+ else:
+ rpki.log.debug("Existing EE certificate for %s %s is no longer covered" % (req.gski, resources))
+ ee.revoke(publisher = publisher)
+
+ for ca_detail in covering:
+ rpki.log.debug("No existing EE certificate for %s %s" % (req.gski, resources))
+ rpki.rpkid.ee_cert_obj.create(
+ ca_detail = ca_detail,
+ subject_name = rpki.x509.X501DN.from_cn(req.cn, req.sn),
+ subject_key = req.pkcs10.getPublicKey(),
+ resources = resources,
+ publisher = publisher,
+ eku = req.eku or None)
+
+ # Anything left is an orphan
+ for ees in existing.values():
+ for ee in ees:
+ ca_details.add(ee.ca_detail)
+ ee.revoke(publisher = publisher)
+
+ self.gctx.sql.sweep()
+
+ for ca_detail in ca_details:
+ ca_detail.generate_crl(publisher = publisher)
+ ca_detail.generate_manifest(publisher = publisher)
+
+ self.gctx.sql.sweep()
+
+ self.gctx.checkpoint()
+ publisher.call_pubd(self.exit, self.publication_failed)
+
+ except (SystemExit, rpki.async.ExitNow):
+ raise
+ except Exception, e:
+ rpki.log.traceback()
+ rpki.log.warn("Could not update EE certificates for %s, skipping: %s" % (self.self_handle, e))
+ self.exit()
+
+ def publication_failed(self, e):
+ rpki.log.traceback()
+ rpki.log.warn("Couldn't publish EE certificate updates for %s, skipping: %s" % (self.self_handle, e))
+ self.gctx.checkpoint()
+ self.exit()
+
+ def get_requests_failed(self, e):
+ rpki.log.traceback()
+ rpki.log.warn("Could not fetch EE certificate requests for %s, skipping: %s" % (self.self_handle, e))
+ self.exit()
+
+
+@queue_task
class RegenerateCRLsAndManifestsTask(AbstractTask):
"""
Generate new CRLs and manifests as necessary for all of this self's
@@ -595,6 +723,8 @@ class RegenerateCRLsAndManifestsTask(AbstractTask):
self.gctx.checkpoint()
self.exit()
+
+@queue_task
class CheckFailedPublication(AbstractTask):
"""
Periodic check for objects we tried to publish but failed (eg, due
diff --git a/rpkid/rpki/sql_schemas.py b/rpkid/rpki/sql_schemas.py
index e7c65299..9a82f0ef 100644
--- a/rpkid/rpki/sql_schemas.py
+++ b/rpkid/rpki/sql_schemas.py
@@ -37,6 +37,7 @@ rpkid = '''-- $Id: rpkid.sql 3745 2011-03-27 00:21:57Z sra $
-- DROP TABLE commands must be in correct (reverse dependency) order
-- to satisfy FOREIGN KEY constraints.
+DROP TABLE IF EXISTS ee_cert;
DROP TABLE IF EXISTS ghostbuster;
DROP TABLE IF EXISTS roa_prefix;
DROP TABLE IF EXISTS roa;
@@ -234,6 +235,20 @@ CREATE TABLE ghostbuster (
FOREIGN KEY (ca_detail_id) REFERENCES ca_detail (ca_detail_id) ON DELETE CASCADE
) ENGINE=InnoDB;
+CREATE TABLE ee_cert (
+ ee_cert_id SERIAL NOT NULL,
+ ski BINARY(20) NOT NULL,
+ cert LONGBLOB NOT NULL,
+ published DATETIME,
+ self_id BIGINT UNSIGNED NOT NULL,
+ ca_detail_id BIGINT UNSIGNED NOT NULL,
+ PRIMARY KEY (ee_cert_id),
+ CONSTRAINT ee_cert_self_id
+ FOREIGN KEY (self_id) REFERENCES self (self_id) ON DELETE CASCADE,
+ CONSTRAINT ee_cert_ca_detail_id
+ FOREIGN KEY (ca_detail_id) REFERENCES ca_detail (ca_detail_id) ON DELETE CASCADE
+) ENGINE=InnoDB;
+
-- Local Variables:
-- indent-tabs-mode: nil
-- End:
diff --git a/rpkid/rpki/up_down.py b/rpkid/rpki/up_down.py
index 6d1fa45a..d2ad85d3 100644
--- a/rpkid/rpki/up_down.py
+++ b/rpkid/rpki/up_down.py
@@ -365,7 +365,7 @@ class issue_pdu(base_elt):
raise rpki.exceptions.NotImplementedYet("req_* attributes not implemented yet, sorry")
# Check the request
- self.pkcs10.check_valid_rpki()
+ self.pkcs10.check_valid_request_ca()
ca = child.ca_from_class_name(self.class_name)
ca_detail = ca.active_ca_detail
if ca_detail is None:
diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py
index 5475a452..fb1a5a2b 100644
--- a/rpkid/rpki/x509.py
+++ b/rpkid/rpki/x509.py
@@ -78,20 +78,6 @@ def first_rsync_uri(xia):
return uri
return None
-def _find_xia_uri(extension, name):
- """
- Find a rsync URI in an SIA or AIA extension.
- Returns the URI if found, otherwise None.
- """
- oid = rpki.oids.name2oid[name]
-
- # extension may be None if the AIA is not present
- if extension:
- for method, location in extension:
- if method == oid and location[0] == "uri" and location[1].startswith("rsync://"):
- return location[1]
- return None
-
class X501DN(object):
"""
Class to hold an X.501 Distinguished Name.
@@ -126,7 +112,7 @@ class X501DN(object):
"""
def __str__(self):
- return "".join("/" + "+".join("%s=%s" % (rpki.oids.safe_dotted2name(a[0]), a[1])
+ return "".join("/" + "+".join("%s=%s" % (rpki.oids.oid2name(a[0]), a[1])
for a in rdn)
for rdn in self.dn)
@@ -145,10 +131,18 @@ class X501DN(object):
rpki.log.debug("++ %r %r" % (self, self.dn))
@classmethod
- def from_cn(cls, s):
- assert isinstance(s, (str, unicode))
+ def from_cn(cls, cn, sn = None):
+ assert isinstance(cn, (str, unicode))
+ if isinstance(sn, (int, long)):
+ sn = "%08X" % sn
+ elif isinstance(sn, (str, unicode)):
+ assert all(c in "0123456789abcdefABCDEF" for c in sn)
+ sn = str(sn)
self = cls()
- self.dn = (((rpki.oids.safe_name2dotted("commonName"), s),),)
+ if sn is not None:
+ self.dn = (((rpki.oids.commonName, cn),), ((rpki.oids.serialNumber, sn),))
+ else:
+ self.dn = (((rpki.oids.commonName, cn),),)
return self
@classmethod
@@ -161,6 +155,28 @@ class X501DN(object):
def get_POW(self):
return self.dn
+ def extract_cn_and_sn(self):
+ cn = None
+ sn = None
+
+ for rdn in self.dn:
+ if len(rdn) == 1 and len(rdn[0]) == 2:
+ oid = rdn[0][0]
+ val = rdn[0][1]
+ if oid == rpki.oids.commonName and cn is None:
+ cn = val
+ continue
+ if oid == rpki.oids.serialNumber and sn is None:
+ sn = val
+ continue
+ raise rpki.exceptions.BadX510DN("Bad subject name: %s" % (self.dn,))
+
+ if cn is None:
+ raise rpki.exceptions.BadX510DN("Subject name is missing CN: %s" % (self.dn,))
+
+ return cn, sn
+
+
class DER_object(object):
"""
Virtual class to hold a generic DER object.
@@ -300,14 +316,26 @@ class DER_object(object):
def get_DER(self):
"""
Get the DER value of this object.
-
- Subclasses will almost certainly override this method.
+ Subclasses may need to override this method.
"""
self.check()
if self.DER:
return self.DER
+ if self.POW:
+ self.DER = self.POW.derWrite()
+ return self.get_DER()
raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available")
+ def get_POW(self):
+ """
+ Get the rpki.POW value of this object.
+ Subclasses may need to override this method.
+ """
+ self.check()
+ if not self.POW: # pylint: disable=E0203
+ self.POW = self.POW_class.derRead(self.get_DER())
+ return self.POW
+
def get_Base64(self):
"""
Get the Base64 encoding of the DER value of this object.
@@ -367,18 +395,22 @@ class DER_object(object):
def get_AKI(self):
"""
- Get the AKI extension from this object. Only works for subclasses
- that support getExtension().
+ Get the AKI extension from this object, if supported.
"""
return self.get_POW().getAKI()
def get_SKI(self):
"""
- Get the SKI extension from this object. Only works for subclasses
- that support getExtension().
+ Get the SKI extension from this object, if supported.
"""
return self.get_POW().getSKI()
+ def get_EKU(self):
+ """
+ Get the Extended Key Usage extension from this object, if supported.
+ """
+ return self.get_POW().getEKU()
+
def get_SIA(self):
"""
Get the SIA extension from this object. Only works for subclasses
@@ -524,27 +556,6 @@ class X509(DER_object):
POW_class = rpki.POW.X509
- def get_DER(self):
- """
- Get the DER value of this certificate.
- """
- self.check()
- if self.DER:
- return self.DER
- if self.POW:
- self.DER = self.POW.derWrite()
- return self.get_DER()
- raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available")
-
- def get_POW(self):
- """
- Get the rpki.POW value of this certificate.
- """
- self.check()
- if not self.POW: # pylint: disable=E0203
- self.POW = rpki.POW.X509.derRead(self.get_DER())
- return self.POW
-
def getIssuer(self):
"""
Get the issuer of this certificate.
@@ -579,7 +590,7 @@ class X509(DER_object):
"""
Extract the public key from this certificate.
"""
- return RSApublic(POW = self.get_POW().getPublicKey())
+ return PublicKey(POW = self.get_POW().getPublicKey())
def get_SKI(self):
"""
@@ -594,13 +605,16 @@ class X509(DER_object):
return self.getNotAfter() <= rpki.sundial.now()
def issue(self, keypair, subject_key, serial, sia, aia, crldp, notAfter,
- cn = None, resources = None, is_ca = True, notBefore = None):
+ cn = None, resources = None, is_ca = True, notBefore = None,
+ sn = None, eku = None):
"""
Issue an RPKI certificate.
"""
assert aia is not None and crldp is not None
+ assert eku is None or not is_ca
+
return self._issue(
keypair = keypair,
subject_key = subject_key,
@@ -611,15 +625,18 @@ class X509(DER_object):
notBefore = notBefore,
notAfter = notAfter,
cn = cn,
+ sn = sn,
resources = resources,
is_ca = is_ca,
aki = self.get_SKI(),
- issuer_name = self.getSubject())
+ issuer_name = self.getSubject(),
+ eku = eku)
@classmethod
def self_certify(cls, keypair, subject_key, serial, sia, notAfter,
- cn = None, resources = None, notBefore = None):
+ cn = None, resources = None, notBefore = None,
+ sn = None):
"""
Generate a self-certified RPKI certificate.
"""
@@ -639,15 +656,17 @@ class X509(DER_object):
notBefore = notBefore,
notAfter = notAfter,
cn = cn,
+ sn = sn,
resources = resources,
is_ca = True,
aki = ski,
- issuer_name = X501DN.from_cn(cn))
+ issuer_name = X501DN.from_cn(cn, sn),
+ eku = None)
@classmethod
def _issue(cls, keypair, subject_key, serial, sia, aia, crldp, notAfter,
- cn, resources, is_ca, aki, issuer_name, notBefore):
+ cn, sn, resources, is_ca, aki, issuer_name, notBefore, eku):
"""
Common code to issue an RPKI certificate.
"""
@@ -673,13 +692,13 @@ class X509(DER_object):
cert.setVersion(2)
cert.setSerial(serial)
cert.setIssuer(issuer_name.get_POW())
- cert.setSubject(X501DN.from_cn(cn).get_POW())
+ cert.setSubject(X501DN.from_cn(cn, sn).get_POW())
cert.setNotBefore(notBefore)
cert.setNotAfter(notAfter)
cert.setPublicKey(subject_key.get_POW())
cert.setSKI(ski)
cert.setAKI(aki)
- cert.setCertificatePolicies((POWify_OID("id-cp-ipAddr-asNumber"),))
+ cert.setCertificatePolicies((rpki.oids.id_cp_ipAddr_asNumber,))
if crldp is not None:
cert.setCRLDP((crldp,))
@@ -712,6 +731,10 @@ class X509(DER_object):
ipv6 = ("inherit" if resources.v6.inherit else
((r.min, r.max) for r in resources.v6)))
+ if eku is not None:
+ assert not is_ca
+ cert.setEKU(eku)
+
cert.sign(keypair.get_POW(), rpki.POW.SHA256_DIGEST)
return cls(POW = cert)
@@ -741,7 +764,7 @@ class X509(DER_object):
keypair = keypair,
issuer_name = subject_name,
subject_name = subject_name,
- subject_key = keypair.get_RSApublic(),
+ subject_key = keypair.get_public(),
serial = serial,
now = now,
notAfter = notAfter,
@@ -753,7 +776,7 @@ class X509(DER_object):
"""
Issue a normal BPKI certificate.
"""
- assert keypair.get_RSApublic() == self.getPublicKey()
+ assert keypair.get_public() == self.getPublicKey()
return self._bpki_certify(
keypair = keypair,
issuer_name = self.getSubject(),
@@ -777,7 +800,7 @@ class X509(DER_object):
if now is None:
now = rpki.sundial.now()
- issuer_key = keypair.get_RSApublic()
+ issuer_key = keypair.get_public()
assert (issuer_key == subject_key) == (issuer_name == subject_name)
assert is_ca or issuer_name != subject_name
@@ -837,10 +860,11 @@ class PKCS10(DER_object):
## @var allowed_extensions
# Extensions allowed by RPKI profile.
- allowed_extensions = frozenset(rpki.oids.safe_name2dotted(name)
- for name in ("basicConstraints",
- "keyUsage",
- "subjectInfoAccess"))
+ allowed_extensions = frozenset((rpki.oids.basicConstraints,
+ rpki.oids.keyUsage,
+ rpki.oids.subjectInfoAccess,
+ rpki.oids.extendedKeyUsage))
+
def get_DER(self):
"""
@@ -873,91 +897,189 @@ class PKCS10(DER_object):
"""
Extract the public key from this certification request.
"""
- return RSApublic(POW = self.get_POW().getPublicKey())
+ return PublicKey(POW = self.get_POW().getPublicKey())
+
+ def get_SKI(self):
+ """
+ Compute SKI for public key from this certification request.
+ """
+ return self.getPublicKey().get_SKI()
- def check_valid_rpki(self):
+
+ def check_valid_request_common(self):
"""
- Check this certification request to see whether it's a valid
- request for an RPKI certificate. This is broken out of the
- up-down protocol code because it's somewhat involved and the
- up-down code doesn't need to know the details.
+ Common code for checking this certification requests to see
+ whether they conform to the RPKI certificate profile.
Throws an exception if the request isn't valid, so if this method
returns at all, the request is ok.
- At the moment, this only allows requests for CA certificates; as a
- direct consequence, it also rejects ExtendedKeyUsage, because the
- RPKI profile only allows EKU for EE certificates.
+ You probably don't want to call this directly, as it only performs
+ the checks that are common to all RPKI certificates.
"""
if not self.get_POW().verify():
- raise rpki.exceptions.BadPKCS10("Signature check failed")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 signature check failed")
ver = self.get_POW().getVersion()
if ver != 0:
- raise rpki.exceptions.BadPKCS10("Bad version number %s" % ver)
+ raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad version number %s" % ver)
- alg = rpki.oids.safe_dotted2name(self.get_POW().getSignatureAlgorithm())
+ ku = self.get_POW().getKeyUsage()
- if alg != "sha256WithRSAEncryption":
- raise rpki.exceptions.BadPKCS10("Bad signature algorithm %s" % alg)
+ if ku is not None and self.expected_ca_keyUsage != ku:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 keyUsage doesn't match profile: %r" % ku)
- bc = self.get_POW().getBasicConstraints()
-
- if bc is None or not bc[0]:
- raise rpki.exceptions.BadPKCS10("Request for EE certificate not allowed here")
+ forbidden_extensions = self.get_POW().getExtensionOIDs() - self.allowed_extensions
- if bc[1] is not None:
- raise rpki.exceptions.BadPKCS10("basicConstraints must not specify Path Length")
+ if forbidden_extensions:
+ raise rpki.exceptions.BadExtension("Forbidden extension%s in PKCS #10 certificate request: %s" % (
+ "" if len(forbidden_extensions) == 1 else "s",
+ ", ".join(forbidden_extensions)))
- ku = self.get_POW().getKeyUsage()
- if ku is not None and self.expected_ca_keyUsage != ku:
- raise rpki.exceptions.BadPKCS10("keyUsage doesn't match basicConstraints: %r" % ku)
+ def check_valid_request_ca(self):
+ """
+ Check this certification request to see whether it's a valid
+ request for an RPKI CA certificate.
+
+ Throws an exception if the request isn't valid, so if this method
+ returns at all, the request is ok.
+ """
- if any(oid not in self.allowed_extensions
- for oid in self.get_POW().getExtensionOIDs()):
- raise rpki.exceptions.BadExtension("Forbidden extension(s) in certificate request")
+ self.check_valid_request_common()
+ alg = self.get_POW().getSignatureAlgorithm()
+ bc = self.get_POW().getBasicConstraints()
+ eku = self.get_POW().getEKU()
sias = self.get_POW().getSIA()
+ if alg != rpki.oids.sha256WithRSAEncryption:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for CA: %s" % alg)
+
+ if bc is None or not bc[0] or bc[1] is not None:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA bad basicConstraints")
+
+ if eku is not None:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA EKU not allowed")
+
if sias is None:
- raise rpki.exceptions.BadPKCS10("Certificate request is missing SIA extension")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA missing")
caRepository, rpkiManifest, signedObject = sias
if signedObject:
- raise rpki.exceptions.BadPKCS10("CA certificate request has SIA id-ad-signedObject")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must not have id-ad-signedObject")
if not caRepository:
- raise rpki.exceptions.BadPKCS10("Certificate request is missing SIA id-ad-caRepository")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must have id-ad-caRepository")
if not any(uri.startswith("rsync://") for uri in caRepository):
- raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-caRepository contains no rsync URIs")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-caRepository contains no rsync URIs")
+
+ if any(uri.startswith("rsync://") and not uri.endswith("/") for uri in caRepository):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-caRepository does not end with slash")
if not rpkiManifest:
- raise rpki.exceptions.BadPKCS10("Certificate request is missing SIA id-ad-rpkiManifest")
-
- if not any(uri.startswith("rsync://") for uri in rpkiManifest):
- raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-rpkiManifest contains no rsync URIs")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must have id-ad-rpkiManifest")
- if any(uri.startswith("rsync://") and not uri.endswith("/") for uri in caRepository):
- raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-caRepository does not end with slash")
+ if not any(uri.startswith("rsync://") for uri in rpkiManifest):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-rpkiManifest contains no rsync URIs")
if any(uri.startswith("rsync://") and uri.endswith("/") for uri in rpkiManifest):
- raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-rpkiManifest ends with slash")
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-rpkiManifest ends with slash")
+
+
+ def check_valid_request_ee(self):
+ """
+ Check this certification request to see whether it's a valid
+ request for an RPKI EE certificate.
+
+ Throws an exception if the request isn't valid, so if this method
+ returns at all, the request is ok.
+
+ We're a bit less strict here than we are for either CA
+ certificates or BGPSEC router certificates, because the profile is
+ less tightly nailed down for unspecified-use RPKI EE certificates.
+ Future specific purposes may impose tighter constraints.
+
+ Note that this method does NOT apply to so-called "infrastructure"
+ EE certificates (eg, the EE certificates embedded in manifests and
+ ROAs); those are constrained fairly tightly, but they're also
+ generated internally so we don't need to check them as user or
+ protocol input.
+ """
+
+ self.check_valid_request_common()
+
+ alg = self.get_POW().getSignatureAlgorithm()
+ bc = self.get_POW().getBasicConstraints()
+ sia = self.get_POW().getSIA()
+
+ caRepository, rpkiManifest, signedObject = sia or (None, None, None)
+
+ if alg not in (rpki.oids.sha256WithRSAEncryption, rpki.oids.ecdsa_with_SHA256):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for EE: %s" % alg)
+
+ if bc is not None and (bc[0] or bc[1] is not None):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 EE has bad basicConstraints")
+
+ if caRepository:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 EE must not have id-ad-caRepository")
+
+ if rpkiManifest:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 EE must not have id-ad-rpkiManifest")
+
+ if signedObject and not any(uri.startswith("rsync://") for uri in signedObject):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 EE SIA id-ad-signedObject contains no rsync URIs")
+
+
+ def check_valid_request_router(self):
+ """
+ Check this certification request to see whether it's a valid
+ request for a BGPSEC router certificate.
+
+ Throws an exception if the request isn't valid, so if this method
+ returns at all, the request is ok.
+
+ draft-ietf-sidr-bgpsec-pki-profiles 3.2 says follow RFC 6487 3
+ except where explicitly overriden, and does not override for SIA.
+ But draft-ietf-sidr-bgpsec-pki-profiles also says that router
+ certificates don't get SIA, while RFC 6487 requires SIA. So what
+ do we do with SIA in PKCS #10 for router certificates?
+
+ For the moment, ignore it, but make sure we don't include it in
+ the certificate when we get to the code that generates that.
+ """
+
+ self.check_valid_request_ee()
+
+ alg = self.get_POW().getSignatureAlgorithm()
+ eku = self.get_POW().getEKU()
+
+ if alg != rpki.oids.ecdsa_with_SHA256:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for router: %s" % alg)
+
+ # Not really clear to me whether PKCS #10 should have EKU or not, so allow
+ # either, but insist that it be the right one if present.
+
+ if eku is not None and rpki.oids.id_kp_bgpsec_router not in eku:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 router must have EKU")
+
@classmethod
def create(cls, keypair, exts = None, is_ca = False,
- caRepository = None, rpkiManifest = None, signedObject = None):
+ caRepository = None, rpkiManifest = None, signedObject = None,
+ cn = None, sn = None, eku = None):
"""
Create a new request for a given keypair.
"""
assert exts is None, "Old calling sequence to rpki.x509.PKCS10.create()"
- cn = "".join(("%02X" % ord(i) for i in keypair.get_SKI()))
+ if cn is None:
+ cn = "".join(("%02X" % ord(i) for i in keypair.get_SKI()))
if isinstance(caRepository, str):
caRepository = (caRepository,)
@@ -970,7 +1092,7 @@ class PKCS10(DER_object):
req = rpki.POW.PKCS10()
req.setVersion(0)
- req.setSubject(X501DN.from_cn(cn).get_POW())
+ req.setSubject(X501DN.from_cn(cn, sn).get_POW())
req.setPublicKey(keypair.get_POW())
if is_ca:
@@ -980,6 +1102,9 @@ class PKCS10(DER_object):
if caRepository or rpkiManifest or signedObject:
req.setSIA(caRepository, rpkiManifest, signedObject)
+ if eku:
+ req.setEKU(eku)
+
req.sign(keypair.get_POW(), rpki.POW.SHA256_DIGEST)
return cls(POW = req)
@@ -1014,9 +1139,10 @@ class insecure_debug_only_rsa_key_generator(object):
self.keyno += 1
return v
-class RSA(DER_object):
+
+class PrivateKey(DER_object):
"""
- Class to hold an RSA key pair.
+ Class to hold a Public/Private key pair.
"""
POW_class = rpki.POW.Asymmetric
@@ -1055,18 +1181,6 @@ class RSA(DER_object):
assert self.empty()
self.POW = self.POW_class.pemReadPrivate(pem)
- @classmethod
- def generate(cls, keylength = 2048, quiet = False):
- """
- Generate a new keypair.
- """
- if not quiet:
- rpki.log.debug("Generating new %d-bit RSA key" % keylength)
- if generate_insecure_debug_only_rsa_key is not None:
- return cls(POW = generate_insecure_debug_only_rsa_key())
- else:
- return cls(POW = rpki.POW.Asymmetric.generateRSA(keylength))
-
def get_public_DER(self):
"""
Get the DER encoding of the public key from this keypair.
@@ -1079,15 +1193,15 @@ class RSA(DER_object):
"""
return self.get_POW().calculateSKI()
- def get_RSApublic(self):
+ def get_public(self):
"""
- Convert the public key of this keypair into a RSApublic object.
+ Convert the public key of this keypair into a PublicKey object.
"""
- return RSApublic(DER = self.get_public_DER())
+ return PublicKey(DER = self.get_public_DER())
-class RSApublic(DER_object):
+class PublicKey(DER_object):
"""
- Class to hold an RSA public key.
+ Class to hold a public key.
"""
POW_class = rpki.POW.Asymmetric
@@ -1132,22 +1246,63 @@ class RSApublic(DER_object):
"""
return self.get_POW().calculateSKI()
-def POWify_OID(oid):
+class KeyParams(DER_object):
+ """
+ Wrapper for OpenSSL's asymmetric key parameter classes.
+ """
+
+ POW_class = rpki.POW.AsymmetricParams
+
+ @classmethod
+ def generateEC(cls, curve = rpki.POW.EC_P256_CURVE):
+ return cls(POW = rpki.POW.AsymmetricParams.generateEC(curve = curve))
+
+class RSA(PrivateKey):
"""
- Utility function to convert tuple form of an OID to the
- dotted-decimal string form that rpki.POW uses.
+ Class to hold an RSA key pair.
"""
- if isinstance(oid, str):
- return POWify_OID(rpki.oids.name2oid[oid])
- else:
- return ".".join(str(i) for i in oid)
+
+ @classmethod
+ def generate(cls, keylength = 2048, quiet = False):
+ """
+ Generate a new keypair.
+ """
+ if not quiet:
+ rpki.log.debug("Generating new %d-bit RSA key" % keylength)
+ if generate_insecure_debug_only_rsa_key is not None:
+ return cls(POW = generate_insecure_debug_only_rsa_key())
+ else:
+ return cls(POW = rpki.POW.Asymmetric.generateRSA(keylength))
+
+class ECDSA(PrivateKey):
+ """
+ Class to hold an ECDSA key pair.
+ """
+
+ @classmethod
+ def generate(cls, params = None, quiet = False):
+ """
+ Generate a new keypair.
+ """
+
+ if params is None:
+ if not quiet:
+ rpki.log.debug("Generating new ECDSA key parameters")
+ params = KeyParams.generateEC()
+
+ assert isinstance(params, KeyParams)
+
+ if not quiet:
+ rpki.log.debug("Generating new ECDSA key")
+
+ return cls(POW = rpki.POW.Asymmetric.generateFromParams(params.get_POW()))
class CMS_object(DER_object):
"""
Abstract class to hold a CMS object.
"""
- econtent_oid = POWify_OID("id-data")
+ econtent_oid = rpki.oids.id_data
POW_class = rpki.POW.CMS
## @var dump_on_verify_failure
@@ -1492,7 +1647,7 @@ class SignedManifest(DER_CMS_object):
Class to hold a signed manifest.
"""
- econtent_oid = POWify_OID("id-ct-rpkiManifest")
+ econtent_oid = rpki.oids.id_ct_rpkiManifest
POW_class = rpki.POW.Manifest
def getThisUpdate(self):
@@ -1525,7 +1680,7 @@ class SignedManifest(DER_CMS_object):
obj.setManifestNumber(serial)
obj.setThisUpdate(thisUpdate)
obj.setNextUpdate(nextUpdate)
- obj.setAlgorithm(POWify_OID(rpki.oids.name2oid["id-sha256"]))
+ obj.setAlgorithm(rpki.oids.id_sha256)
obj.addFiles(filelist)
self = cls(POW = obj)
@@ -1537,7 +1692,7 @@ class ROA(DER_CMS_object):
Class to hold a signed ROA.
"""
- econtent_oid = POWify_OID("id-ct-routeOriginAttestation")
+ econtent_oid = rpki.oids.id_ct_routeOriginAttestation
POW_class = rpki.POW.ROA
@classmethod
@@ -1614,7 +1769,7 @@ class XML_CMS_object(Wrapped_CMS_object):
Class to hold CMS-wrapped XML protocol data.
"""
- econtent_oid = POWify_OID("id-ct-xml")
+ econtent_oid = rpki.oids.id_ct_xml
## @var dump_outbound_cms
# If set, we write all outbound XML-CMS PDUs to disk, for debugging.
@@ -1755,7 +1910,7 @@ class Ghostbuster(Wrapped_CMS_object):
managed by the back-end.
"""
- econtent_oid = POWify_OID("id-ct-rpkiGhostbusters")
+ econtent_oid = rpki.oids.id_ct_rpkiGhostbusters
def encode(self):
"""