diff options
Diffstat (limited to 'rpkid/rpki/cms.py')
-rw-r--r-- | rpkid/rpki/cms.py | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/rpkid/rpki/cms.py b/rpkid/rpki/cms.py new file mode 100644 index 00000000..7b4916dc --- /dev/null +++ b/rpkid/rpki/cms.py @@ -0,0 +1,120 @@ +# $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. + +These used to use the OpenSSL CLI too, which was slow. I've since +added minimal PKCS #7 / CMS capability to POW, so we now use that +instead. I should write a pretty DER_object wrapper around the POW +code and include it in x509.py, but I haven't gotten to that yet. +""" + +import os, rpki.x509, rpki.exceptions, lxml.etree, rpki.log, POW + +debug = 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): + """Sign plaintext as CMS with specified key and bag of certificates. + + We have to sort the certificates into the correct order before the + OpenSSL CLI tool will accept them. rpki.x509 handles that for us. + """ + + p7 = POW.PKCS7() + p7.sign(certs[0].get_POW(), keypair.get_POW(), [x.get_POW() for x in certs[1:]], plaintext) + cms = p7.derWrite() + + if debug >= 2: + print + print "Signed CMS:" + dumpasn1(cms) + + return cms + +# openssl smime -verify -inform DER -in THING.der -CAfile biz-certs/Alice-Root.cer + +def verify(cms, 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(cms) + + p7 = POW.derRead(POW.PKCS7_MESSAGE, cms) + + store = POW.X509Store() + store.addTrust(ta.get_POW()) + + try: + return p7.verify(store) + + except: + if debug >= 1: + print "CMS verification failed, dumping inputs:" + print + print "TA:" + dumpasn1(ta.get_DER()) + print + print "CMS:" + dumpasn1(cms) + raise rpki.exceptions.CMSVerificationFailed, "CMS verification failed" + +# openssl smime -verify -noverify -inform DER -in THING.der + +def extract(cms): + """Extract the content of a signed CMS message WITHOUT verifying the + signature. Don't try this at home, kids. + """ + + return POW.derRead(POW.PKCS7_MESSAGE, cms).extract() + +def xml_verify(cms, ta): + """Composite routine to verify CMS-wrapped XML.""" + + val = lxml.etree.fromstring(verify(cms, 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) |