aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki/irdb/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid/rpki/irdb/models.py')
-rw-r--r--rpkid/rpki/irdb/models.py84
1 files changed, 65 insertions, 19 deletions
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py
index e05f3f03..93926bd1 100644
--- a/rpkid/rpki/irdb/models.py
+++ b/rpkid/rpki/irdb/models.py
@@ -76,6 +76,7 @@ class DERField(django.db.models.Field):
def __init__(self, *args, **kwargs):
kwargs["serialize"] = False
kwargs["blank"] = True
+ kwargs["default"] = None
django.db.models.Field.__init__(self, *args, **kwargs)
def db_type(self, connection):
@@ -85,22 +86,18 @@ class DERField(django.db.models.Field):
return "BLOB"
def to_python(self, value):
- if value is None or isinstance(value, self.rpki_type):
- return value
- else:
- assert isinstance(value, str)
+ assert value is None or isinstance(value, (self.rpki_type, str))
+ if isinstance(value, str):
return self.rpki_type(DER = value)
+ else:
+ return value
def get_prep_value(self, value):
+ assert value is None or isinstance(value, (self.rpki_type, str))
if isinstance(value, self.rpki_type):
return value.get_DER()
- elif value is None or isinstance(value, str):
- return value
else:
- import sys
- sys.stderr.write(
- "get_prep_value got %r, expected string or %r\n" % (type(value), self.rpki_type))
- assert isinstance(value, (self.rpki_type, str))
+ return value
class CertificateField(DERField):
description = "X.509 certificate"
@@ -118,6 +115,10 @@ class PKCS10Field(DERField):
description = "PKCS #10 certificate request"
rpki_type = rpki.x509.PKCS10
+## @todo
+# SignedReferral doesn't belong in rpki.irdb, but I haven't yet
+# figured out where it does belong.
+
class SignedReferral(rpki.x509.XML_CMS_object):
encoding = "us-ascii"
schema = rpki.relaxng.myrpki
@@ -138,6 +139,40 @@ ip_version_map = { "IPv4" : 4, "IPv6" : 6 }
#
ip_version_choices = tuple((y, x) for (x, y) in ip_version_map.iteritems())
+class CertificateManager(django.db.models.Manager):
+
+ def get_or_certify(self, **kwargs):
+ """
+ Sort of like .get_or_create(), but for models containing
+ certificates which need to be generated based on other fields.
+
+ Takes keyword arguments like .get(), checks for existing object.
+ If none, creates a new one; if found an existing object but some
+ of the non-key fields don't match, updates the existing object.
+ Runs certification method for new or updated objects. Returns a
+ tuple consisting of the object and a boolean indicating whether
+ anything has changed.
+
+ This is a somewhat weird use of .Meta.unique_together, but fits
+ with the way this application use unique indices and foreign keys.
+ """
+
+ try:
+ obj = self.get(**dict((k, kwargs[k]) for k in self.model.Meta.unique_together))
+
+ except self.model.DoesNotExist:
+ obj = self.model(**kwargs)
+
+ else:
+ if all(getattr(obj, k) == kwargs[k] for k in kwargs):
+ return obj, False
+ for k in kwargs:
+ setattr(obj, k, kwargs[k])
+
+ obj.avow()
+ obj.save()
+ return obj, True
+
###
class Identity(django.db.models.Model):
@@ -160,6 +195,8 @@ class CA(django.db.models.Model):
last_crl_update = django.db.models.DateTimeField()
next_crl_update = django.db.models.DateTimeField()
+ objects = CertificateManager()
+
class Meta:
unique_together = ("identity", "purpose")
@@ -167,7 +204,9 @@ class CA(django.db.models.Model):
ca_certificate_lifetime = rpki.sundial.timedelta(days = 3652)
crl_interval = rpki.sundial.timedelta(days = 1)
- def self_certify(self):
+ def avow(self):
+ if self.private_key is None:
+ self.private_key = rpki.x509.RSA.generate()
subject_name = rpki.x509.X501DN("%s BPKI %s CA" % (
self.identity.handle, self.get_purpose_display()))
now = rpki.sundial.now()
@@ -179,6 +218,7 @@ class CA(django.db.models.Model):
now = now,
notAfter = notAfter)
self.serial += 1
+ self.generate_crl()
return self.certificate
def certify(self, subject_name, subject_key, validity_interval, is_ca, pathLenConstraint = None):
@@ -208,18 +248,22 @@ class CA(django.db.models.Model):
def generate_crl(self):
now = rpki.sundial.now()
self.revocations.filter(expires__lt = now).delete()
- revoked_certificates = [(r.serial, rpki.sundial.datetime.fromdatetime(r.revoked).toASN1tuple(), ())
- for r in self.revocations]
+ revoked = [(r.serial, rpki.sundial.datetime.fromdatetime(r.revoked).toASN1tuple(), ())
+ for r in self.revocations]
self.latest_crl = rpki.x509.CRL.generate(
keypair = self.private_key,
issuer = self.certificate.getSubject(),
+ serial = self.next_crl_number,
thisUpdate = now,
nextUpdate = now + self.crl_interval,
- revoked_certificates = revoked_certificates)
-
+ revoked_certificates = revoked)
+ self.last_crl_update = now
+ self.next_crl_update = now + self.crl_interval
+ self.next_crl_number += 1
class Certificate(django.db.models.Model):
certificate = CertificateField()
+ objects = CertificateManager()
default_interval = rpki.sundial.timedelta(days = 60)
@@ -238,7 +282,7 @@ class CrossCertification(Certificate):
abstract = True
unique_together = ("issuer", "handle")
- def generate_certificate(self):
+ def avow(self):
self.certificate = self.issuer.certify(
subject_name = self.ta.getSubject(),
subject_key = self.ta.getPublicKey(),
@@ -258,14 +302,16 @@ class Revocation(django.db.models.Model):
class EECertificate(Certificate):
issuer = django.db.models.ForeignKey(CA, related_name = "ee_certificates")
- purpose_map = ChoiceMap("rpkid", "pubd", "irdbd", "irbe", "rootd")
+ purpose_map = ChoiceMap("rpkid", "pubd", "irdbd", "irbe", "rootd", "referral")
purpose = django.db.models.PositiveSmallIntegerField(choices = purpose_map.choices)
private_key = RSAKeyField()
class Meta:
unique_together = ("issuer", "purpose")
- def generate_certificate(self):
+ def avow(self):
+ if self.private_key is None:
+ self.private_key = rpki.x509.RSA.generate()
subject_name = rpki.x509.X501DN("%s BPKI %s EE" % (
self.issuer.identity.handle, self.get_purpose_display()))
self.certificate = self.issuer.certify(
@@ -282,7 +328,7 @@ class BSC(Certificate):
class Meta:
unique_together = ("issuer", "handle")
- def generate_certificate(self):
+ def avow(self):
self.certificate = self.issuer.certify(
subject_name = self.pkcs10.getSubject(),
subject_key = self.pkcs10.getPublicKey(),