aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2008-04-19 22:52:16 +0000
committerRob Austein <sra@hactrn.net>2008-04-19 22:52:16 +0000
commit14f2160b5f9a78c02b8b072930040210220c63cb (patch)
treeb253a47e66ede9ba68c2bef296836a052044ded4
parentbd558a26aad5da955c4b59e446837ecab0618be2 (diff)
Convert CMS code to something vaguely object-oriented, to simplify
handling of eContentType OIDs, etc. Unifiy some of the redundant XML processing as method routines. svn path=/pow/POW-0.7/POW.c; revision=1679
-rw-r--r--pow/POW-0.7/POW.c24
-rwxr-xr-xrpkid/irbe-cli.py56
-rw-r--r--rpkid/irbe-setup.py28
-rwxr-xr-xrpkid/irdbd.py12
-rwxr-xr-xrpkid/rootd.py18
-rw-r--r--rpkid/rpki/cms.py120
-rw-r--r--rpkid/rpki/gctx.py21
-rw-r--r--rpkid/rpki/left_right.py38
-rw-r--r--rpkid/rpki/oids.py49
-rw-r--r--rpkid/rpki/x509.py152
-rwxr-xr-xrpkid/rpkid.py2
-rw-r--r--rpkid/testbed.py14
-rw-r--r--rpkid/testpoke.py18
13 files changed, 253 insertions, 299 deletions
diff --git a/pow/POW-0.7/POW.c b/pow/POW-0.7/POW.c
index cbe4f8a1..1b3af645 100644
--- a/pow/POW-0.7/POW.c
+++ b/pow/POW-0.7/POW.c
@@ -148,17 +148,17 @@
#define DER_FORMAT 2
// Object check functions
-#define X_X509_Check(op) ((op)->ob_type == &x509type)
-#define X_X509_store_Check(op) ((op)->ob_type == &x509_storetype)
-#define X_X509_crl_Check(op) ((op)->ob_type == &x509_crltype)
+#define X_X509_Check(op) ((op)->ob_type == &x509type)
+#define X_X509_store_Check(op) ((op)->ob_type == &x509_storetype)
+#define X_X509_crl_Check(op) ((op)->ob_type == &x509_crltype)
#define X_X509_revoked_Check(op) ((op)->ob_type == &x509_revokedtype)
-#define X_asymmetric_Check(op) ((op)->ob_type == &asymmetrictype)
-#define X_symmetric_Check(op) ((op)->ob_type == &symmetrictype)
-#define X_digest_Check(op) ((op)->ob_type == &digesttype)
-#define X_hmac_Check(op) ((op)->ob_type == &hmactype)
-#define X_ssl_Check(op) ((op)->ob_type == &ssltype)
-#define X_pkcs7_Check(op) ((op)->ob_type == &pkcs7type)
-#define X_cms_Check(op) ((op)->ob_type == &cmstype)
+#define X_asymmetric_Check(op) ((op)->ob_type == &asymmetrictype)
+#define X_symmetric_Check(op) ((op)->ob_type == &symmetrictype)
+#define X_digest_Check(op) ((op)->ob_type == &digesttype)
+#define X_hmac_Check(op) ((op)->ob_type == &hmactype)
+#define X_ssl_Check(op) ((op)->ob_type == &ssltype)
+#define X_pkcs7_Check(op) ((op)->ob_type == &pkcs7type)
+#define X_cms_Check(op) ((op)->ob_type == &cmstype)
static char pow_module__doc__ [] =
"<moduleDescription>\n"
@@ -290,9 +290,9 @@ typedef struct {
Simple function to install a constant in the module name space.
*/
static void
-install_int_const( PyObject *d, char *name, int value )
+install_int_const( PyObject *d, char *name, long value )
{
- PyObject *v = PyInt_FromLong( (long)value );
+ PyObject *v = PyInt_FromLong(value);
if (!v || PyDict_SetItemString(d, name, v) )
PyErr_Clear();
diff --git a/rpkid/irbe-cli.py b/rpkid/irbe-cli.py
index 786d6c98..77c8f870 100755
--- a/rpkid/irbe-cli.py
+++ b/rpkid/irbe-cli.py
@@ -21,7 +21,7 @@ The query back-channel is handled by a separate program.
"""
import getopt, sys, lxml.etree, lxml.sax
-import rpki.left_right, rpki.relaxng, rpki.cms, rpki.https, rpki.x509, rpki.config, rpki.log
+import rpki.left_right, rpki.relaxng, rpki.https, rpki.x509, rpki.config, rpki.log
pem_out = None
@@ -153,6 +153,14 @@ if not argv:
cfg = rpki.config.parser(cfg_file, "irbe-cli")
+cms_key = rpki.x509.RSA( Auto_file = cfg.get( "cms-key"))
+cms_certs = rpki.x509.X509_chain(Auto_files = cfg.multiget("cms-cert"))
+cms_ta = rpki.x509.X509( Auto_file = cfg.get( "cms-ta"))
+https_key = rpki.x509.RSA( Auto_file = cfg.get( "https-key"))
+https_certs = rpki.x509.X509_chain(Auto_files = cfg.multiget("https-cert"))
+https_ta = rpki.x509.X509( Auto_file = cfg.get( "https-ta"))
+https_url = cfg.get( "https-url")
+
q_msg = rpki.left_right.msg()
while argv:
@@ -163,41 +171,19 @@ while argv:
argv = q_pdu.client_getopt(argv[1:])
q_msg.append(q_pdu)
-# We don't use rpki.cms.xml_sign() and rpki.cms.xml_verify() because
-# we want to display the raw XML. If and when that changes, we clean
-# up the following slightly.
-
q_elt = q_msg.toXML()
-q_xml = lxml.etree.tostring(q_elt, pretty_print=True, encoding="us-ascii", xml_declaration=True)
-try:
- rpki.relaxng.left_right.assertValid(q_elt)
-except lxml.etree.DocumentInvalid:
- print "Generated query document does not pass schema check:"
- print
- print q_xml
- raise
-
-q_cms = rpki.cms.sign(q_xml,
- rpki.x509.RSA(Auto_file = cfg.get("cms-key")),
- rpki.x509.X509_chain(Auto_files = cfg.multiget("cms-cert")))
-
-r_cms = rpki.https.client(client_key = rpki.x509.RSA(Auto_file = cfg.get("https-key")),
- client_certs = rpki.x509.X509_chain(Auto_files = cfg.multiget("https-cert")),
- server_ta = rpki.x509.X509(Auto_file = cfg.get("https-ta")),
- url = cfg.get("https-url"),
- msg = q_cms)
-
-r_xml = rpki.cms.verify(r_cms, rpki.x509.X509(Auto_file = cfg.get("cms-ta")))
-
-r_elt = lxml.etree.fromstring(r_xml)
-try:
- rpki.relaxng.left_right.assertValid(r_elt)
-except lxml.etree.DocumentInvalid:
- print "Received reply document does not pass schema check:"
- print r_xml
- raise
-
-print r_xml
+q_cms = rpki.x509.left_right_pdu.build(q_elt, cms_key, cms_certs)
+
+der = rpki.https.client(client_key = https_key,
+ client_certs = https_certs,
+ server_ta = https_ta,
+ url = https_url,
+ msg = q_cms.get_DER())
+
+r_cms = rpki.x509.left_right(DER = der)
+r_elt = r_cms.verify(r_cms, cms_ta)
+
+print r_cms.prettyprint_content()
handler = sax_handler()
lxml.sax.saxify(r_elt, handler)
diff --git a/rpkid/irbe-setup.py b/rpkid/irbe-setup.py
index d3170bfa..452aae76 100644
--- a/rpkid/irbe-setup.py
+++ b/rpkid/irbe-setup.py
@@ -20,7 +20,7 @@ engine for every registrant object in the IRDB.
"""
import os, MySQLdb, getopt, sys, lxml.etree, lxml.sax
-import rpki.left_right, rpki.relaxng, rpki.cms, rpki.https
+import rpki.left_right, rpki.relaxng, rpki.https
import rpki.x509, rpki.config, rpki.log
rpki.log.init("irbe-setup")
@@ -48,24 +48,14 @@ def call_rpkid(pdu):
pdu.type = "query"
msg = rpki.left_right.msg((pdu,))
elt = msg.toXML()
- try:
- rpki.relaxng.left_right.assertValid(elt)
- except lxml.etree.DocumentInvalid:
- print lxml.etree.tostring(elt, pretty_print = True, encoding = "us-ascii")
- raise
- elt = rpki.cms.xml_verify(der = rpki.https.client(client_key = https_key,
- client_certs = https_certs,
- server_ta = https_ta,
- url = https_url,
- msg = rpki.cms.xml_sign(elt = elt,
- key = cms_key,
- certs = cms_certs)),
- ta = cms_ta)
- try:
- rpki.relaxng.left_right.assertValid(elt)
- except lxml.etree.DocumentInvalid:
- print lxml.etree.tostring(elt, pretty_print = True, encoding = "us-ascii")
- raise
+ cms = rpki.x509.let_right_pdu.build(elt, cms_key, cms_certs)
+ der = rpki.https.client(client_key = https_key,
+ client_certs = https_certs,
+ server_ta = https_ta,
+ url = https_url,
+ msg = cms)
+ cms = rpki.x509.left_right_pdu(DER = der)
+ elt = cms.verify(cms_ta)
msg = rpki.left_right.sax_handler.saxify(elt)
pdu = msg[0]
assert len(msg) == 1 and pdu.type == "reply" and not isinstance(pdu, rpki.left_right.report_error_elt)
diff --git a/rpkid/irdbd.py b/rpkid/irdbd.py
index 09b1685e..6570eeb3 100755
--- a/rpkid/irdbd.py
+++ b/rpkid/irdbd.py
@@ -24,13 +24,13 @@ Default configuration file is irdbd.conf, override with --config option.
import sys, os, time, getopt, urlparse, traceback
import tlslite.api, MySQLdb, lxml.etree
-import rpki.https, rpki.config, rpki.resource_set, rpki.cms, rpki.relaxng
-import rpki.exceptions, rpki.left_right, rpki.log
+import rpki.https, rpki.config, rpki.resource_set, rpki.relaxng
+import rpki.exceptions, rpki.left_right, rpki.log, rpki.x509
def handler(query, path):
try:
- q_elt = rpki.cms.xml_verify(query, cms_ta)
- rpki.relaxng.left_right.assertValid(q_elt)
+ q_cms = rpki.x509.left_right_pdu(DER = query)
+ q_elt = q_cms.verify(cms_ta)
q_msg = rpki.left_right.sax_handler.saxify(q_elt)
if not isinstance(q_msg, rpki.left_right.msg):
raise rpki.exceptions.BadQuery, "Unexpected %s PDU" % repr(q_msg)
@@ -71,8 +71,8 @@ def handler(query, path):
r_msg.append(r_pdu)
r_elt = r_msg.toXML()
- rpki.relaxng.left_right.assertValid(r_elt)
- return 200, rpki.cms.xml_sign(r_elt, cms_key, cms_certs)
+ r_cms = rpki.x509.left_right_pdu.build(r_elt, cms_key, cms_certs)
+ return 200, r_cms.get_DER()
except Exception, data:
rpki.log.error(traceback.format_exc())
diff --git a/rpkid/rootd.py b/rpkid/rootd.py
index ba767917..21b1b371 100755
--- a/rpkid/rootd.py
+++ b/rpkid/rootd.py
@@ -26,7 +26,7 @@ Default configuration file is rootd.conf, override with --config option.
import traceback, os, time, getopt, sys, lxml
import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509
-import rpki.https, rpki.config, rpki.cms, rpki.exceptions, rpki.relaxng
+import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng
import rpki.sundial, rpki.log
rpki_subject_lifetime = rpki.sundial.timedelta(days = 30)
@@ -130,8 +130,8 @@ class sax_handler(rpki.sax_utils.handler):
def up_down_handler(query, path):
try:
- q_elt = rpki.cms.xml_verify(query, cms_ta)
- rpki.relaxng.up_down.assertValid(q_elt)
+ q_cms = rpki.x509.up_down_pdu(DER = query)
+ q_elt = q_cms.verify(cms_ta)
q_msg = sax_handler.saxify(q_elt)
except Exception, data:
rpki.log.error(traceback.format_exc())
@@ -139,19 +139,15 @@ def up_down_handler(query, path):
try:
r_msg = q_msg.serve_top_level(None)
r_elt = r_msg.toXML()
- try:
- rpki.relaxng.up_down.assertValid(r_elt)
- except lxml.etree.DocumentInvalid:
- rpki.log.debug(lxml.etree.tostring(r_elt, pretty_print = True, encoding ="utf-8", xml_declaration = True))
- raise
- return 200, rpki.cms.xml_sign(r_elt, cms_key, cms_certs, encoding = "utf-8")
+ r_cms = rpki.x509.up_down_pdu.build(r_elt, cms_key, cms_certs)
+ return 200, r_cms.get_DER()
except Exception, data:
rpki.log.error(traceback.format_exc())
try:
r_msg = q_msg.serve_error(data)
r_elt = r_msg.toXML()
- rpki.relaxng.up_down.assertValid(r_elt)
- return 200, rpki.cms.xml_sign(r_elt, cms_key, cms_certs, encoding = "utf-8")
+ r_cms = rpki.x509.up_down_pdu.build(r_elt, cms_key, cms_certs)
+ return 200, r_cms.get_DER()
except Exception, data:
rpki.log.error(traceback.format_exc())
return 500, "Could not process PDU: %s" % data
diff --git a/rpkid/rpki/cms.py b/rpkid/rpki/cms.py
deleted file mode 100644
index 35d08e8a..00000000
--- a/rpkid/rpki/cms.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# $Id$
-
-# 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.
-#
-# 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.
-
-"""CMS routines. I should write a pretty DER_object wrapper around
-the POW code and include it in x509.py, haven't gotten to that yet.
-"""
-
-import os, rpki.x509, rpki.exceptions, lxml.etree, rpki.log, POW
-
-debug = 1
-
-id_data = (1, 2, 840, 113549, 1, 7, 1)
-
-# openssl smime -sign -nodetach -outform DER -signer biz-certs/Alice-EE.cer
-# -certfile biz-certs/Alice-CA.cer -inkey biz-certs/Alice-EE.key
-# -in THING -out THING.der
-
-def sign(plaintext, keypair, certs, oid = id_data, no_certs = False):
- """Sign plaintext as CMS with specified key and bag of certificates."""
-
- cms = POW.CMS()
- cms.sign(certs[0].get_POW(),
- keypair.get_POW(),
- [x.get_POW() for x in certs[1:]],
- plaintext,
- ".".join(str(i) for i in oid),
- no_certs)
- der = cms.derWrite()
-
- if debug >= 2:
- print
- print "Signed CMS:"
- dumpasn1(der)
-
- return der
-
-# openssl smime -verify -inform DER -in THING.der -CAfile biz-certs/Alice-Root.cer
-
-def verify(der, ta):
- """Verify the signature of a chunk of CMS.
-
- Returns the plaintext on success, otherwise raise an exception.
- """
-
- if debug >= 2:
- print
- print "Verifying CMS:"
- dumpasn1(der)
-
- cms = POW.derRead(POW.CMS_MESSAGE, der)
-
- store = POW.X509Store()
-
- if isinstance(ta, (tuple, list)):
- for x in ta:
- store.addTrust(x.get_POW())
- else:
- store.addTrust(ta.get_POW())
-
- try:
- return cms.verify(store)
-
- except:
- if debug >= 1:
- print "CMS verification failed, dumping inputs:"
- print
- if isinstance(ta, (tuple, list)):
- for x in ta:
- print "TA:"
- dumpasn1(x.get_DER())
- else:
- print "TA:"
- dumpasn1(ta.get_DER())
- print
- print "CMS:"
- dumpasn1(der)
- raise rpki.exceptions.CMSVerificationFailed, "CMS verification failed"
-
-def xml_verify(der, ta):
- """Composite routine to verify CMS-wrapped XML."""
-
- val = lxml.etree.fromstring(verify(der, ta))
- return val
-
-def xml_sign(elt, key, certs, encoding = "us-ascii"):
- """Composite routine to sign CMS-wrapped XML."""
-
- val = sign(lxml.etree.tostring(elt, pretty_print = True, encoding = encoding, xml_declaration = True),
- key, certs)
- return val
-
-def dumpasn1(thing):
- """Prettyprint an ASN.1 DER object using cryptlib dumpasn1 tool.
- Use a temporary file rather than popen4() because dumpasn1 uses
- seek() when decoding ASN.1 content nested in OCTET STRING values.
- """
-
- fn = "dumpasn1.tmp"
- try:
- f = open(fn, "w")
- f.write(thing)
- f.close()
- f = os.popen("dumpasn1 2>&1 -a " + fn)
- print "\n".join(x for x in f.read().splitlines() if x.startswith(" "))
- f.close()
- finally:
- os.unlink(fn)
diff --git a/rpkid/rpki/gctx.py b/rpkid/rpki/gctx.py
index d0d3d2c4..d6a572a4 100644
--- a/rpkid/rpki/gctx.py
+++ b/rpkid/rpki/gctx.py
@@ -21,7 +21,7 @@ the identifier gctx is scattered all through the code at the moment.
import traceback, os, time, getopt, sys, MySQLdb, lxml.etree
import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql
-import rpki.https, rpki.config, rpki.cms, rpki.exceptions, rpki.relaxng, rpki.log
+import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log
# This should be wrapped somewhere in rpki.x509 eventually
import POW
@@ -75,16 +75,15 @@ class global_context(object):
q_msg[0].self_id = self_id
q_msg[0].child_id = child_id
q_elt = q_msg.toXML()
- rpki.relaxng.left_right.assertValid(q_elt)
- q_cms = rpki.cms.xml_sign(q_elt, self.cms_key, self.cms_certs)
- r_cms = rpki.https.client(
+ q_cms = rpki.x509.left_right_pdu.build(q_elt, self.cms_key, self.cms_certs)
+ der = rpki.https.client(
client_key = self.https_key,
client_certs = self.https_certs,
server_ta = self.https_ta_irdb,
url = self.irdb_url,
- msg = q_cms)
- r_elt = rpki.cms.xml_verify(r_cms, self.cms_ta_irdb)
- rpki.relaxng.left_right.assertValid(r_elt)
+ msg = q_cms.get_DER())
+ r_cms = rpki.x509.left_right_pdu(DER = der)
+ r_elt = r_cms.verify(self.cms_ta_irdb)
r_msg = rpki.left_right.sax_handler.saxify(r_elt)
if len(r_msg) == 0 or not isinstance(r_msg[0], rpki.left_right.list_resources_elt) or r_msg[0].type != "reply":
raise rpki.exceptions.BadIRDBReply, "Unexpected response to IRDB query: %s" % lxml.etree.tostring(r_msg.toXML(), pretty_print = True, encoding = "us-ascii")
@@ -116,13 +115,13 @@ class global_context(object):
"""Process one left-right PDU."""
rpki.log.trace()
try:
- q_elt = rpki.cms.xml_verify(query, self.cms_ta_irbe)
- rpki.relaxng.left_right.assertValid(q_elt)
+ q_cms = rpki.x509.left_right_pdu(DER = query)
+ q_elt = q_cms.verify(self.cms_ta_irbe)
q_msg = rpki.left_right.sax_handler.saxify(q_elt)
r_msg = q_msg.serve_top_level(self)
r_elt = r_msg.toXML()
- rpki.relaxng.left_right.assertValid(r_elt)
- reply = rpki.cms.xml_sign(r_elt, self.cms_key, self.cms_certs)
+ r_cms = rpki.x509.left_right_pdu.build(r_elt, self.cms_key, self.cms_certs)
+ reply = r_cms.get_DER()
self.sql_sweep()
return 200, reply
except lxml.etree.DocumentInvalid:
diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py
index 5a3ae255..e14ed7cb 100644
--- a/rpkid/rpki/left_right.py
+++ b/rpkid/rpki/left_right.py
@@ -610,21 +610,16 @@ class parent_elt(data_elt):
sender = self.sender_name,
recipient = self.recipient_name)
q_elt = q_msg.toXML()
- try:
- rpki.relaxng.up_down.assertValid(q_elt)
- except lxml.etree.DocumentInvalid:
- rpki.log.error("Message does not pass schema check: " + lxml.etree.tostring(q_elt, pretty_print = True))
- raise
- q_cms = rpki.cms.xml_sign(q_elt, bsc.private_key_id, bsc.signing_cert, encoding = "UTF-8")
-
- r_cms = rpki.https.client(server_ta = self.peer_biz_cert,
- client_key = bsc.private_key_id,
- client_certs = bsc.signing_cert,
- msg = q_cms,
- url = self.peer_contact_uri)
-
- r_elt = rpki.cms.xml_verify(r_cms, self.peer_biz_cert)
- rpki.relaxng.up_down.assertValid(r_elt)
+ q_cms = rpki.x509.up_down_pdu.build(q_elt, bsc.private_key_id, bsc.signing_cert)
+
+ der = rpki.https.client(server_ta = self.peer_biz_cert,
+ client_key = bsc.private_key_id,
+ client_certs = bsc.signing_cert,
+ msg = q_cms.get_DER(),
+ url = self.peer_contact_uri)
+
+ r_cms = rpki.x509.up_down_pdu(DER = der)
+ r_elt = r_cms.verify(self.peer_biz_cert)
r_msg = rpki.up_down.sax_handler.saxify(r_elt)
r_msg.payload.check_response()
return r_msg
@@ -704,8 +699,8 @@ class child_elt(data_elt):
bsc = self.bsc()
if bsc is None:
raise rpki.exceptions.BSCNotFound, "Could not find BSC %s" % self.bsc_id
- q_elt = rpki.cms.xml_verify(query, self.peer_biz_cert)
- rpki.relaxng.up_down.assertValid(q_elt)
+ q_cms = rpki.x509.up_down_pdu(DER = query)
+ q_elt = q_cms.verify(self.peer_biz_cert)
q_msg = rpki.up_down.sax_handler.saxify(q_elt)
q_msg.payload.gctx = self.gctx
if enforce_strict_up_down_xml_sender and q_msg.sender != str(self.child_id):
@@ -721,13 +716,8 @@ class child_elt(data_elt):
# May require refactoring, ignore the issue for now.
#
r_elt = r_msg.toXML()
- try:
- rpki.relaxng.up_down.assertValid(r_elt)
- except:
- rpki.log.debug(lxml.etree.tostring(r_elt, pretty_print = True, encoding = "UTF-8"))
- rpki.log.error(traceback.format_exc())
- raise
- return rpki.cms.xml_sign(r_elt, bsc.private_key_id, bsc.signing_cert, encoding = "UTF-8")
+ r_cms = rpki.x509.up_down_pdu.build(r_elt, bsc.private_key_id, bsc.signing_cert)
+ return r_cms.get_DER()
class repository_elt(data_elt):
"""<repository/> element."""
diff --git a/rpkid/rpki/oids.py b/rpkid/rpki/oids.py
index 4e08aef7..a2dbf239 100644
--- a/rpkid/rpki/oids.py
+++ b/rpkid/rpki/oids.py
@@ -20,27 +20,34 @@
# Mapping table of OIDs to conventional string names.
oid2name = {
- (1, 2, 840, 113549, 1, 1, 11) : "sha256WithRSAEncryption",
- (1, 2, 840, 113549, 1, 1, 12) : "sha384WithRSAEncryption",
- (1, 2, 840, 113549, 1, 1, 13) : "sha512WithRSAEncryption",
- (1, 3, 6, 1, 5, 5, 7, 1, 1) : "authorityInfoAccess",
- (1, 3, 6, 1, 5, 5, 7, 1, 11) : "subjectInfoAccess",
- (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, 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, 5, 29, 14) : "subjectKeyIdentifier",
- (2, 5, 29, 15) : "keyUsage",
- (2, 5, 29, 19) : "basicConstraints",
- (2, 5, 29, 20) : "cRLNumber",
- (2, 5, 29, 31) : "cRLDistributionPoints",
- (2, 5, 29, 32) : "certificatePolicies",
- (2, 5, 29, 35) : "authorityKeyIdentifier",
- (2, 5, 4, 3) : "commonName",
+ (1, 2, 840, 113549, 1, 1, 11) : "sha256WithRSAEncryption",
+ (1, 2, 840, 113549, 1, 1, 12) : "sha384WithRSAEncryption",
+ (1, 2, 840, 113549, 1, 1, 13) : "sha512WithRSAEncryption",
+ (1, 2, 840, 113549, 1, 7, 1) : "id-data",
+ (1, 2, 840, 113549, 1, 9, 16) : "id-smime",
+ (1, 2, 840, 113549, 1, 9, 16, 1) : "id-ct",
+ (1, 2, 840, 113549, 1, 9, 16, 1, 24) : "id-ct-routeOriginAttestation",
+ (1, 2, 840, 113549, 1, 9, 16, 1, 26) : "id-ct-rpkiManifest",
+ (1, 2, 840, 113549, 1, 9, 16, 1, 28) : "id-ct-xml",
+ (1, 3, 6, 1, 5, 5, 7, 1, 1) : "authorityInfoAccess",
+ (1, 3, 6, 1, 5, 5, 7, 1, 11) : "subjectInfoAccess",
+ (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, 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",
+ (2, 16, 840, 1, 101, 3, 4, 2, 1) : "id-sha256",
+ (2, 5, 29, 14) : "subjectKeyIdentifier",
+ (2, 5, 29, 15) : "keyUsage",
+ (2, 5, 29, 19) : "basicConstraints",
+ (2, 5, 29, 20) : "cRLNumber",
+ (2, 5, 29, 31) : "cRLDistributionPoints",
+ (2, 5, 29, 32) : "certificatePolicies",
+ (2, 5, 29, 35) : "authorityKeyIdentifier",
+ (2, 5, 4, 3) : "commonName",
}
## @var name2oid
diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py
index c9ed2864..fa46fb74 100644
--- a/rpkid/rpki/x509.py
+++ b/rpkid/rpki/x509.py
@@ -26,9 +26,9 @@ bring together the functionality I need in a way that hides at least
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.cms, rpki.oids, rpki.sundial
-import rpki.manifest, rpki.roa
+import POW, tlslite.api, POW.pkix, base64, lxml.etree, os
+import rpki.exceptions, rpki.resource_set, rpki.oids, rpki.sundial
+import rpki.manifest, rpki.roa, rpki.relaxng
def calculate_SKI(public_key_der):
"""Calculate the SKI value given the DER representation of a public
@@ -208,6 +208,23 @@ class DER_object(object):
"""Convert to SQL storage format."""
return self.get_DER()
+ def dumpasn1(self):
+ """Prettyprint an ASN.1 DER object using cryptlib dumpasn1 tool.
+ Use a temporary file rather than popen4() because dumpasn1 uses
+ seek() when decoding ASN.1 content nested in OCTET STRING values.
+ """
+
+ fn = "dumpasn1.tmp"
+ try:
+ f = open(fn, "wb")
+ f.write(self.get_DER())
+ f.close()
+ f = os.popen("dumpasn1 2>&1 -a " + fn)
+ print "\n".join(x for x in f.read().splitlines() if x.startswith(" "))
+ f.close()
+ finally:
+ os.unlink(fn)
+
class X509(DER_object):
"""X.509 certificates.
@@ -579,6 +596,15 @@ class RSApublic(DER_object):
"""Calculate the SKI of this public key."""
return calculate_SKI(self.get_DER())
+def POWify(oid):
+ """Utility function to convert tuple form of an OID to
+ the dotted-decimal string form that POW uses.
+ """
+ if isinstance(oid, str):
+ return POWify(rpki.oids.name2oid[oid])
+ else:
+ return ".".join(str(i) for i in oid)
+
class CMS_object(DER_object):
"""Class to hold a CMS-wrapped object.
@@ -593,7 +619,10 @@ class CMS_object(DER_object):
formats = ("DER",)
other_clear = ("content",)
+ econtent_oid = POWify("id-data")
+ dump_on_verify_failure = False
+
def get_DER(self):
"""Get the DER value of this CMS_object."""
assert not self.empty()
@@ -611,20 +640,57 @@ class CMS_object(DER_object):
self.clear()
self.content = content
- def verify_and_store(self, ta, obj):
+ def verify(self, ta):
"""Verify CMS wrapper and store inner content."""
- s = rpki.cms.verify(self.get_DER(), ta)
- obj.fromString(s)
- self.content = obj
- def sign(self, keypair, certs):
+ cms = POW.derRead(POW.CMS_MESSAGE, self.get_DER())
+ store = POW.X509Store()
+ if isinstance(ta, (tuple, list)):
+ for x in ta:
+ store.addTrust(x.get_POW())
+ else:
+ store.addTrust(ta.get_POW())
+ try:
+ content = cms.verify(store)
+ except:
+ if self.dump_on_verify_failure:
+ print "CMS verification failed, dumping ASN.1:"
+ self.dumpasn1()
+ raise rpki.exceptions.CMSVerificationFailed, "CMS verification failed"
+ self.decode(content)
+ return self.get_content()
+
+ def sign(self, keypair, certs, no_certs = False):
"""Sign and wrap inner content."""
- self.DER = rpki.cms.sign(self.get_content().toString(), keypair, certs)
-class SignedManifest(CMS_object):
+ cms = POW.CMS()
+ cms.sign(certs[0].get_POW(),
+ keypair.get_POW(),
+ [x.get_POW() for x in certs[1:]],
+ self.encode(),
+ self.econtent_oid,
+ no_certs)
+ self.DER = cms.derWrite()
+
+class DER_CMS_object(CMS_object):
+ """Class to hold CMS objects with DER-based content."""
+
+ def encode(self):
+ """Encode inner content for signing."""
+ return self.get_content().toString()
+
+ def decode(self, der):
+ """Decode DER and set inner content."""
+ obj = self.content_class()
+ obj.fromString(der)
+ self.content = obj
+
+class SignedManifest(DER_CMS_object):
"""Class to hold a signed manifest."""
pem_converter = PEM_converter("RPKI MANIFEST")
+ content_class = rpki.manifest.Manifest
+ econtent_oid = POWify("id-ct-rpkiManifest")
def getThisUpdate(self):
"""Get thisUpdate value from this manifest."""
@@ -634,10 +700,6 @@ class SignedManifest(CMS_object):
"""Get nextUpdate value from this manifest."""
return rpki.sundial.datetime.fromGeneralizedTime(self.get_content().nextUpdate.get())
- def verify(self, ta):
- """Verify this manifest."""
- self.verify_and_store(ta, rpki.manifest.Manifest())
-
@classmethod
def build(cls, serial, thisUpdate, nextUpdate, names_and_objs, keypair, certs, version = 0):
"""Build a signed manifest."""
@@ -653,20 +715,18 @@ class SignedManifest(CMS_object):
m.manifestNumber.set(serial)
m.thisUpdate.set(thisUpdate.toGeneralizedTime())
m.nextUpdate.set(nextUpdate.toGeneralizedTime())
- m.fileHashAlg.set((2, 16, 840, 1, 101, 3, 4, 2, 1)) # id-sha256
+ m.fileHashAlg.set(rpki.oids.name2oid["id-sha256"])
m.fileList.set(filelist)
self.set_content(m)
self.sign(keypair, certs)
return self
-class ROA(CMS_object):
+class ROA(DER_CMS_object):
"""Class to hold a signed ROA."""
pem_converter = PEM_converter("ROUTE ORIGIN ATTESTATION")
-
- def verify(self, ta):
- """Verify this ROA."""
- self.verify_and_store(ta, rpki.roa.RouteOriginAttestation())
+ content_class = rpki.roa.RouteOriginAttestation
+ econtent_oid = POWify("id-ct-routeOriginAttestation")
@classmethod
def build(cls, as_number, exact_match, ipv4, ipv6, keypair, certs, version = 0):
@@ -681,6 +741,58 @@ class ROA(CMS_object):
self.sign(keypair, certs)
return self
+class XML_CMS_object(CMS_object):
+ """Class to hold CMS-wrapped XML protocol data."""
+
+ econtent_oid = POWify("id-ct-xml")
+
+ def encode(self):
+ """Encode inner content for signing."""
+ return lxml.etree.tostring(self.get_content(), pretty_print = True, encoding = self.encoding, xml_declaration = True)
+
+ def decode(self, xml):
+ """Decode XML and set inner content."""
+ self.content = lxml.etree.fromstring(xml)
+
+ def prettyprint_content(self):
+ """Prettyprint XML content of this message."""
+ return lxml.etree.tostring(self.get_content(), pretty_print = True, encoding = self.encoding, xml_declaration = True)
+
+ def schema_check(self):
+ """Handle XML RelaxNG schema check."""
+ try:
+ self.schema.assertValid(self.get_content())
+ except lxml.etree.DocumentInvalid:
+ rpki.log.error("PDU failed schema check: " + self.prettyprint_content())
+ raise
+
+ @classmethod
+ def build(cls, elt, keypair, certs):
+ """Build a CMS-wrapped XML PDU."""
+ self = cls()
+ self.set_content(elt)
+ self.schema_check()
+ self.sign(keypair, certs)
+ return self
+
+ def verify(self, ta):
+ """Wrapper around CMS_object.verify(), adds RelaxNG schema check."""
+ CMS_object.verify(self, ta)
+ self.schema_check()
+ return self.get_content()
+
+class left_right_pdu(XML_CMS_object):
+ """Class to hold a CMS-signed left-right PDU."""
+
+ encoding = "us-ascii"
+ schema = rpki.relaxng.left_right
+
+class up_down_pdu(XML_CMS_object):
+ """Class to hold a CMS-signed up-down PDU."""
+
+ encoding = "UTF-8"
+ schema = rpki.relaxng.up_down
+
class CRL(DER_object):
"""Class to hold a Certificate Revocation List."""
diff --git a/rpkid/rpkid.py b/rpkid/rpkid.py
index 7ea22a46..c61826d7 100755
--- a/rpkid/rpkid.py
+++ b/rpkid/rpkid.py
@@ -24,7 +24,7 @@ Default configuration file is rpkid.conf, override with --config option.
import traceback, os, time, getopt, sys, MySQLdb, lxml.etree
import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql
-import rpki.https, rpki.config, rpki.cms, rpki.exceptions, rpki.relaxng, rpki.log
+import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log
import rpki.gctx
os.environ["TZ"] = "UTC"
diff --git a/rpkid/testbed.py b/rpkid/testbed.py
index 3534b24f..8c689f04 100644
--- a/rpkid/testbed.py
+++ b/rpkid/testbed.py
@@ -568,22 +568,18 @@ class allocation(object):
pdu.type = "query"
elt = rpki.left_right.msg((pdu,)).toXML()
rpki.log.debug(lxml.etree.tostring(elt, pretty_print = True, encoding = "us-ascii"))
- rpki.relaxng.left_right.assertValid(elt)
- cms = rpki.cms.xml_sign(
- elt = elt,
- key = testbed_key,
- certs = testbed_certs)
+ cms = rpki.x509.left_right_pdu.build(elt, testbed_key, testbed_certs)
url = "https://localhost:%d/left-right" % self.rpki_port
rpki.log.debug("Attempting to connect to %s" % url)
- cms = rpki.https.client(
+ der = rpki.https.client(
client_key = testbed_key,
client_certs = testbed_certs,
server_ta = self.rpkid_ta,
url = url,
- msg = cms)
- elt = rpki.cms.xml_verify(der = cms, ta = self.rpkid_ta)
+ msg = cms.get_DER())
+ cms = rpki.x509.left_right_pdu(DER = der)
+ elt = cms.verify(ta = self.rpkid_ta)
rpki.log.debug(lxml.etree.tostring(elt, pretty_print = True, encoding = "us-ascii"))
- rpki.relaxng.left_right.assertValid(elt)
pdu = rpki.left_right.sax_handler.saxify(elt)[0]
assert pdu.type == "reply" and not isinstance(pdu, rpki.left_right.report_error_elt)
return pdu
diff --git a/rpkid/testpoke.py b/rpkid/testpoke.py
index b2908bd9..99ee53c0 100644
--- a/rpkid/testpoke.py
+++ b/rpkid/testpoke.py
@@ -29,7 +29,7 @@ Default configuration file is testpoke.yaml, override with --yaml option.
import os, time, getopt, sys, lxml, yaml
import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509
-import rpki.https, rpki.config, rpki.cms, rpki.exceptions
+import rpki.https, rpki.config, rpki.exceptions
import rpki.relaxng, rpki.oids, rpki.log
os.environ["TZ"] = "UTC"
@@ -90,18 +90,16 @@ def query_up_down(q_pdu):
sender = yaml_data["sender-id"],
recipient = yaml_data["recipient-id"])
q_elt = q_msg.toXML()
- rpki.relaxng.up_down.assertValid(q_elt)
- q_cms = rpki.cms.xml_sign(q_elt, cms_key, cms_certs, encoding = "UTF-8")
- r_cms = rpki.https.client(
+ q_cms = rpki.x509.up_down_pdu.build(q_elt, cms_key, cms_certs)
+ der = rpki.https.client(
server_ta = https_ta,
client_key = https_key,
client_certs = https_certs,
- msg = q_cms,
- url = yaml_data["posturl"])
- r_xml = rpki.cms.verify(r_cms, cms_ta)
- r_elt = lxml.etree.fromstring(r_xml)
- rpki.relaxng.up_down.assertValid(r_elt)
- return r_xml
+ msg = q_cms.get_DER(),
+ url = yaml_data["posturl"])
+ r_cms = rpki.x509.up_down_pdu(DER = der)
+ r_elt = r_cms.verify(cms_ta)
+ return r_cms.prettyprint_content()
def do_list():
print query_up_down(rpki.up_down.list_pdu())