diff options
Diffstat (limited to 'scripts/rpki')
-rw-r--r-- | scripts/rpki/left_right.py | 6 | ||||
-rw-r--r-- | scripts/rpki/sql.py | 8 | ||||
-rw-r--r-- | scripts/rpki/sundial.py | 110 | ||||
-rw-r--r-- | scripts/rpki/x509.py | 14 |
4 files changed, 124 insertions, 14 deletions
diff --git a/scripts/rpki/left_right.py b/scripts/rpki/left_right.py index 3528152d..e1fcf4a1 100644 --- a/scripts/rpki/left_right.py +++ b/scripts/rpki/left_right.py @@ -4,7 +4,7 @@ import base64, lxml.etree, time, traceback import rpki.sax_utils, rpki.resource_set, rpki.x509, rpki.sql, rpki.exceptions -import rpki.https, rpki.up_down, rpki.relaxng +import rpki.https, rpki.up_down, rpki.relaxng, rpki.sundial xmlns = "http://www.hactrn.net/uris/rpki/left-right-spec/" @@ -640,7 +640,7 @@ class list_resources_elt(base_elt): assert name == "list_resources", "Unexpected name %s, stack %s" % (name, stack) self.read_attrs(attrs) if isinstance(self.valid_until, str): - self.valid_until = int(time.mktime(time.strptime(self.valid_until, "%Y-%m-%dT%H:%M:%SZ"))) + self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until) if self.as is not None: self.as = rpki.resource_set.resource_set_as(self.as) if self.ipv4 is not None: @@ -652,7 +652,7 @@ class list_resources_elt(base_elt): """Generate <list_resources/> element.""" elt = self.make_elt() if isinstance(self.valid_until, int): - elt.set("valid_until", time.strftime("%Y-%m-%dT%H:%M:%SZ", time.localtime(self.valid_until))) + elt.set("valid_until", self.valid_until.toXMLtime()) return elt class report_error_elt(base_elt): diff --git a/scripts/rpki/sql.py b/scripts/rpki/sql.py index 1553d7c2..ea3e74e4 100644 --- a/scripts/rpki/sql.py +++ b/scripts/rpki/sql.py @@ -1,7 +1,7 @@ # $Id$ import MySQLdb, time -import rpki.x509, rpki.resource_set +import rpki.x509, rpki.resource_set, rpki.sundial def connect(cfg, section="sql"): """Connect to a MySQL database using connection parameters from an @@ -448,8 +448,8 @@ class ca_detail_obj(sql_persistant): self.self_id = parent.self_id AND parent.parent_id = %s """ % ca.parent_id) - now = time.time() - then = now + self_obj.crl_interval + now = rpki.sundial.datetime.utcnow() + then = now + rpki.sundial.timedelta(seconds = self_obj.crl_interval) certs = [] for cert in child_cert_obj.sql_fetch_where(gctx, "child_cert.ca_detail_id = %s AND child_cert.revoked" % self.ca_detail_id): raise rpki.exceptions.NotImplementedYet @@ -479,7 +479,7 @@ class ca_detail_obj(sql_persistant): m = rpki.x509.SignedManifest() m.build(serial = ca.next_manifest_number(), - nextUpdate = time.time() + self_obj.crl_interval, + nextUpdate = rpki.sundial.datetime.utcnow() + rpki.sundial.timedelta(seconds = self_obj.crl_interval), names_and_objs = [(c.cert.gSKI() + ".cer", c.cert) for c in certs]) m.sign(keypair = self.manifest_private_key_id, certs = rpki.x509.X509_chain(self.latest_manifest_cert)) diff --git a/scripts/rpki/sundial.py b/scripts/rpki/sundial.py new file mode 100644 index 00000000..2360732d --- /dev/null +++ b/scripts/rpki/sundial.py @@ -0,0 +1,110 @@ +# $Id$ + +"""Unified RPKI date/time handling, based on the standard Python datetime module. + +Module name chosen to sidestep a nightmare of import-related errors +that occur with the more obvious module names. +""" + +import datetime as pydatetime + +class datetime(pydatetime.datetime): + """RPKI extensions to standard datetime.datetime class. All work + here is in UTC, so we use naive datetime objects. + """ + + def totimestamp(self): + """Convert to seconds from epoch (like time.time()). Conversion + method is a bit silly, but avoids time module timezone whackiness. + """ + return int(self.strftime("%s")) + + @classmethod + def fromUTCTime(cls, x): + """Convert from ASN.1 UTCTime.""" + return cls.strptime(x, "%y%m%d%H%M%SZ") + + def toUTCTime(self): + """Convert to ASN.1 UTCTime.""" + return self.strftime("%y%m%d%H%M%SZ") + + @classmethod + def fromGeneralizedTime(cls, x): + """Convert from ASN.1 GeneralizedTime.""" + return cls.strptime(x, "%Y%m%d%H%M%SZ") + + def toGeneralizedTime(self): + """Convert to ASN.1 GeneralizedTime.""" + return self.strftime("%Y%m%d%H%M%SZ") + + @classmethod + def fromASN1tuple(cls, x): + """Convert from ASN.1 tuple representation.""" + assert isinstance(x, tuple) and len(x) == 2 and x[0] in ("utcTime", "generalTime") + if x[0] == "utcTime": + return cls.fromUTCTime(x) + else: + return cls.fromGeneralizedTime(x) + + ## @var PKIX_threshhold + # Threshold specified in RFC 3280 for switchover from UTCTime to GeneralizedTime. + + PKIX_threshhold = pydatetime.datetime(2050, 1, 1) + + def toASN1tuple(self): + """Convert to ASN.1 tuple representation.""" + if self < self.PKIX_threshhold: + return "utcTime", self.toUTCTime() + else: + return "generalTime", self.toGeneralizedTime() + + @classmethod + def fromXMLtime(cls, x): + """Convert from XML time representation.""" + return cls.strptime(x, "%Y-%m-%dT%H:%M:%SZ") + + def toXMLtime(self): + """Convert to XML time representation.""" + return self.strftime("%Y-%m-%dT%H:%M:%SZ") + + @classmethod + def _cast(cls, x): + """Convert a datetime.datetime object into this subclass. + This is whacky due to the weird constructors for datetime. + """ + return cls.combine(x.date(), x.time()) + + def __add__(self, other): + """Force correct class for timedelta results.""" + return self._cast(pydatetime.datetime.__add__(self, other)) + + def __sub__(self, other): + """Force correct class for timedelta results.""" + return self._cast(pydatetime.datetime.__sub__(self, other)) + +# Alias to simplify imports for callers + +timedelta = pydatetime.timedelta + +if __name__ == "__main__": + + now = datetime.utcnow() + print now + print repr(now) + print now.strftime("%s") + print now.toUTCTime() + print now.toGeneralizedTime() + print now.toASN1tuple() + print now.toXMLtime() + + print + + then = now + then += timedelta(days = 30) + print then + print repr(then) + print then.strftime("%s") + print then.toUTCTime() + print then.toGeneralizedTime() + print then.toASN1tuple() + print then.toXMLtime() diff --git a/scripts/rpki/x509.py b/scripts/rpki/x509.py index 5ee612a3..44c2a168 100644 --- a/scripts/rpki/x509.py +++ b/scripts/rpki/x509.py @@ -13,7 +13,7 @@ some of the nasty details. This involves a lot of format conversion. """ import POW, tlslite.api, POW.pkix, base64, time -import rpki.exceptions, rpki.resource_set, rpki.manifest, rpki.cms, rpki.oids +import rpki.exceptions, rpki.resource_set, rpki.manifest, rpki.cms, rpki.oids, rpki.sundial class PEM_converter(object): """Convert between DER and PEM encodings for various kinds of ASN.1 data.""" @@ -248,7 +248,7 @@ class X509(DER_object): cn = None, notAfter = None, resources = None, is_ca = True): """Issue a certificate.""" - now = time.time() + now = rpki.sundial.datetime.utcnow() aki = self.get_SKI() ski = subject_key.get_SKI() @@ -256,15 +256,15 @@ class X509(DER_object): cn = "".join(("%02X" % ord(i) for i in ski)) if notAfter is None: - notAfter = now + 30 * 24 * 60 * 60 + notAfter = now + rpki.sundial.timedelta(days = 30) cert = POW.pkix.Certificate() cert.setVersion(2) cert.setSerial(serial) cert.setIssuer(self.get_POWpkix().getSubject()) cert.setSubject((((rpki.oids.name2oid["commonName"], ("printableString", cn)),),)) - cert.setNotBefore(("utcTime", POW.pkix.time2utc(now))) - cert.setNotAfter(("utcTime", POW.pkix.time2utc(notAfter))) + cert.setNotBefore(now.toASN1tuple()) + cert.setNotAfter(notAfter.toASN1tuple()) cert.tbs.subjectPublicKeyInfo.fromString(subject_key.get_DER()) exts = [ ["subjectKeyIdentifier", False, ski], @@ -596,8 +596,8 @@ class SignedManifest(DER_object): m = rpki.manifest.Manifest() m.version.set(version) m.manifestNumber.set(serial) - m.thisUpdate.set(POW.pkix.time2gen(time.time())) - m.nextUpdate.set(POW.pkix.time2gen(nextUpdate)) + m.thisUpdate.set(rpki.sundial.datetime.utcnow().toGeneralizedTime()) + m.nextUpdate.set(nextUpdate.toGeneralizedTime()) m.fileHashAlg.set((2, 16, 840, 1, 101, 3, 4, 2, 1)) # id-sha256 m.fileList.set(filelist) self.set_content(m) |