diff options
Diffstat (limited to 'rpkid')
-rw-r--r-- | rpkid/rpki/x509.py | 152 |
1 files changed, 80 insertions, 72 deletions
diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index a536cc97..3bd0a3cd 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -69,43 +69,13 @@ def base64_with_linebreaks(der): n = len(b) return "\n" + "\n".join(b[i : min(i + 64, n)] for i in xrange(0, n, 64)) + "\n" -class PEM_converter(object): +def looks_like_PEM(text): """ - Convert between DER and PEM encodings for various kinds of ASN.1 data. + Guess whether text looks like a PEM encoding. """ - def __init__(self, kind): # "CERTIFICATE", "RSA PRIVATE KEY", ... - """ - Initialize PEM_converter. - """ - self.b = "-----BEGIN %s-----" % kind - self.e = "-----END %s-----" % kind - - def looks_like_PEM(self, text): - """ - Guess whether text looks like a PEM encoding. - """ - b = text.find(self.b) - return b >= 0 and text.find(self.e) > b + len(self.b) - - def to_DER(self, pem): - """ - Convert from PEM to DER. - """ - lines = [line.strip() for line in pem.splitlines(0)] - while lines and lines.pop(0) != self.b: - pass - while lines and lines.pop(-1) != self.e: - pass - if not lines: - raise rpki.exceptions.EmptyPEM("Could not find PEM in:\n%s" % pem) - return base64.b64decode("".join(lines)) - - def to_PEM(self, der): - """ - Convert from DER to PEM. - """ - return self.b + base64_with_linebreaks(der) + self.e + "\n" + i = text.find("-----BEGIN ") + return i >= 0 and text.find("\n-----END ", i) > i def first_rsync_uri(xia): """ @@ -207,17 +177,28 @@ class DER_object(object): Virtual class to hold a generic DER object. """ - ## Formats supported in this object - formats = ("DER",) + ## @var formats + # Formats supported in this object. This is kind of redundant now + # that we're down to a single ASN.1 package and everything supports + # the same DER and POW formats, it's mostly historical baggage from + # the days when we had three different ASN.1 encoders, each with its + # own low-level Python object format. Clean up, some day. + formats = ("DER", "POW") - ## PEM converter for this object - pem_converter = None + ## @var POW_class + # Class of underlying POW object. Concrete subclasses must supply this. + POW_class = None - ## Other attributes that self.clear() should whack + ## Other attributes that self.clear() should whack. other_clear = () ## @var DER - ## DER value of this object + # DER value of this object + DER = None + + ## @var failure_threshold + # Rate-limiting interval between whines about Auto_update objects. + failure_threshold = rpki.sundial.timedelta(minutes = 5) def empty(self): """ @@ -233,7 +214,7 @@ class DER_object(object): setattr(self, a, None) self.filename = None self.timestamp = None - self.failures = 0 + self.lastfail = None def __init__(self, **kw): """ @@ -261,7 +242,7 @@ class DER_object(object): return if name == "PEM": self.clear() - self.DER = self.pem_converter.to_DER(kw[name]) + self._set_PEM(kw[name]) return if name == "Base64": self.clear() @@ -275,10 +256,11 @@ class DER_object(object): f = open(kw[name], "rb") value = f.read() f.close() - if name == "PEM_file" or (name == "Auto_file" and self.pem_converter.looks_like_PEM(value)): - value = self.pem_converter.to_DER(value) self.clear() - self.DER = value + if name == "PEM_file" or (name == "Auto_file" and looks_like_PEM(value)): + self._set_PEM(value) + else: + self.DER = value return raise rpki.exceptions.DERObjectConversionError("Can't honor conversion request %r" % (kw,)) @@ -296,25 +278,35 @@ class DER_object(object): f = open(filename, "rb") value = f.read() f.close() - if self.pem_converter.looks_like_PEM(value): - value = self.pem_converter.to_DER(value) self.clear() - self.DER = value + if looks_like_PEM(value): + self._set_PEM(value) + else: + self.DER = value self.filename = filename self.timestamp = timestamp except (IOError, OSError), e: - self.failures += 1 - if self.failures % 100 == 1: + now = rpki.sundial.now() + if self.lastfail is None or now > self.lastfail + self.failure_threshold: rpki.log.warn("Could not auto_update %r (failures %d): %s" % (self, self.failures, e)) + self.lastfail = now else: - self.failures = 0 + self.failures = None def check(self): """ Perform basic checks on a DER object. """ - assert not self.empty() self.check_auto_update() + assert not self.empty() + + def _set_PEM(self, pem): + """ + Set the POW value of this object based on a PEM input value. + Subclasses may need to override this. + """ + assert self.empty() + self.POW = self.POW_class.pemRead(pem) def get_DER(self): """ @@ -337,7 +329,7 @@ class DER_object(object): """ Get the PEM representation of this object. """ - return self.pem_converter.to_PEM(self.get_DER()) + return self.get_POW().pemWrite() def __cmp__(self, other): """ @@ -541,9 +533,8 @@ class X509(DER_object): have to care about this implementation nightmare. """ - formats = ("DER", "POW") - pem_converter = PEM_converter("CERTIFICATE") - + POW_class = rpki.POW.X509 + def get_DER(self): """ Get the DER value of this certificate. @@ -838,8 +829,7 @@ class PKCS10(DER_object): Class to hold a PKCS #10 request. """ - formats = ("DER", "POW") - pem_converter = PEM_converter("CERTIFICATE REQUEST") + POW_class = rpki.POW.PKCS10 ## @var expected_ca_keyUsage # KeyUsage extension flags expected for CA requests. @@ -1030,10 +1020,9 @@ class RSA(DER_object): """ Class to hold an RSA key pair. """ - - formats = ("DER", "POW") - pem_converter = PEM_converter("RSA PRIVATE KEY") + POW_class = rpki.POW.Asymmetric + def get_DER(self): """ Get the DER value of this keypair. @@ -1055,6 +1044,19 @@ class RSA(DER_object): self.POW = rpki.POW.Asymmetric.derReadPrivate(self.get_DER()) return self.POW + def get_PEM(self): + """ + Get the PEM representation of this keypair. + """ + return self.get_POW().pemWritePrivate() + + def _set_PEM(self, pem): + """ + Set the POW value of this keypair from a PEM string. + """ + assert self.empty() + self.POW = self.POW_class.pemReadPrivate(pem) + @classmethod def generate(cls, keylength = 2048, quiet = False): """ @@ -1089,10 +1091,9 @@ class RSApublic(DER_object): """ Class to hold an RSA public key. """ - - formats = ("DER", "POW") - pem_converter = PEM_converter("RSA PUBLIC KEY") + POW_class = rpki.POW.Asymmetric + def get_DER(self): """ Get the DER value of this public key. @@ -1114,6 +1115,19 @@ class RSApublic(DER_object): self.POW = rpki.POW.Asymmetric.derReadPublic(self.get_DER()) return self.POW + def get_PEM(self): + """ + Get the PEM representation of this public key. + """ + return self.get_POW().pemWritePublic() + + def _set_PEM(self, pem): + """ + Set the POW value of this public key from a PEM string. + """ + assert self.empty() + self.POW = self.POW_class.pemReadPublic(pem) + def get_SKI(self): """ Calculate the SKI of this public key. @@ -1135,9 +1149,7 @@ class CMS_object(DER_object): Abstract class to hold a CMS object. """ - formats = ("DER", "POW") econtent_oid = POWify_OID("id-data") - pem_converter = PEM_converter("CMS") POW_class = rpki.POW.CMS ## @var dump_on_verify_failure @@ -1473,7 +1485,6 @@ class SignedManifest(DER_CMS_object): Class to hold a signed manifest. """ - pem_converter = PEM_converter("RPKI MANIFEST") econtent_oid = POWify_OID("id-ct-rpkiManifest") POW_class = rpki.POW.Manifest @@ -1519,7 +1530,6 @@ class ROA(DER_CMS_object): Class to hold a signed ROA. """ - pem_converter = PEM_converter("ROUTE ORIGIN ATTESTATION") econtent_oid = POWify_OID("id-ct-routeOriginAttestation") POW_class = rpki.POW.ROA @@ -1741,7 +1751,6 @@ class Ghostbuster(Wrapped_CMS_object): managed by the back-end. """ - pem_converter = PEM_converter("GHOSTBUSTERS RECORD") econtent_oid = POWify_OID("id-ct-rpkiGhostbusters") def encode(self): @@ -1773,10 +1782,9 @@ class CRL(DER_object): """ Class to hold a Certificate Revocation List. """ - - formats = ("DER", "POW") - pem_converter = PEM_converter("X509 CRL") + POW_class = rpki.POW.CRL + def get_DER(self): """ Get the DER value of this CRL. |