aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2014-01-29 02:32:36 +0000
committerRob Austein <sra@hactrn.net>2014-01-29 02:32:36 +0000
commitf3ee7f51c0a091f244985ae7fd3d975a3259fee9 (patch)
tree4c61669687f037ed125abe55e794188934f20c6b /rpkid/rpki
parent27c6393b76fa9921f0bb1b3977f091579e44872a (diff)
Checkpoint
svn path=/branches/tk671/; revision=5651
Diffstat (limited to 'rpkid/rpki')
-rw-r--r--rpkid/rpki/exceptions.py5
-rw-r--r--rpkid/rpki/irdb/zookeeper.py40
-rw-r--r--rpkid/rpki/oids.py73
-rw-r--r--rpkid/rpki/x509.py77
4 files changed, 125 insertions, 70 deletions
diff --git a/rpkid/rpki/exceptions.py b/rpkid/rpki/exceptions.py
index 7a0eb71c..d8d3774e 100644
--- a/rpkid/rpki/exceptions.py
+++ b/rpkid/rpki/exceptions.py
@@ -360,3 +360,8 @@ 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/zookeeper.py b/rpkid/rpki/irdb/zookeeper.py
index bb52bddd..d2bd0c75 100644
--- a/rpkid/rpki/irdb/zookeeper.py
+++ b/rpkid/rpki/irdb/zookeeper.py
@@ -1589,7 +1589,7 @@ class Zookeeper(object):
@django.db.transaction.commit_on_success
- def add_ee_certificate_request(self, pkcs10, resources):
+ def add_ee_certificate_request(self, pkcs10, resources, kind = "ee"):
"""
Check a PKCS #10 request to see if it complies with the
specification for a RPKI EE certificate; if it does, add an
@@ -1600,7 +1600,7 @@ class Zookeeper(object):
.load_asns() and .load_prefixes() for other strategies.
"""
- pkcs10.check_valid_rpki(ee = True)
+ pkcs10.check_valid_rpki(kind = kind)
ee_request = self.resource_ca.ee_certificate_requests.create(
pkcs10 = pkcs10,
gski = pkcs10.gSKI(),
@@ -1613,28 +1613,40 @@ class Zookeeper(object):
ee_request.address_ranges.create(start_ip = str(range.min), end_ip = str(range.max), version = 6)
- def add_router_certificate_request(self, pkcs10, asn):
+ def add_router_certificate_request(self, pkcs10, *asns):
"""
Check a 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 a specified ASN.
"""
- if isinstance(asn, (str, unicode)):
- asn = long(asn)
- if not isinstance(asn, (int, long)) or asn < 0 or asn > 0xFFFFFFFF:
- raise rpki.exceptions.BadAutonomousSystemNumber("Bad AutonomousSystem number: %s" % asn)
+ asns = tuple(long(a) if isinstance(a, (str, unicode)) else a for a in asns)
+
+ if not asns or not all(isinstance(a, (int, long)) and a >= 0 and a <= 0xFFFFFFFF for a in asns):
+ raise rpki.exceptions.BadAutonomousSystemNumber("Bad AutonomousSystem number%s: %s" % (
+ "" if len(asns) == 1 else "s", ", ".join(repr(a) for a in asns)))
# This attempts to enforce draft-ietf-sidr-bgpsec-pki-profiles-06
# section 3.1.1.1, which may be a mistake, too early to tell.
+ #
+ # Upon further reading: this will have to go somewhere else,
+ # because the combination of draft-ietf-sidr-bgpsec-pki-profiles
+ # and RFC 6487 says that the subject-name-to-be can't be in the
+ # PKCS #10, it has to be carried separately like the ASNs. Save
+ # this code, refactor once I figure out where this belongs.
+
cn, sn = pkcs10.getSubject().extract_cn_and_sn()
- if not cn.startswith("ROUTER-") \
- or len(cn) != 7 + 8 \
- or not cn[7:].isalnum() \
- or int(cn[7:], 16) != asn \
- or not sn.isalnum() \
- or len(sn) != 8 \
- or int(sn, 16) > 0xFFFFFFFF:
+ if (not cn.startswith("ROUTER-") or
+ len(cn) != 7 + 8 or
+ not cn[7:].isalnum() or
+ int(cn[7:], 16) not in asns or
+ not sn.isalnum() or
+ len(sn) != 8 or
+ int(sn, 16) > 0xFFFFFFFF):
raise rpki.exceptions.BadX510DN("Subject name doesn't match router profile: %s" % pkcs10.getSubject())
+ eku = pkcs10.getEKU()
+ if eku is None or rpki.oids.name2oid["id-kp-bgpsec-router"] not in eku:
+ raise rpki.exceptions.WrongEKU("Router certificate EKU not present in request")
+
raise NotImplementedError, "Not finished"
diff --git a/rpkid/rpki/oids.py b/rpkid/rpki/oids.py
index 094fa1a2..1acc8035 100644
--- a/rpkid/rpki/oids.py
+++ b/rpkid/rpki/oids.py
@@ -1,41 +1,61 @@
# $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.
"""
+def defoid(name, *numbers):
+ """
+ Define a new OID, including adding it to a few dictionaries and
+ making an entry for it in the rpki.oids module symbol table, so
+ other code can refer to it as an ordinary symbol.
+ """
+
+ assert all(isinstance(n, (int, long)) for n in numbers)
+
+ dotted = ".".join(str(n) for n in numbers)
+ name_ = name.replace("-", "_")
+
+ assert name_ not in globals()
+
+ global oid2name
+ oid2name[numbers] = name
+
+ globals()[name_] = dotted
+
+ global dotted2name
+ dotted2name[dotted] = name
+
+ global dotted2name_
+ dotted2name_[dotted] = name_
+
+ global name2dotted
+ name2dotted[name] = dotted
+ name2dotted[name_] = dotted
+
+
## @var oid2name
# Mapping table of OIDs to conventional string names.
oid2name = {
+ (1, 2, 840, 10045, 4, 3, 2) : "ecdsa-with-SHA256",
(1, 2, 840, 113549, 1, 1, 11) : "sha256WithRSAEncryption",
(1, 2, 840, 113549, 1, 1, 12) : "sha384WithRSAEncryption",
(1, 2, 840, 113549, 1, 1, 13) : "sha512WithRSAEncryption",
@@ -51,11 +71,12 @@ oid2name = {
(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, 3, 666) : "id-kp-bgpsec-router", # {id-kp, 666} -- Real value not known yet
+ (1, 3, 6, 1, 5, 5, 7, 48, 10) : "id-ad-rpkiManifest",
+ (1, 3, 6, 1, 5, 5, 7, 48, 11) : "id-ad-signedObject",
(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",
@@ -65,14 +86,14 @@ oid2name = {
(2, 5, 29, 32) : "certificatePolicies",
(2, 5, 29, 35) : "authorityKeyIdentifier",
(2, 5, 29, 37) : "extendedKeyUsage",
+ (2, 5, 4, 10) : "organizationName",
+ (2, 5, 4, 11) : "organizationalUnitName",
(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",
}
## @var name2oid
diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py
index 2e09cb35..8d3ea634 100644
--- a/rpkid/rpki/x509.py
+++ b/rpkid/rpki/x509.py
@@ -864,7 +864,8 @@ class PKCS10(DER_object):
allowed_extensions = frozenset(rpki.oids.safe_name2dotted(name)
for name in ("basicConstraints",
"keyUsage",
- "subjectInfoAccess"))
+ "subjectInfoAccess",
+ "extendedKeyUsage"))
def get_DER(self):
"""
@@ -905,7 +906,7 @@ class PKCS10(DER_object):
"""
return self.getPublicKey().get_SKI()
- def check_valid_rpki(self, ee = False):
+ def check_valid_rpki(self, kind = "ca"):
"""
Check this certification request to see whether it's a valid
request for an RPKI certificate. This is broken out of the
@@ -915,13 +916,11 @@ class PKCS10(DER_object):
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.
+ This needs refactoring, as the nested conditionals to handle the
+ different kinds of certificates have gotten rather nasty.
"""
- if ee:
- raise NotImplementedError("Haven't written EE-certificate checking yet, oops")
+ assert kind in ("ca", "ee", "router")
if not self.get_POW().verify():
raise rpki.exceptions.BadPKCS10("PKCS #10 signature check failed")
@@ -933,15 +932,19 @@ class PKCS10(DER_object):
alg = rpki.oids.safe_dotted2name(self.get_POW().getSignatureAlgorithm())
- if alg != "sha256WithRSAEncryption":
+ if alg != ("ecdsa-with-SHA256" if kind == "router" else "sha256WithRSAEncryption"):
raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg)
bc = self.get_POW().getBasicConstraints()
- if bc is None or not bc[0]:
- raise rpki.exceptions.BadPKCS10("Request for EE certificate not allowed here")
-
- if bc[1] is not None:
+ if kind == "ca":
+ if bc is None or not bc[0]:
+ raise rpki.exceptions.BadPKCS10("Request for EE certificate not allowed here")
+ else:
+ if bc is not None and bc[0]:
+ raise rpki.exceptions.BadPKCS10("Request for CA certificate not allowed here")
+
+ if bc is not None and bc[1] is not None:
raise rpki.exceptions.BadPKCS10("PKCS #10 basicConstraints must not specify Path Length")
ku = self.get_POW().getKeyUsage()
@@ -949,37 +952,51 @@ class PKCS10(DER_object):
if ku is not None and self.expected_ca_keyUsage != ku:
raise rpki.exceptions.BadPKCS10("PKCS #10 keyUsage doesn't match basicConstraints: %r" % ku)
+ eku = self.get_POW().getEKU()
+
+ if kind == "ca" and eku is not None:
+ raise rpki.exceptions.BadPKCS10("EKU not allowed in CA certificate PKCS #10")
+ elif kind == "router" and (eku is None or rpki.oids.name2oid["id-kp-bgpsec-router"] not in eku):
+ raise rpki.exceptions.BadPKCS10("EKU required for router certificate PKCS #10")
+
if any(oid not in self.allowed_extensions
for oid in self.get_POW().getExtensionOIDs()):
raise rpki.exceptions.BadExtension("Forbidden extension(s) in PKCS #10 certificate request")
sias = self.get_POW().getSIA()
- if sias is None:
- raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension")
+ if kind == "router":
- caRepository, rpkiManifest, signedObject = sias
+ if sias is not None:
+ raise rpki.exceptions.BadPKCS10("router certificate PKCS #10 must not contain SIA extension")
- if signedObject:
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject")
+ elif kind == "ca":
- if not caRepository:
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-caRepository")
+ if sias is None:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension")
- if not any(uri.startswith("rsync://") for uri in caRepository):
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository contains no rsync URIs")
+ caRepository, rpkiManifest, signedObject = sias
- if not rpkiManifest:
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-rpkiManifest")
-
- if not any(uri.startswith("rsync://") for uri in rpkiManifest):
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-rpkiManifest contains no rsync URIs")
+ if signedObject:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject")
+
+ if not caRepository:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-caRepository")
+
+ if not any(uri.startswith("rsync://") for uri in caRepository):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository contains no rsync URIs")
+
+ if not rpkiManifest:
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-rpkiManifest")
+
+ if not any(uri.startswith("rsync://") for uri in rpkiManifest):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-rpkiManifest contains no rsync URIs")
- if any(uri.startswith("rsync://") and not uri.endswith("/") for uri in caRepository):
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository does not end with slash")
+ if any(uri.startswith("rsync://") and not uri.endswith("/") for uri in caRepository):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository does not end with slash")
- if any(uri.startswith("rsync://") and uri.endswith("/") for uri in rpkiManifest):
- raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-rpkiManifest ends with slash")
+ if any(uri.startswith("rsync://") and uri.endswith("/") for uri in rpkiManifest):
+ raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-rpkiManifest ends with slash")
@classmethod
def create(cls, keypair, exts = None, is_ca = False,