aboutsummaryrefslogtreecommitdiff
path: root/scripts/rpki
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/rpki')
-rw-r--r--scripts/rpki/left_right.py6
-rw-r--r--scripts/rpki/sql.py8
-rw-r--r--scripts/rpki/sundial.py110
-rw-r--r--scripts/rpki/x509.py14
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)